diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-14 20:03:01 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-14 20:03:01 +0000 |
commit | a453ac31f3428614cceb99027f8efbdb9258a40b (patch) | |
tree | f61f87408f32a8511cbd91799f9cececb53e0374 /collections-debian-merged/ansible_collections/cisco/aci | |
parent | Initial commit. (diff) | |
download | ansible-a453ac31f3428614cceb99027f8efbdb9258a40b.tar.xz ansible-a453ac31f3428614cceb99027f8efbdb9258a40b.zip |
Adding upstream version 2.10.7+merged+base+2.10.8+dfsg.upstream/2.10.7+merged+base+2.10.8+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'collections-debian-merged/ansible_collections/cisco/aci')
277 files changed, 50127 insertions, 0 deletions
diff --git a/collections-debian-merged/ansible_collections/cisco/aci/.github/workflows/ansible-test.yml b/collections-debian-merged/ansible_collections/cisco/aci/.github/workflows/ansible-test.yml new file mode 100644 index 00000000..054c1808 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/.github/workflows/ansible-test.yml @@ -0,0 +1,171 @@ +name: CI +on: + push: + branches: master + pull_request: + schedule: + # * is a special character in YAML so you have to quote this string + - cron: '0 8 * * *' +jobs: + build: + name: Build collection + runs-on: ubuntu-latest + strategy: + matrix: + ansible: [2.9.12, 2.10.0] + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + + - name: Install ansible-base (v${{ matrix.ansible }}) + run: pip install https://github.com/ansible/ansible/archive/v${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + - name: Build a collection tarball + run: ansible-galaxy collection build --output-path "${GITHUB_WORKSPACE}/.cache/collection-tarballs" + + - name: Store migrated collection artifacts + uses: actions/upload-artifact@v1 + with: + name: collection + path: .cache/collection-tarballs + + sanity: + name: Sanity in ubuntu-latest + needs: + - build + runs-on: ubuntu-latest + strategy: + matrix: + ansible: [2.9.12, 2.10.0] + steps: + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + + - name: Install ansible-base (v${{ matrix.ansible }}) + run: pip install https://github.com/ansible/ansible/archive/v${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + - name: Install coverage (v4.5.4) + run: pip install coverage==4.5.4 + + - name: Download migrated collection artifacts + uses: actions/download-artifact@v1 + with: + name: collection + path: .cache/collection-tarballs + + - name: Install the collection tarball + run: ansible-galaxy collection install .cache/collection-tarballs/*.tar.gz + + - name: Run sanity tests + run: ansible-test sanity --docker -v --color --truncate 0 --coverage + working-directory: /home/runner/.ansible/collections/ansible_collections/cisco/aci + + - name: Generate coverage report + run: ansible-test coverage xml -v --requirements --group-by command --group-by version + working-directory: /home/runner/.ansible/collections/ansible_collections/cisco/aci + + - name: Push coverate report to codecov.io + run: bash <(curl -s https://codecov.io/bash) -s 'tests/output/reports/' -F integration + working-directory: /home/runner/.ansible/collections/ansible_collections/cisco/aci + + units: + name: Units in ubuntu-latest + needs: + - build + runs-on: ubuntu-latest + strategy: + matrix: + ansible: [2.9.12, 2.10.0] + steps: + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + + - name: Install ansible-base (v${{ matrix.ansible }}) + run: pip install https://github.com/ansible/ansible/archive/v${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + - name: Install coverage (v4.5.4) + run: pip install coverage==4.5.4 + + - name: Download migrated collection artifacts + uses: actions/download-artifact@v1 + with: + name: collection + path: .cache/collection-tarballs + + - name: Install the collection tarball + run: ansible-galaxy collection install .cache/collection-tarballs/*.tar.gz + + - name: Run unit tests + run: ansible-test units --docker -v --color --truncate 0 --python 3.7 --coverage + working-directory: /home/runner/.ansible/collections/ansible_collections/cisco/aci + + - name: Generate coverage report. + run: ansible-test coverage xml -v --requirements --group-by command --group-by version + working-directory: /home/runner/.ansible/collections/ansible_collections/cisco/aci + + - uses: codecov/codecov-action@v1 + with: + fail_ci_if_error: false + file: /home/runner/.ansible/collections/ansible_collections/cisco/aci/tests/output/reports/coverage.xml + + integration: + name: Integration in ubuntu-latest + needs: + - build + runs-on: ubuntu-latest + steps: + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + + - name: Install ansible-base (v2.9.12) + run: pip install https://github.com/ansible/ansible/archive/v2.9.12.tar.gz --disable-pip-version-check + + - name: Install coverage (v4.5.4) + run: pip install coverage==4.5.4 + + - name: Download migrated collection artifacts + uses: actions/download-artifact@v1 + with: + name: collection + path: .cache/collection-tarballs + + - name: Install the collection tarball + run: ansible-galaxy collection install .cache/collection-tarballs/*.tar.gz + + - name: Requesting integration mutex + uses: nev7n/wait_for_response@v1 + with: + url: ${{ format('https://8v7s765ibh.execute-api.us-west-1.amazonaws.com/v1/ansible-aci?repo={0}', github.repository) }} + responseCode: 200 + timeout: 2000000 + interval: 5000 + + - name: Run integration tests on Python 3.7 + run: ansible-test network-integration --docker -v --color --retry-on-error --python 3.7 --truncate 0 --continue-on-error --coverage + working-directory: /home/runner/.ansible/collections/ansible_collections/cisco/aci + + - name: Releasing integration mutex + uses: nev7n/wait_for_response@v1 + if: always() + with: + url: ${{ format('https://8v7s765ibh.execute-api.us-west-1.amazonaws.com/v1/ansible-aci/release?repo={0}', github.repository) }} + responseCode: 200 + + - name: Generate coverage report + run: ansible-test coverage xml -v --requirements --group-by command --group-by version + working-directory: /home/runner/.ansible/collections/ansible_collections/cisco/aci + + - name: Push coverate report to codecov.io + run: bash <(curl -s https://codecov.io/bash) -s 'tests/output/reports/' -F integration + working-directory: /home/runner/.ansible/collections/ansible_collections/cisco/aci diff --git a/collections-debian-merged/ansible_collections/cisco/aci/.gitignore b/collections-debian-merged/ansible_collections/cisco/aci/.gitignore new file mode 100644 index 00000000..2ee73b45 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/.gitignore @@ -0,0 +1,393 @@ + +# Created by https://www.gitignore.io/api/git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv +# Edit at https://www.gitignore.io/?templates=git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv + +### dotenv ### +.env + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + + +### Git ### +# Created by git for backups. To disable backups in Git: +# $ git config --global mergetool.keepBackup false +*.orig + +# Created by git when using merge tools for conflicts +*.BACKUP.* +*.BASE.* +*.LOCAL.* +*.REMOTE.* +*_BACKUP_*.txt +*_BASE_*.txt +*_LOCAL_*.txt +*_REMOTE_*.txt + +#!! ERROR: jupyternotebook is undefined. Use list command to see defined gitignore types !!# + +### Linux ### + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### PyCharm+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### pydev ### +.pydevproject + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# Mr Developer +.mr.developer.cfg +.project + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### WebStorm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### WebStorm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/**/sonarlint/ + +# SonarQube Plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator/ + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv + +# vsCode +.vscode + +# Ansible Collection tarball +cisco-aci-*.tar.gz
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/.vscode/settings.json b/collections-debian-merged/ansible_collections/cisco/aci/.vscode/settings.json new file mode 100644 index 00000000..76aa33c1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "aci_3.8/bin/python" +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/CHANGELOG.rst b/collections-debian-merged/ansible_collections/cisco/aci/CHANGELOG.rst new file mode 100644 index 00000000..3f0628bc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/CHANGELOG.rst @@ -0,0 +1,130 @@ +========================================== +Cisco ACI Ansible Collection Release Notes +========================================== + +.. contents:: Topics + + +v1.1.1 +====== + +Release Summary +--------------- + +Release v1.1.1 of the ``cisco.mso`` collection on 2020-11-23. +This changelog describes all changes made to the modules and plugins included in this collection since v1.1.0. + +Minor Changes +------------- + +- Add test file for aci_domain_to_encap_pool +- aci_epg_to_domain moving child configs & classes to each domain type + +Bugfixes +-------- + +- Fix galaxy import warnings +- Fix sanity issue in aci_epg_to_domain + +v1.1.0 +====== + +Release Summary +--------------- + +Release v1.1.0 of the ``cisco.mso`` collection on 2020-10-30. +This changelog describes all changes made to the modules and plugins included in this collection since v1.0.1. + +Minor Changes +------------- + +- Ability to add monitoring policy to epgs and anps +- Add Ansible Network ENV to fallback +- Add aci_l3out_external_path_to_member.py & aci_l3out_static_routes modules +- Add env_fallback for common connection params +- Add env_fallback for the rest of the argument spec +- Add new Subclass path support +- Add new module and test file for leaf breakout port group +- Added failure message to aci_interface_policy_leaf_policy_group +- Update README.md +- Update inventory +- aci_epg_to_domain addition of promiscuous mode (#79) +- aci_interface_policy_port_security addition of attribute:timeout (#80) + +Bugfixes +-------- + +- Existing_config variable is not reset during loop +- Fix galaxy import warnings +- Fix how validity of private key/private key file is checked to support new types +- Fix incorrect domain types in aci_domain_to_encap_pool module + +v1.0.1 +====== + +Release Summary +--------------- + +Release v1.0.1 of the ``cisco.mso`` collection on 2020-10-13. +This changelog describes all changes made to the modules and plugins included in this collection since v1.0.0. + +Minor Changes +------------- + +- Enable/Disable infra vlan in aci_aep and its test module +- Set scope default value in aci_l3out_extsubnet + +Bugfixes +-------- + +- Fix convertion of json/yaml payload to xml in aci_rest +- Fix dump of config for aci_rest +- Fix issue of "current" in firmware_source module +- Fix sanity issue in aci_rest and bump version to v1.0.1 + +v1.0.0 +====== + +Release Summary +--------------- + +This is the first official release of the ``cisco.aci`` collection on 2020-08-18. +This changelog describes all changes made to the modules and plugins included in this collection since Ansible 2.9.0. + + +Minor Changes +------------- + +- Add Fex capability to aci_interface_policy_leaf_profile, aci_access_port_to_interface_policy_leaf_profile and aci_access_port_block_to_access_port +- Add LICENSE file +- Add aci_epg_to_contract_master module +- Add annotation attribute to aci.py and to doc fragment. +- Add annotation to every payload and add test case for annotation. +- Add changelog +- Add collection prefix to all integration tests +- Add galaxy.yml file for collection listing +- Add github action CI pipeline +- Add module and test file for aci_bd_dhcp_label +- Add modules and test files for aci_cloud_ctx_profile, aci_cloud_cidr, aci_cloud_subnet and aci_cloud_zone +- Add modules and test files for aci_l2out, aci_l2out_extepg and aci_l3out_extepg_to_contract +- Add names to documentation examples for modules from community.network +- Add preferred group support to aci_vrf +- Add support for Azure on all cloud modules +- Add support for output_path to allow dump of REST API objects +- Add support for owner_key and owner_tag for all modules and add test case for it. +- Add vpn gateway dedicated module and remove vpn_gateway from cloud_ctx_profile module +- Fix M() and module to use FQCN +- Initial commit based on the collection migration available at "ansible-collection-migration/cisco.aci" which contains the ACI module from Ansible Core +- Move aci.py to base of module_utils and fix references +- Move test file to root of tests/unit/module_utils +- Update Ansible version in CI and add 2.10.0 to sanity in CI. +- Update Readme with supported versions +- Update to test files to make the tests work on both 3.2 and 4.2. + +Bugfixes +-------- + +- Fix sanity issues to support 2.10.0 +- Fix some doc issues for a few modules +- Fix some formatting issues (flake8) in unit tests. +- Fixing integration tests and sanity. Tested on ACI 4.2(3l). diff --git a/collections-debian-merged/ansible_collections/cisco/aci/FILES.json b/collections-debian-merged/ansible_collections/cisco/aci/FILES.json new file mode 100644 index 00000000..085cea69 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/FILES.json @@ -0,0 +1,2938 @@ +{ + "files": [ + { + "name": ".", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "LICENSE", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1c38eb11b6b8cd591aed90793ca80bd3c499393f207b96640ddfa571f61f7b4f", + "format": 1 + }, + { + "name": "plugins", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/doc_fragments", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/doc_fragments/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/doc_fragments/aci.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ce1eba18a30f6906c18c834b271d9ee36b064697885e083fc1f0a2e3d51022ad", + "format": 1 + }, + { + "name": "plugins/module_utils", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/aci.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f354e6dc6fd808d4d7ace041cfbab7136f6787e846a59e1050f4db4fbec3e81a", + "format": 1 + }, + { + "name": "plugins/modules", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/modules/aci_l2out.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3c1f007283369e47a9b2dbd8c9dfb2f9a94a48f9285035cad4286c094b31796d", + "format": 1 + }, + { + "name": "plugins/modules/aci_vlan_pool.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8d065c55b8d6bee3fe61538d6c7118a5d93ce76025b0ef76fcbee063daea74e3", + "format": 1 + }, + { + "name": "plugins/modules/aci_domain_to_encap_pool.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ab41cc631460014f0b4ac1d34fc8b106aef641b997383e51d235ce220811c500", + "format": 1 + }, + { + "name": "plugins/modules/aci_tenant_span_src_group_to_dst_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "695e6e52d0d44f86dbc3ebe829c4396b9a4d896bcbef1dcac34f8d0d797d65fa", + "format": 1 + }, + { + "name": "plugins/modules/aci_domain_to_vlan_pool.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "588e692945d98acf13d7dbc26eeae170cff1ef83aebe02d9926cd160bd7c8e72", + "format": 1 + }, + { + "name": "plugins/modules/aci_l3out_extsubnet.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "de51d3411dc447cee6e3a0860f43308885986f3d17368a6c6560145ab17641c5", + "format": 1 + }, + { + "name": "plugins/modules/aci_l3out_static_routes.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b88228b4e5cada88f0606a023177d6196bd5b605e93db1229f6f8ecc229e2915", + "format": 1 + }, + { + "name": "plugins/modules/aci_epg.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7cd2f9ee8b0b50998249d4f49ec8b4f25593b354fcc7ec35643ad62ec2e2a049", + "format": 1 + }, + { + "name": "plugins/modules/aci_cloud_subnet.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "48b2a95ff5087918c7ae3d1b36cf274efc868b247918ddb5674308c3295045a3", + "format": 1 + }, + { + "name": "plugins/modules/aci_aep_to_domain.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "575e2872b0a6aac9545cb75354f1565ad15372f407c062b303d598d927d04166", + "format": 1 + }, + { + "name": "plugins/modules/aci_aaa_user_certificate.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "27e514fbb89ba01365db8b0094275375985829367dddddcdb36391062004083a", + "format": 1 + }, + { + "name": "plugins/modules/aci_firmware_group_node.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "095207bd3f72dcf32638922578d3ad31e77a731e667ca91efcc8df027af4b307", + "format": 1 + }, + { + "name": "plugins/modules/aci_filter.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b3cda6a2a0aefe84a256b8e01fd06f1e7753ca1d3f8db6a5a02f7d775ec59061", + "format": 1 + }, + { + "name": "plugins/modules/aci_config_snapshot.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0046490d4a19dfb2bb0b20be256e8952fbaf0b31e275b262ce392d4219e89549", + "format": 1 + }, + { + "name": "plugins/modules/aci_epg_to_contract_master.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "efe1f84425d5831c82ec0986504081fa4fcfb7c6982c2c0c42bc7c78f873248e", + "format": 1 + }, + { + "name": "plugins/modules/aci_interface_policy_lldp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fd31cf864e4d8ff9cfb3fd0e51d0aa8e9acd9d4cae59cc4f63d679509bc950b3", + "format": 1 + }, + { + "name": "plugins/modules/aci_interface_policy_port_security.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8e0b9a3c9a05439d0cf4dcb2b728853268dbaeaa96c2b327e8f6c6d8f61bd5de", + "format": 1 + }, + { + "name": "plugins/modules/aci_tenant_span_src_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "376d583433dea1741ba2daf84eaa713013f1e9a6070a46ed0835e846c237820e", + "format": 1 + }, + { + "name": "plugins/modules/aci_static_binding_to_epg.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ca35e7fe3132341b2346da7f9dc65823d6beb72562a0a777259c5142d3a95e8a", + "format": 1 + }, + { + "name": "plugins/modules/aci_contract_subject.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be0097f6cdeae3f29d214dea14032bbb68bf3329f747b12200ac01d3b57b5adb", + "format": 1 + }, + { + "name": "plugins/modules/aci_interface_policy_l2.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "609aa622d863ba996eda0ad74707a33a7bc43a62410e5de8a18b46c931164fbf", + "format": 1 + }, + { + "name": "plugins/modules/aci_cloud_provider.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d6a731ad325068e4813bd8859e1b517f2a49ee79487b260b130667bb2c486f62", + "format": 1 + }, + { + "name": "plugins/modules/aci_filter_entry.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e7dff4086b1415e9f63f34286c77123c5f0123251464debd22de8a2f117344f6", + "format": 1 + }, + { + "name": "plugins/modules/aci_vrf.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d813b37cef2680264a09c11d8e0eea504a1ed8b80a1b0ea87813d8ac27fd5eda", + "format": 1 + }, + { + "name": "plugins/modules/aci_interface_policy_leaf_profile.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0be99340ce8d2e2604da3e55ef97e608f2a5a5e4e6e6f073365988ce8e8eaede", + "format": 1 + }, + { + "name": "plugins/modules/aci_l3out.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9167ec5b6d509536548bb34d7848c341592b2521d0dc694b850dd0e2d5903fe6", + "format": 1 + }, + { + "name": "plugins/modules/aci_interface_policy_fc.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "664865e0683cc6607959a2da1858088da6bae01645003efb09b8221b0000409c", + "format": 1 + }, + { + "name": "plugins/modules/aci_interface_policy_link_level.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bbbbd6ba28668c4030d00b37f6ae34c75ce4676a37c4223a9ecb5979d288a700", + "format": 1 + }, + { + "name": "plugins/modules/aci_switch_policy_vpc_protection_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "86004a3da639ba800c566cf57108a7d41212d7f0394e3567581a0cdc37adc8f0", + "format": 1 + }, + { + "name": "plugins/modules/aci_tenant_action_rule_profile.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0bf64c679529b9663b56c09e75b8bd1bf71f6ddbb72c0da4555d49a10b9c432a", + "format": 1 + }, + { + "name": "plugins/modules/aci_interface_policy_leaf_breakout_port_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0b11f0e91043c6f6765150618c656098eb5e280297da31948f11cc7d2dbb6977", + "format": 1 + }, + { + "name": "plugins/modules/aci_interface_policy_mcp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9c2615487ddf26c6e694cac5e0183781f09d8a7d2c0cffa1a23864863c268f7b", + "format": 1 + }, + { + "name": "plugins/modules/aci_maintenance_policy.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b74cef04324c1d086590507679b810369b63c54b12332f069cc946c27404257d", + "format": 1 + }, + { + "name": "plugins/modules/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/modules/aci_cloud_vpn_gateway.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9562e2f407f6e5a501f0bc0dbca115b4b3f336f8931c8b2d08c634821df0bdcd", + "format": 1 + }, + { + "name": "plugins/modules/aci_cloud_cidr.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f27c5b956d7dc1fb6b157e8ba6e208f1018ff62195645cb19cbea21b62b64a4e", + "format": 1 + }, + { + "name": "plugins/modules/aci_ap.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "90230741a96a0e6981a04d20d9acbf125a75aaf3a5962ee995246dbab5a6a1a4", + "format": 1 + }, + { + "name": "plugins/modules/aci_tenant_span_dst_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "80dc907e9807583ed88e7efb234e3e6d9ab8cea0f2ef43857dfb90d00472621a", + "format": 1 + }, + { + "name": "plugins/modules/aci_taboo_contract.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4a3925936cfba0f2b8744987288eb5a648449162ee65c5880e12a4e79367b84e", + "format": 1 + }, + { + "name": "plugins/modules/aci_contract.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "49b727ed24577161054da66ec4792bbe86226d247a752bddc15c929259e73218", + "format": 1 + }, + { + "name": "plugins/modules/aci_fabric_node.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "95a08f9eb724433591732ad7f27939c8ed95b88d0bb7a4b4fbcfb25cd873d94d", + "format": 1 + }, + { + "name": "plugins/modules/aci_firmware_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "24d62ab8356e6bf6ad4dc5a91b33abd5075168b2f83f6eb4721f06558aabc1dd", + "format": 1 + }, + { + "name": "plugins/modules/aci_firmware_policy.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6669e6641be20f7facdcce1b2607559fd9150c91e453d8d08ba463c9e654b22b", + "format": 1 + }, + { + "name": "plugins/modules/aci_interface_policy_ospf.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5f83137ba4c5804bf3273616cfb97f7f04e63095de18cb9a6907ad2193e4dcdc", + "format": 1 + }, + { + "name": "plugins/modules/aci_epg_to_contract.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cb1827a32607cb67d9cd7f6ccbf574673ecaec0ffc189637a549a04318bcf038", + "format": 1 + }, + { + "name": "plugins/modules/aci_cloud_region.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fe30f345a9eb00e13b035d205e5b58422feb52f68694638d87f51ded691a3914", + "format": 1 + }, + { + "name": "plugins/modules/aci_encap_pool_range.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8e970c136a2ac2a24f4447097ec6c42883ba495d0b476f3146e02fee0755ad53", + "format": 1 + }, + { + "name": "plugins/modules/aci_switch_policy_leaf_profile.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2ad6ba100355c8a9d37e9ae302476c06c150f2833292823a212f1d9e3e90c512", + "format": 1 + }, + { + "name": "plugins/modules/aci_vlan_pool_encap_block.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "59dec9619780eb596bf9e3e859dfd48377e250f31092e54c5acfc1dcfb58d940", + "format": 1 + }, + { + "name": "plugins/modules/aci_system.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f0e0d492601df84d88933d6656827afa9625333add0eb0054a1e112b673a6242", + "format": 1 + }, + { + "name": "plugins/modules/aci_maintenance_group_node.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "450452e406acc8b838595da5bacf6e7148ee908f847ba7f0f95821ccb88dc3c9", + "format": 1 + }, + { + "name": "plugins/modules/aci_maintenance_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2f42e4204734a163a7efdb5d6d613da665d5d41d70261c4353e7a5b8c34ad584", + "format": 1 + }, + { + "name": "plugins/modules/aci_interface_selector_to_switch_policy_leaf_profile.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "73cf9ec9088029ed3f0a408fc10ab163e09280e74e4a3d6a876b6225ff62f0f5", + "format": 1 + }, + { + "name": "plugins/modules/aci_access_sub_port_block_to_access_port.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c4c6b1cf6e53dc6f63f93ecf7d81ada7d4c7893966ec27b73287de2c7d16fe81", + "format": 1 + }, + { + "name": "plugins/modules/aci_bd_subnet.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "881aee018ed6793b3e38cf8d9b28679bb84ae5e68290ec5a4894724c09c8a62b", + "format": 1 + }, + { + "name": "plugins/modules/aci_cloud_zone.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "24daa0d08e06f8c971fa570236f8f8b69a690c6a84494923b90d4217fb22c3ad", + "format": 1 + }, + { + "name": "plugins/modules/aci_encap_pool.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6537e8b5a578c985a17af3cde6af5523e7c98a6670d2bdfa725a328c91fc36b5", + "format": 1 + }, + { + "name": "plugins/modules/aci_contract_subject_to_filter.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5f88c18f09502755c243e30f16acd72b2165971eb623d7c1b953f3d2a798191c", + "format": 1 + }, + { + "name": "plugins/modules/aci_domain.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b2b7794f5e416221d90a531a7afadcca0bdb05c046014646ab28f2db0c0907f5", + "format": 1 + }, + { + "name": "plugins/modules/aci_access_port_to_interface_policy_leaf_profile.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3c274650a08c47c1776c8609726aa43f8f05f6c7a1dae27f9ecd0d2441516413", + "format": 1 + }, + { + "name": "plugins/modules/aci_interface_policy_cdp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1daf11c03d58f973cf789c7f1db8ba9cf2e159f5a7e5ac10cbff92faaea1e2dd", + "format": 1 + }, + { + "name": "plugins/modules/aci_l3out_extepg_to_contract.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "518fd244082eb28bfa62ed9aac65f5aac18347cd4db8bc632f0bab4a736799ea", + "format": 1 + }, + { + "name": "plugins/modules/aci_config_rollback.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dbcde141d3c9a58fa4d6fa5482a89a4584ca812fc842e086678effb1ff6bd665", + "format": 1 + }, + { + "name": "plugins/modules/aci_tenant_ep_retention_policy.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f12ea0188dbbde492407dfecf44f776d0e0c03eb2a2740766d9921b0fb0b233a", + "format": 1 + }, + { + "name": "plugins/modules/aci_access_port_block_to_access_port.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4ed9b58daa467df5dbf2fe51bdbfe5d7a78f5bc0c2203180debad9a2cbc09892", + "format": 1 + }, + { + "name": "plugins/modules/aci_bd_to_l3out.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "eced4bb8c951f33637d8876cf7581b604abe81c008b2210db06e5514af863fa8", + "format": 1 + }, + { + "name": "plugins/modules/aci_rest.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8a08598d8442f7c14c44e32cfa47ff2ca38212901420b8edbe1f791fe2c3571d", + "format": 1 + }, + { + "name": "plugins/modules/aci_l3out_logical_interface_vpc_member.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fa4459f3bf78bf8cb2f6d86b7bfe23cb7a89f298be73c4b3ed1f9872195187bd", + "format": 1 + }, + { + "name": "plugins/modules/aci_tenant.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1111e3d2319085161e60d6deee53bb618c06bac99440050f734f4af11feef580", + "format": 1 + }, + { + "name": "plugins/modules/aci_interface_policy_leaf_policy_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "826e77222521083e0efca1cc41185133ff71b26a5551e7954172689c53da14a9", + "format": 1 + }, + { + "name": "plugins/modules/aci_aaa_user.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "837daf6aa51e584c5b6d90b980ea53f4ade47836583aa8b3864e978a6af97138", + "format": 1 + }, + { + "name": "plugins/modules/aci_fabric_scheduler.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ff038cb757cea8fec85fc149082ac928c4eb7158c82ea7a94db3c85ed8a48b2c", + "format": 1 + }, + { + "name": "plugins/modules/aci_aep.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "46570b0ec5b85f6dae90d03636a29b0bea2f40b79a6e2d17aaaffc9dea559a1b", + "format": 1 + }, + { + "name": "plugins/modules/aci_l3out_extepg.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5b07dc084c7a195038e6814f664852274481994a1a0787b63c350473775dcf7e", + "format": 1 + }, + { + "name": "plugins/modules/aci_epg_monitoring_policy.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7e83a903f88c38527d34558acf33f94468ca1dd1fa9717771b354f30f2210082", + "format": 1 + }, + { + "name": "plugins/modules/aci_interface_policy_port_channel.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "06ce40cedba107ecb76c95f6717b2ea26d0d2649c471a630026a9c8d90e2c43c", + "format": 1 + }, + { + "name": "plugins/modules/aci_l2out_extepg.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e9acbbe8c83458217796b180abf57008add4e49c76973173ba63b6bc67382608", + "format": 1 + }, + { + "name": "plugins/modules/aci_l3out_route_tag_policy.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "74d9093f97947e13ac4f199f6be7f880cdde94a00869e189813bac851f876e52", + "format": 1 + }, + { + "name": "plugins/modules/aci_epg_to_domain.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6d33e15e8443abaa652ef9592bb52f7e2be28e0fc2ec815a529e617d1833d888", + "format": 1 + }, + { + "name": "plugins/modules/aci_cloud_ctx_profile.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a1ba83faa33cd570bbc8a938982ed1f8bf00a725f5c0253de95acd9cd3a7d56c", + "format": 1 + }, + { + "name": "plugins/modules/aci_bd.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "273515aa9bb78792d59bba83d6fdfad9e81225613634f94e3ea64300c5fcc3cf", + "format": 1 + }, + { + "name": "plugins/modules/aci_bd_dhcp_label.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "234e6c848c64b3377f244575b3db0f9cd7b4d18e0c26e22ab70527ee25f52cb0", + "format": 1 + }, + { + "name": "plugins/modules/aci_vmm_credential.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b1d714a256c6ab0434c1f47f2d86d346d2c826f08cadb3fea2ed9cebd6a42c0c", + "format": 1 + }, + { + "name": "plugins/modules/aci_firmware_source.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "afae46ca8450a352216c6ce2935e348850ca6d418f67d1ddefad30ba2cd7c054", + "format": 1 + }, + { + "name": "plugins/modules/aci_switch_leaf_selector.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "edbd55df9c4d0e33215e237d96811c35c7adcc460c7f1df7b61eb2afc1e6d80c", + "format": 1 + }, + { + "name": "tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/compat", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/compat/unittest.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5401a046e5ce71fa19b6d905abd0f9bdf816c0c635f7bdda6730b3ef06e67096", + "format": 1 + }, + { + "name": "tests/unit/compat/builtins.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0ca4cac919e166b25e601e11acb01f6957dddd574ff0a62569cb994a5ecb63e1", + "format": 1 + }, + { + "name": "tests/unit/compat/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/compat/mock.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0af958450cf6de3fbafe94b1111eae8ba5a8dbe1d785ffbb9df81f26e4946d99", + "format": 1 + }, + { + "name": "tests/unit/requirements.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "09c75b1b3fadff6870584617f4f178e168a23d4e0fb3187d7b100ffc8d4dbb68", + "format": 1 + }, + { + "name": "tests/unit/mock", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/mock/vault_helper.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4535613601c419f7d20f0c21e638dabccf69b4a7fac99d5f6f9b81d1519dafd6", + "format": 1 + }, + { + "name": "tests/unit/mock/procenv.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "340adca3524b64ea1804b1be207cd86bba734e104b713bfe54852a20643aa07f", + "format": 1 + }, + { + "name": "tests/unit/mock/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/mock/loader.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c45064a0beb765cd0b6cfbb74ca0cd491ceed2f4c2d22808f60a57071d9712cc", + "format": 1 + }, + { + "name": "tests/unit/mock/yaml_helper.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fada9f3506c951e21c60c2a0e68d3cdf3cadd71c8858b2d14a55c4b778f10983", + "format": 1 + }, + { + "name": "tests/unit/mock/path.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "359e469f24b220034c278a24eb121934afee992b427083eba2b34dfcd8ab1597", + "format": 1 + }, + { + "name": "tests/unit/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/module_utils", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/module_utils/conftest.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "96375dffba3a180868fdd4038a2308c0b7096cd7dfcab3d9b1a99f9c3aa412e1", + "format": 1 + }, + { + "name": "tests/unit/module_utils/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/module_utils/test_aci.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "768b3fed741cf5685a4fde97bb61b7f3d621214bdf8d16a3486506e0e168e045", + "format": 1 + }, + { + "name": "tests/unit/modules", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/modules/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/modules/utils.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "778c223e9618499416ec085116566f93e446298b40de0e97179e07a58353c353", + "format": 1 + }, + { + "name": "tests/integration", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_firmware_source", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_firmware_source/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_firmware_source/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "95ee636c739d032edc8dea1c14cb802c49f4bfd34b3bb2b0c2095390d35b4259", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_firmware_source/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_access_port_block_to_access_port", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_access_port_block_to_access_port/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_access_port_block_to_access_port/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "302595aa86e1ba3434fa3b62a5a67d7ff313cf4b66d6a2985c7cf00b9f70df86", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_access_port_block_to_access_port/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_selector_to_switch_policy_leaf_profile", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_selector_to_switch_policy_leaf_profile/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_selector_to_switch_policy_leaf_profile/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "17613562e9afcee2a22a3561fb48d712bb11d2c75cdc943e3f579243002fbfcb", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_selector_to_switch_policy_leaf_profile/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_filter_entry", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_filter_entry/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_filter_entry/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ec89c0177c4aef8eeb8cc6a9fa86ccbae363e74b15b0f93904367d41a6b7bd10", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_filter_entry/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vlan_pool_encap_block", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vlan_pool_encap_block/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vlan_pool_encap_block/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "75de5c4fc7645577b679e2f19cf9e7d8c52b419ace18f4f2337171b4f02cbebf", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vlan_pool_encap_block/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vlan_pool", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vlan_pool/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vlan_pool/tasks/dynamic.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "48460744b70367fdb873c13664c6f84baa3a6897b37d48a863233fce20aa7671", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vlan_pool/tasks/static.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "444e2593c90eac02ddd8f07369bb78a237dba0cafc94f099d5ea6e6a26e613ed", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vlan_pool/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e76d7fb454bd723c8d50d843592740a7ccd9c55be5adca9696e58f87fcb61915", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vlan_pool/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain_to_encap_pool", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain_to_encap_pool/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain_to_encap_pool/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1844a85615bd16d2bec3d8eb61f04466c3b577879a7641a31922709a97f020f9", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain_to_encap_pool/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_logical_interface_vpc_member", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_logical_interface_vpc_member/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_logical_interface_vpc_member/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3851edfac41a945b9f385dc13fa3fd17d86ad69a5cda1f05270ac6b3af779a4e", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_logical_interface_vpc_member/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_access_port_to_interface_policy_leaf_profile", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_access_port_to_interface_policy_leaf_profile/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_access_port_to_interface_policy_leaf_profile/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6c86383d15c5ed0e2ca4b19fa79985fe774ae3ca967b524bfdc977b6981a8ba5", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_access_port_to_interface_policy_leaf_profile/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_vpn_gateway", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_vpn_gateway/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_vpn_gateway/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3b8e50d44778469f24eeb06adbf0e81b38549a9be2b78e4be7fe2cd23530db7f", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_vpn_gateway/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_bd", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_bd/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_bd/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "69eb8ee842de7a9d5c5a5d8205a6cea9f96d9c6853bb9c4273cd47990d90b0ff", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_bd/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_config_snapshot", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_config_snapshot/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_config_snapshot/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9421728c55bcc8e0b4ced96f918cd211818ac18d6bcdfe60ecd814cb619425a4", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_config_snapshot/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_leaf_breakout_port_group", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_leaf_breakout_port_group/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_leaf_breakout_port_group/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9d5ef7fdf45d2ad149fc8755388332bf48ada02be873c84efc4d11b0a9ef1c4b", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_leaf_breakout_port_group/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_ap", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_ap/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_ap/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "636337667a8e39effe478991b00879375187a0c94c6ddb8998894443230ce90a", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_ap/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_bd_subnet", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_bd_subnet/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_bd_subnet/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5d264e7f48fdff018bf886edf3f3d9a5fe7b523c55fd1b835f9154037cdff393", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_bd_subnet/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_contract", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_contract/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_contract/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3b8e1e7ca5d0e43b23de7430700dd50360d1f4f8f40a3cb4e910d4d17cba3c29", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_contract/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_leaf_policy_group", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_leaf_policy_group/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_leaf_policy_group/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d88f7cfa27b684113ed401b53f6a68c7902bd923b76a38037cde17012d63e851", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_leaf_policy_group/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_extepg_to_contract", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_extepg_to_contract/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_extepg_to_contract/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e31a115146e45d490d640c244a07e479e2f38fddd1a4edcf9e23c465d21d86d8", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_extepg_to_contract/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_taboo_contract", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_taboo_contract/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_taboo_contract/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "53e04e56771d3b74a5b672935be3e8466768e334399499a9b9f625e81b1fd6ab", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_taboo_contract/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain_to_vlan_pool", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain_to_vlan_pool/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain_to_vlan_pool/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6a129f9b4b6cccccfd27b7820c01b674d875379b635238fac2fd73eb320665be", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain_to_vlan_pool/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg_to_domain", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg_to_domain/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg_to_domain/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c2cf8fcae73240916ddb1c7fafc117b49969fcac17cac6b2a3efb8445d3e80e1", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg_to_domain/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_cdp", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_cdp/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_cdp/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "28d97d22d9d57fe3eea01812765d6d0fa5d0da24ffea67107a0b46ca6f8086fd", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_cdp/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7e5f1f59656b5c296b78849e4845810f74bbe10151203a6d224474a31f470f3f", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_tenant", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_tenant/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_tenant/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b14d80fb154ad98992b0a6a0c7a1cce018f87d50afdf524699d8439941817f55", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_tenant/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aep", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aep/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aep/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "95ab6919b4c83c6e7be692c372d4c57c982accec8b50f080f16f642c1f90d0a9", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aep/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_link_level", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_link_level/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_link_level/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b055b73ed42cf465ff92d17a7d30487af202c192ca67366968880534c7317be1", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_link_level/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_extsubnet", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_extsubnet/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_extsubnet/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3be8b756d85fc8dc8b72ccb506bf3e9cad396698ae65b85e0ec1be5708709e1a", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_extsubnet/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l2out", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l2out/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l2out/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7f4423a6bdeeebdb16684343d744d83bb07c734ef5e6138f97b4d54fccfd0ba9", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l2out/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain/tasks/vmm-vmware.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "959a617c0be88f33096cb1942485f8545bf0c26302eb42d2af560c98d6526d39", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain/tasks/l2dom.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "703e40fb6d8eacdee5ac7895835a8f15e07c569aa13fe5dda0ee034020bbf702", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain/tasks/fc.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e7adac87a2d6a985a3912607cc2ee38f1b79c817bf30e7b30ee0a69bc7bbe3f7", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain/tasks/phys.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a3d37b1db065366bdee6ca7c977129eb278390171bce9c3cf50fd73cacd7e3ef", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8094a133d927bf97b43ccdc714c7b8b962af676a11e616f205f21e605f27f1e9", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain/tasks/l3dom.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "70bf07065aee2ddb867e2c2edb238d17fead70d369b776ce7f0973793995aa02", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_domain/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg_to_contract", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg_to_contract/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg_to_contract/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "39737fc5e285966c703f3918665cca13e2032452ab8bb537770649d77130e5d1", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg_to_contract/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_leaf_profile", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_leaf_profile/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_leaf_profile/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "309e9c7d77e445bbde896d1e69ae9302bf25370958d3dc233a231af803de1ca3", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_leaf_profile/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_fabric_node", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_fabric_node/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_fabric_node/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4bf934fa7834ad085caa3817a50559bd17d536b2967e0d823602510616d53e45", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_fabric_node/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aep_to_domain", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aep_to_domain/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aep_to_domain/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9c324d2cbfc8fce8e1793e38d1b7fd3f5d003793db2c4f82d5da0418a78c3f8c", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aep_to_domain/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool_range", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool_range/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool_range/tasks/vsan.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b465efdc08e1e1a1a14290d23b5577dc5715fde6e1cba402d0312b3ef61f8a17", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool_range/tasks/vlan.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8fc7481d36c2521ddec7b606520d1800ecac3f76bc0f8bac6c56e1bdee5f7e8e", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool_range/tasks/vxlan.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9a6dfeec5aa6516e733189aa6d28b0e6ef8876f23c173fb75079a2b7d6466f8c", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool_range/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5db109efeefd874fc7bf1d2a065a9486de6e4f425ad1aa4d05b991d0e3b4ea1c", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool_range/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_ospf", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_ospf/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_ospf/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8654a33e0a398c7ff6952476008457903409186552e9ac1d3d19d3e65df3286d", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_ospf/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_provider", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_provider/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_provider/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "688bf2fd96a6cdafac44d492e9a87baeb9ba099ebab9f2c859d25717ca056dae", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_provider/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c8b190f24432bcb398a73cc0b133fb50b36e1bec6eb9c0b4fa4e7797535d54fd", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg_to_contract_master", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg_to_contract_master/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg_to_contract_master/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4fae553fe3fa2dea6fd434d77b0ba9520e9d0366be1dd60f4b0f17b8668b96e3", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg_to_contract_master/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_subnet", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_subnet/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_subnet/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "eb13dc900d536786df7b30a0097acd5bb93f1da9e777e49f6d6d202a4b35ed16", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_subnet/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vrf", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vrf/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vrf/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5121d7b9dc6e3596602cbb61b7276a27eb8e65456e8a197112c821079c08145b", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vrf/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_filter", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_filter/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_filter/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8266681c02afd2fe0727d5b9b9cdad65d9d3e6839050cb4f5cb5ae4b3d7d2da3", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_filter/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bd4e0aaddf0a6c1884b87d7e667d6c6eb7c7e06efa5ab47422fc60a3b3efef71", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_epg/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_static_binding_to_epg", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_static_binding_to_epg/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_static_binding_to_epg/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e1b29a9a51fce0d2f865d00a3b952298abbb6f25b4fca3117de5b4994139a52e", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_static_binding_to_epg/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user_certificate", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user_certificate/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user_certificate/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9d7ff43eec015a9f3f9572411178bf9b79312d587a38c1f7b145593873225d39", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user_certificate/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user_certificate/pki", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user_certificate/pki/admin_invalid.key", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "55fb85eae133a7b8649703b1d6e303cdf5b585d381e761f3674d1fcc5beb40cb", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user_certificate/pki/rsa_user.key", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8a74dffaf7d04e74642988302b185f0e9eda2dd35a5b7f32317696b12a9790c1", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user_certificate/pki/admin.crt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f97928b2cd1ce43cfe9ae9bbf47a40b858a749c4c3f25a7ec02898611f4fee90", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user_certificate/pki/openssh_rsa.key", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "81a6ac3a61f2df64339252db67fea1307e891c41232e2da53a1e5219f677c917", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user_certificate/pki/admin.key", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "947c1b5a6c419641d4b2c8492b91f2d71588958666aeceeffb1f878460b1d256", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user_certificate/pki/rsa_user.crt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "77629e6b31c8c9fad7052f4696dcead786fecef66e33e81c50ce09fcb39d8b4a", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_aaa_user_certificate/pki/rsa_ansible.key", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6945fcb844179106a63be7e8ea5b407f31a8a1e95a1eaa0b5e67da1c26802032", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_ctx_profile", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_ctx_profile/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_ctx_profile/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b975c93d5b5349200870eb59a8d67f48ac2125d0aa4e58e6538784d88d037769", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_ctx_profile/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_contract_subject", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_contract_subject/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_contract_subject/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e2efff70368092af76367a837d66ec0f5f213e8f2dcb18f86ff17387635367b7", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_contract_subject/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l2out_extepg", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l2out_extepg/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l2out_extepg/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c48c8e04da56cfc813a2d61eefe6f967f1fa960b7a47ee0249399561118e3a12", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l2out_extepg/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_static_routes", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_static_routes/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_static_routes/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fb2042728b7d89dfc37377cb2bb4220e18b6cf1559fa96fa249c86c02274e215", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_l3out_static_routes/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_zone", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_zone/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_zone/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1068d22db9147852b860870ac376782f1e7dde0a36b1a6864b644e99271a23ca", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_zone/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_switch_leaf_selector", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_switch_leaf_selector/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_switch_leaf_selector/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "92eff154bbd10cb5f9c4f29a6df3db4449b5b492bc1005aa6526b18d7d70cdbc", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_switch_leaf_selector/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_region", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_region/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_region/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "81276c113cd83391e893462842a0700fc1a1bf27c9802acd596023e0cb406992", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_region/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_switch_policy_leaf_profile", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_switch_policy_leaf_profile/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_switch_policy_leaf_profile/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "df9813a66a2f13fcb63dc567c9c837dd5361fd8cd95d11bf7acc0161fc74314b", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_switch_policy_leaf_profile/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vmm_credential", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vmm_credential/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vmm_credential/tasks/vmware.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d5a44d6dc013507d32ffe42b42fdf6ddb5d0b7926fd36a7940083ed390db1a59", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vmm_credential/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3160edcdbd1b730523e2239ca29b9ce2050d22d65028504573171f59b2a62e82", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_vmm_credential/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_switch_policy_vpc_protection_group", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_switch_policy_vpc_protection_group/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_switch_policy_vpc_protection_group/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3665afd2390c61d564b42ec3d5d198283a16c947a332e441c3d0cf6234e06b82", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_switch_policy_vpc_protection_group/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_config_rollback", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_config_rollback/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_config_rollback/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "91881432f41395ee83f079d8c9eafa410c41deaa076b0606f33b9818dc6271f9", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_config_rollback/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_port_security", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_port_security/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_port_security/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5ff9173fc6507a1aca48cffa0748004a8055bfeae76e9f9479e6a8eb3f780a1f", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_interface_policy_port_security/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_system", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_system/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_system/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "258a556f7e0b3951c846d808b4da3fc59645ea08e03895990ec9734777196fcc", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_system/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_bd_dhcp_label", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_bd_dhcp_label/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_bd_dhcp_label/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7a723f08c47ecd1f395e357fc117549dccf050580ba706b647aea98332ed8a4e", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_bd_dhcp_label/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool/tasks/vsan.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6d3a61c7dd273ea3d25fc294fd6cb6d389f6eeb92d7475827bfd2be86800d7b9", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool/tasks/vlan.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7eb94b25fdef72fa4505a685d0c269e030e4be19e7ef4b07d19d420d142a73f0", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool/tasks/vxlan.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ac938a576416da777b9fb855810f8dd1d582470b0a6b04c3da971be25a9ecfeb", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "78202505d750c4fd93a314def948be7e95516c4bb820cd9aed37ebd58b3c44ea", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_encap_pool/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_rest", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_rest/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_rest/tasks/json_inline.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a1ff9d6891ccf01fa4dcb08f1de31fe4e484d7d56632dce362989c6b5f731887", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_rest/tasks/json_string.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "27682e4605d65f1d9b188eddcb49aa7881cec73309ab5d519e5cf5b26379a13f", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_rest/tasks/yaml_inline.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "56f653f63378ed596d11764df28f5703066d41d3b8829bc6efb91807a60f1c30", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_rest/tasks/xml_string.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7c1720f00012ec0e72d8bda66f6407bc24b724d04baf77d0404cfe1b89af21ab", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_rest/tasks/yaml_string.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "56f653f63378ed596d11764df28f5703066d41d3b8829bc6efb91807a60f1c30", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_rest/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b28a5c321130fdea95a8fe4b349662f499fec0d1bd10fa4f190df9154d798fcc", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_rest/tasks/error_handling.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5e118317b9f9438907e0e9c327afcfb4adc2feefdbd646b97df53890e9359553", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_rest/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_contract_subject_to_filter", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_contract_subject_to_filter/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_contract_subject_to_filter/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4c4e96f5fd47e53addc3c926b16011820324542e82a7700ba756ee76e75610be", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_contract_subject_to_filter/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_access_sub_port_block_to_access_port", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_access_sub_port_block_to_access_port/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_access_sub_port_block_to_access_port/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9338f942c41c0dd7a2ef0165c41b1b36800e2dfc376f7822a49106b8d1f0a87f", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_access_sub_port_block_to_access_port/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_cidr", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_cidr/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_cidr/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "50047d8dbfeb8d2015ad34d4174710a4c283c0c63c88a8b80fefb2ad5f7daab9", + "format": 1 + }, + { + "name": "tests/integration/targets/aci_cloud_cidr/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "616f27a9a7bb49e561ef3782330e18d1a8cea3840e3166e798796684dd546a69", + "format": 1 + }, + { + "name": "tests/integration/network-integration.requirements.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "61902b3c85adc8a2143950820ed906014fbbc48b98f1dcdbc3ea3edf36c84f0a", + "format": 1 + }, + { + "name": "tests/integration/target-prefixes.network", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "febb2a0938d28c21b2834600aa14a4878723de041f5f95a9c753dc7fe14794f3", + "format": 1 + }, + { + "name": "tests/integration/inventory.networking", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "71c715d3172e5995476fcb005eb7367b04df579567d0ed9c31208de69cf82725", + "format": 1 + }, + { + "name": "tests/sanity", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/sanity/requirements.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c8a4ac4bfdef88e75d6e748e35a42fb4915947dfa2b7dd788626fd829600e014", + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.10.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.9.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/.gitignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b5726d3ec9335a09c124469eca039523847a6b0f08a083efaefd002b83326600", + "format": 1 + }, + { + "name": "meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "meta/runtime.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "48a2f61c72296aaa190a3ad670b0a5759bd047262b59b3d870e986b05878d87d", + "format": 1 + }, + { + "name": "changelogs", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "changelogs/config.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3e20eced2de0cbf27b3220fcf0e10f59ef770591c430effa860ce0ceba6c0cbf", + "format": 1 + }, + { + "name": "changelogs/.gitignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "919ef00776e7d2ff349950ac4b806132aa9faf006e214d5285de54533e443b33", + "format": 1 + }, + { + "name": "changelogs/.plugin-cache.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8d4a9e6df40c7c8cc866cce31a6f009e8128ecdc478825e04efe176a09bd488b", + "format": 1 + }, + { + "name": "changelogs/changelog.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d54dc2777895e53a8d11e3f4959259db0cb12eb7a5d0407edc08503df4374611", + "format": 1 + }, + { + "name": "README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9a92f358d773d20208cf3d33bc9b0c297ee9e097ecf04369b558c9fa0a3ae5b8", + "format": 1 + }, + { + "name": ".gitignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c407a0218d3e6adafc83a5c5ff8a5df0bd63aa887c414756810efe035ea67e33", + "format": 1 + }, + { + "name": ".github", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".github/workflows", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".github/workflows/ansible-test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e736c682ce3d31fa3698816eb359042baf3d6e1af46c063d32df17dedc23324d", + "format": 1 + }, + { + "name": "CHANGELOG.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4a13d21a307458494ced0f3b9a0545b08924361fe23ec09b7db7e7b38b0ecb69", + "format": 1 + }, + { + "name": ".vscode", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".vscode/settings.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d7f0d7666ed5a63b02508a1d58a523d82f97aeeae83659e2ad4bc33dc2e25070", + "format": 1 + } + ], + "format": 1 +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/LICENSE b/collections-debian-merged/ansible_collections/cisco/aci/LICENSE new file mode 100644 index 00000000..e09a4143 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/LICENSE @@ -0,0 +1,678 @@ +Copyright (c) 2019, Cisco Systems +All rights reserved. + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. + diff --git a/collections-debian-merged/ansible_collections/cisco/aci/MANIFEST.json b/collections-debian-merged/ansible_collections/cisco/aci/MANIFEST.json new file mode 100644 index 00000000..594286d9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/MANIFEST.json @@ -0,0 +1,45 @@ +{ + "collection_info": { + "namespace": "cisco", + "name": "aci", + "version": "1.1.1", + "authors": [ + "Dag Wieers (@dagwieers) <dag@wieers.com>", + "Swetha Chunduri (@schunduri)", + "Jacob McGill (@jmcgill298)", + "Rob Huelga (@RobW3LGA)", + "Bruno Calogero <brunocalogero@hotmail.com>", + "Simon Metzger <smnmtzgr@gmail.com>", + "Tim Knipper <tim.knipper@gmail.com>", + "Apoorva Gururaja (@aciguru)", + "Devarshi Shah (@devarshishah3)", + "Ramses Smeyers (@rsmeyers)", + "Lionel Hercot (@lhercot) <lhercot@cisco.com>" + ], + "readme": "README.md", + "tags": [ + "cisco", + "aci", + "cloud", + "collection", + "networking", + "sdn" + ], + "description": "Ansible Modules for Cisco ACI", + "license": [], + "license_file": "LICENSE", + "dependencies": {}, + "repository": "https://github.com/CiscoDevNet/ansible-aci", + "documentation": "https://docs.ansible.com/ansible/latest/scenario_guides/guide_aci.html", + "homepage": "https://github.com/CiscoDevNet/ansible-aci", + "issues": "https://github.com/CiscoDevNet/ansible-aci/issues" + }, + "file_manifest_file": { + "name": "FILES.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8646ec2754f5caace3ddf61b0105408f918de0ee2664906215c5039c9f8d40d0", + "format": 1 + }, + "format": 1 +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/README.md b/collections-debian-merged/ansible_collections/cisco/aci/README.md new file mode 100644 index 00000000..4e5cdf6a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/README.md @@ -0,0 +1,91 @@ +# ansible-aci + +The ansible-aci project provides an Ansible collection for managing and automating your Cisco ACI environment. It consists of a set of modules and roles for performing tasks related to ACI. + +This collection has been tested and supports ACI 3.2+. +Modules supporting new features introduced in ACI API in specific ACI versions might not be supported in earlier ACI releases. + +*Note: This collection is not compatible with versions of Ansible before v2.8.* + +## Requirements +Ansible v2.9 or newer + +## Install +Ansible must be installed +``` +sudo pip install ansible +``` + +Install the collection +``` +ansible-galaxy collection install cisco.aci +``` +## Use +Once the collection is installed, you can use it in a playbook by specifying the full namespace path to the module, plugin and/or role. + +``` +- hosts: aci + gather_facts: no + + tasks: + - name: Add a new EPG + cisco.aci.aci_epg: + hostname: apic + username: admin + password: SomeSecretPassword + tenant: production + ap: intranet + epg: web_epg + description: Web Intranet EPG + bd: prod_bd + delegate_to: localhost +``` + +## Update +Getting the latest/nightly collection build + +### First Approach +Clone the ansible-aci repository. +``` +git clone https://github.com/CiscoDevNet/ansible-aci.git +``` + +Go to the ansible-aci directory +``` +cd ansible-aci +``` + +Pull the latest master on your aci +``` +git pull origin master +``` + +Build and Install a collection from source +``` +ansible-galaxy collection build --force +ansible-galaxy collection install cisco-aci-* --force +``` + +### Second Approach +Go to: https://github.com/CiscoDevNet/ansible-aci/actions + +Select the latest CI build + +Under Artifacts download collection and unzip it using Terminal or Console. + +*Note: The collection file is a zip file containing a tar.gz file. We recommend using CLI because some GUI-based unarchiver might unarchive both nested archives in one go.* + +Install the unarchived tar.gz file +``` +ansible-galaxy collection install cisco-aci-1.0.0.tar.gz —-force +``` + +### See Also: + +* [Ansible Using collections](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html) for more details. + +## Contributing to this collection + +Ongoing development efforts and contributions to this collection are tracked as issues in this repository. + +We welcome community contributions to this collection. If you find problems, need an enhancement or need a new module, please open an issue or create a PR against the [Cisco ACI collection repository](https://github.com/CiscoDevNet/ansible-aci/issues). diff --git a/collections-debian-merged/ansible_collections/cisco/aci/changelogs/.gitignore b/collections-debian-merged/ansible_collections/cisco/aci/changelogs/.gitignore new file mode 100644 index 00000000..6be6b533 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/changelogs/.gitignore @@ -0,0 +1 @@ +/.plugin-cache.yaml diff --git a/collections-debian-merged/ansible_collections/cisco/aci/changelogs/.plugin-cache.yaml b/collections-debian-merged/ansible_collections/cisco/aci/changelogs/.plugin-cache.yaml new file mode 100644 index 00000000..eeebb8f7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/changelogs/.plugin-cache.yaml @@ -0,0 +1,444 @@ +plugins: + become: {} + cache: {} + callback: {} + cliconf: {} + connection: {} + httpapi: {} + inventory: {} + lookup: {} + module: + aci_aaa_user: + description: Manage AAA users (aaa:User) + name: aci_aaa_user + namespace: '' + version_added: null + aci_aaa_user_certificate: + description: Manage AAA user certificates (aaa:UserCert) + name: aci_aaa_user_certificate + namespace: '' + version_added: null + aci_access_port_block_to_access_port: + description: Manage port blocks of Fabric interface policy leaf profile interface + selectors (infra:HPortS, infra:PortBlk) + name: aci_access_port_block_to_access_port + namespace: '' + version_added: null + aci_access_port_to_interface_policy_leaf_profile: + description: Manage Fabric interface policy leaf profile interface selectors + (infra:HPortS, infra:RsAccBaseGrp, infra:PortBlk) + name: aci_access_port_to_interface_policy_leaf_profile + namespace: '' + version_added: null + aci_access_sub_port_block_to_access_port: + description: Manage sub port blocks of Fabric interface policy leaf profile + interface selectors (infra:HPortS, infra:SubPortBlk) + name: aci_access_sub_port_block_to_access_port + namespace: '' + version_added: null + aci_aep: + description: Manage attachable Access Entity Profile (AEP) objects (infra:AttEntityP, + infra:ProvAcc) + name: aci_aep + namespace: '' + version_added: null + aci_aep_to_domain: + description: Bind AEPs to Physical or Virtual Domains (infra:RsDomP) + name: aci_aep_to_domain + namespace: '' + version_added: null + aci_ap: + description: Manage top level Application Profile (AP) objects (fv:Ap) + name: aci_ap + namespace: '' + version_added: null + aci_bd: + description: Manage Bridge Domains (BD) objects (fv:BD) + name: aci_bd + namespace: '' + version_added: null + aci_bd_dhcp_label: + description: Manage DHCP Labels (dhcp:Lbl) + name: aci_bd_dhcp_label + namespace: '' + version_added: null + aci_bd_subnet: + description: Manage Subnets (fv:Subnet) + name: aci_bd_subnet + namespace: '' + version_added: null + aci_bd_to_l3out: + description: Bind Bridge Domain to L3 Out (fv:RsBDToOut) + name: aci_bd_to_l3out + namespace: '' + version_added: null + aci_cloud_cidr: + description: Manage CIDR under Cloud Context Profile (cloud:Cidr) + name: aci_cloud_cidr + namespace: '' + version_added: null + aci_cloud_ctx_profile: + description: Manage Cloud Context Profile (cloud:CtxProfile) + name: aci_cloud_ctx_profile + namespace: '' + version_added: null + aci_cloud_provider: + description: Query Cloud Provider information (cloud:ProvP) + name: aci_cloud_provider + namespace: '' + version_added: null + aci_cloud_region: + description: Manage Cloud Providers Region (cloud:Region) + name: aci_cloud_region + namespace: '' + version_added: null + aci_cloud_subnet: + description: Manage Cloud Subnet (cloud:Subnet) + name: aci_cloud_subnet + namespace: '' + version_added: null + aci_cloud_vpn_gateway: + description: Manage cloudRouterP in Cloud Context Profile (cloud:cloudRouterP) + name: aci_cloud_vpn_gateway + namespace: '' + version_added: null + aci_cloud_zone: + description: Manage Cloud Availability Zone (cloud:Zone) + name: aci_cloud_zone + namespace: '' + version_added: null + aci_config_rollback: + description: Provides rollback and rollback preview functionality (config:ImportP) + name: aci_config_rollback + namespace: '' + version_added: null + aci_config_snapshot: + description: Manage Config Snapshots (config:Snapshot, config:ExportP) + name: aci_config_snapshot + namespace: '' + version_added: null + aci_contract: + description: Manage contract resources (vz:BrCP) + name: aci_contract + namespace: '' + version_added: null + aci_contract_subject: + description: Manage initial Contract Subjects (vz:Subj) + name: aci_contract_subject + namespace: '' + version_added: null + aci_contract_subject_to_filter: + description: Bind Contract Subjects to Filters (vz:RsSubjFiltAtt) + name: aci_contract_subject_to_filter + namespace: '' + version_added: null + aci_domain: + description: Manage physical, virtual, bridged, routed or FC domain profiles + (phys:DomP, vmm:DomP, l2ext:DomP, l3ext:DomP, fc:DomP) + name: aci_domain + namespace: '' + version_added: null + aci_domain_to_encap_pool: + description: Bind Domain to Encap Pools (infra:RsVlanNs) + name: aci_domain_to_encap_pool + namespace: '' + version_added: null + aci_domain_to_vlan_pool: + description: Bind Domain to VLAN Pools (infra:RsVlanNs) + name: aci_domain_to_vlan_pool + namespace: '' + version_added: null + aci_encap_pool: + description: Manage encap pools (fvns:VlanInstP, fvns:VxlanInstP, fvns:VsanInstP) + name: aci_encap_pool + namespace: '' + version_added: null + aci_encap_pool_range: + description: Manage encap ranges assigned to pools (fvns:EncapBlk, fvns:VsanEncapBlk) + name: aci_encap_pool_range + namespace: '' + version_added: null + aci_epg: + description: Manage End Point Groups (EPG) objects (fv:AEPg) + name: aci_epg + namespace: '' + version_added: null + aci_epg_monitoring_policy: + description: Manage monitoring policies (mon:EPGPol) + name: aci_epg_monitoring_policy + namespace: '' + version_added: null + aci_epg_to_contract: + description: Bind EPGs to Contracts (fv:RsCons, fv:RsProv) + name: aci_epg_to_contract + namespace: '' + version_added: null + aci_epg_to_contract_master: + description: Manage End Point Group (EPG) contract master relationships (fv:RsSecInherited) + name: aci_epg_to_contract_master + namespace: '' + version_added: null + aci_epg_to_domain: + description: Bind EPGs to Domains (fv:RsDomAtt) + name: aci_epg_to_domain + namespace: '' + version_added: null + aci_fabric_node: + description: Manage Fabric Node Members (fabric:NodeIdentP) + name: aci_fabric_node + namespace: '' + version_added: null + aci_fabric_scheduler: + description: This modules creates ACI schedulers. + name: aci_fabric_scheduler + namespace: '' + version_added: null + aci_filter: + description: Manages top level filter objects (vz:Filter) + name: aci_filter + namespace: '' + version_added: null + aci_filter_entry: + description: Manage filter entries (vz:Entry) + name: aci_filter_entry + namespace: '' + version_added: null + aci_firmware_group: + description: This module creates a firmware group + name: aci_firmware_group + namespace: '' + version_added: null + aci_firmware_group_node: + description: This modules adds and remove nodes from the firmware group + name: aci_firmware_group_node + namespace: '' + version_added: null + aci_firmware_policy: + description: This creates a firmware policy + name: aci_firmware_policy + namespace: '' + version_added: null + aci_firmware_source: + description: Manage firmware image sources (firmware:OSource) + name: aci_firmware_source + namespace: '' + version_added: null + aci_interface_policy_cdp: + description: Manage CDP interface policies (cdp:IfPol) + name: aci_interface_policy_cdp + namespace: '' + version_added: null + aci_interface_policy_fc: + description: Manage Fibre Channel interface policies (fc:IfPol) + name: aci_interface_policy_fc + namespace: '' + version_added: null + aci_interface_policy_l2: + description: Manage Layer 2 interface policies (l2:IfPol) + name: aci_interface_policy_l2 + namespace: '' + version_added: null + aci_interface_policy_leaf_breakout_port_group: + description: Manage fabric interface policy leaf breakout port group (infra:BrkoutPortGrp) + name: aci_interface_policy_leaf_breakout_port_group + namespace: '' + version_added: null + aci_interface_policy_leaf_policy_group: + description: Manage fabric interface policy leaf policy groups (infra:AccBndlGrp, + infra:AccPortGrp) + name: aci_interface_policy_leaf_policy_group + namespace: '' + version_added: null + aci_interface_policy_leaf_profile: + description: Manage fabric interface policy leaf profiles (infra:AccPortP) + name: aci_interface_policy_leaf_profile + namespace: '' + version_added: null + aci_interface_policy_link_level: + description: Manage Link Level interface policies (fabric:HIfPol) + name: aci_interface_policy_link_level + namespace: '' + version_added: null + aci_interface_policy_lldp: + description: Manage LLDP interface policies (lldp:IfPol) + name: aci_interface_policy_lldp + namespace: '' + version_added: null + aci_interface_policy_mcp: + description: Manage MCP interface policies (mcp:IfPol) + name: aci_interface_policy_mcp + namespace: '' + version_added: null + aci_interface_policy_ospf: + description: Manage OSPF interface policies (ospf:IfPol) + name: aci_interface_policy_ospf + namespace: '' + version_added: null + aci_interface_policy_port_channel: + description: Manage port channel interface policies (lacp:LagPol) + name: aci_interface_policy_port_channel + namespace: '' + version_added: null + aci_interface_policy_port_security: + description: Manage port security (l2:PortSecurityPol) + name: aci_interface_policy_port_security + namespace: '' + version_added: null + aci_interface_selector_to_switch_policy_leaf_profile: + description: Bind interface selector profiles to switch policy leaf profiles + (infra:RsAccPortP) + name: aci_interface_selector_to_switch_policy_leaf_profile + namespace: '' + version_added: null + aci_l2out: + description: Manage Layer2 Out (L2Out) objects. + name: aci_l2out + namespace: '' + version_added: null + aci_l2out_extepg: + description: Manage External Network Instance (L2Out External EPG) objects (l2extInstP). + name: aci_l2out_extepg + namespace: '' + version_added: null + aci_l3out: + description: Manage Layer 3 Outside (L3Out) objects (l3ext:Out) + name: aci_l3out + namespace: '' + version_added: null + aci_l3out_extepg: + description: Manage External Network Instance Profile (ExtEpg) objects (l3extInstP:instP) + name: aci_l3out_extepg + namespace: '' + version_added: null + aci_l3out_extepg_to_contract: + description: Bind Contracts to External End Point Groups (EPGs) + name: aci_l3out_extepg_to_contract + namespace: '' + version_added: null + aci_l3out_extsubnet: + description: Manage External Subnet objects (l3extSubnet:extsubnet) + name: aci_l3out_extsubnet + namespace: '' + version_added: null + aci_l3out_logical_interface_vpc_member: + description: Manage Member Node objects (l3extMember:Member) + name: aci_l3out_logical_interface_vpc_member + namespace: '' + version_added: null + aci_l3out_route_tag_policy: + description: Manage route tag policies (l3ext:RouteTagPol) + name: aci_l3out_route_tag_policy + namespace: '' + version_added: null + aci_l3out_static_routes: + description: Manage Static routes object (l3ext:ipRouteP) + name: aci_l3out_static_routes + namespace: '' + version_added: null + aci_maintenance_group: + description: This creates an ACI maintenance group + name: aci_maintenance_group + namespace: '' + version_added: null + aci_maintenance_group_node: + description: Manage maintenance group nodes + name: aci_maintenance_group_node + namespace: '' + version_added: null + aci_maintenance_policy: + description: Manage firmware maintenance policies + name: aci_maintenance_policy + namespace: '' + version_added: null + aci_rest: + description: Direct access to the Cisco APIC REST API + name: aci_rest + namespace: '' + version_added: null + aci_static_binding_to_epg: + description: Bind static paths to EPGs (fv:RsPathAtt) + name: aci_static_binding_to_epg + namespace: '' + version_added: null + aci_switch_leaf_selector: + description: Bind leaf selectors to switch policy leaf profiles (infra:LeafS, + infra:NodeBlk, infra:RsAccNodePGrep) + name: aci_switch_leaf_selector + namespace: '' + version_added: null + aci_switch_policy_leaf_profile: + description: Manage switch policy leaf profiles (infra:NodeP) + name: aci_switch_policy_leaf_profile + namespace: '' + version_added: null + aci_switch_policy_vpc_protection_group: + description: Manage switch policy explicit vPC protection groups (fabric:ExplicitGEp, + fabric:NodePEp). + name: aci_switch_policy_vpc_protection_group + namespace: '' + version_added: null + aci_system: + description: Query the ACI system information (top:System) + name: aci_system + namespace: '' + version_added: null + aci_taboo_contract: + description: Manage taboo contracts (vz:BrCP) + name: aci_taboo_contract + namespace: '' + version_added: null + aci_tenant: + description: Manage tenants (fv:Tenant) + name: aci_tenant + namespace: '' + version_added: null + aci_tenant_action_rule_profile: + description: Manage action rule profiles (rtctrl:AttrP) + name: aci_tenant_action_rule_profile + namespace: '' + version_added: null + aci_tenant_ep_retention_policy: + description: Manage End Point (EP) retention protocol policies (fv:EpRetPol) + name: aci_tenant_ep_retention_policy + namespace: '' + version_added: null + aci_tenant_span_dst_group: + description: Manage SPAN destination groups (span:DestGrp) + name: aci_tenant_span_dst_group + namespace: '' + version_added: null + aci_tenant_span_src_group: + description: Manage SPAN source groups (span:SrcGrp) + name: aci_tenant_span_src_group + namespace: '' + version_added: null + aci_tenant_span_src_group_to_dst_group: + description: Bind SPAN source groups to destination groups (span:SpanLbl) + name: aci_tenant_span_src_group_to_dst_group + namespace: '' + version_added: null + aci_vlan_pool: + description: Manage VLAN pools (fvns:VlanInstP) + name: aci_vlan_pool + namespace: '' + version_added: null + aci_vlan_pool_encap_block: + description: Manage encap blocks assigned to VLAN pools (fvns:EncapBlk) + name: aci_vlan_pool_encap_block + namespace: '' + version_added: null + aci_vmm_credential: + description: Manage virtual domain credential profiles (vmm:UsrAccP) + name: aci_vmm_credential + namespace: '' + version_added: null + aci_vrf: + description: Manage contexts or VRFs (fv:Ctx) + name: aci_vrf + namespace: '' + version_added: null + netconf: {} + shell: {} + strategy: {} + vars: {} +version: 1.1.1 diff --git a/collections-debian-merged/ansible_collections/cisco/aci/changelogs/changelog.yaml b/collections-debian-merged/ansible_collections/cisco/aci/changelogs/changelog.yaml new file mode 100644 index 00000000..a2163676 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/changelogs/changelog.yaml @@ -0,0 +1,102 @@ +ancestor: null +releases: + 1.0.0: + changes: + bugfixes: + - Fix sanity issues to support 2.10.0 + - Fix some doc issues for a few modules + - Fix some formatting issues (flake8) in unit tests. + - Fixing integration tests and sanity. Tested on ACI 4.2(3l). + minor_changes: + - Add Fex capability to aci_interface_policy_leaf_profile, aci_access_port_to_interface_policy_leaf_profile + and aci_access_port_block_to_access_port + - Add LICENSE file + - Add aci_epg_to_contract_master module + - Add annotation attribute to aci.py and to doc fragment. + - Add annotation to every payload and add test case for annotation. + - Add changelog + - Add collection prefix to all integration tests + - Add galaxy.yml file for collection listing + - Add github action CI pipeline + - Add module and test file for aci_bd_dhcp_label + - Add modules and test files for aci_cloud_ctx_profile, aci_cloud_cidr, aci_cloud_subnet + and aci_cloud_zone + - Add modules and test files for aci_l2out, aci_l2out_extepg and aci_l3out_extepg_to_contract + - Add names to documentation examples for modules from community.network + - Add preferred group support to aci_vrf + - Add support for Azure on all cloud modules + - Add support for output_path to allow dump of REST API objects + - Add support for owner_key and owner_tag for all modules and add test case + for it. + - Add vpn gateway dedicated module and remove vpn_gateway from cloud_ctx_profile + module + - Fix M() and module to use FQCN + - Initial commit based on the collection migration available at "ansible-collection-migration/cisco.aci" + which contains the ACI module from Ansible Core + - Move aci.py to base of module_utils and fix references + - Move test file to root of tests/unit/module_utils + - Update Ansible version in CI and add 2.10.0 to sanity in CI. + - Update Readme with supported versions + - Update to test files to make the tests work on both 3.2 and 4.2. + release_summary: 'This is the first official release of the ``cisco.aci`` collection + on 2020-08-18. + + This changelog describes all changes made to the modules and plugins included + in this collection since Ansible 2.9.0. + + ' + release_date: '2020-08-18' + 1.0.1: + changes: + bugfixes: + - Fix convertion of json/yaml payload to xml in aci_rest + - Fix dump of config for aci_rest + - Fix issue of "current" in firmware_source module + - Fix sanity issue in aci_rest and bump version to v1.0.1 + minor_changes: + - Enable/Disable infra vlan in aci_aep and its test module + - Set scope default value in aci_l3out_extsubnet + release_summary: 'Release v1.0.1 of the ``cisco.mso`` collection on 2020-10-13. + + This changelog describes all changes made to the modules and plugins included + in this collection since v1.0.0. ' + release_date: '2020-10-13' + 1.1.0: + changes: + bugfixes: + - Existing_config variable is not reset during loop + - Fix galaxy import warnings + - Fix how validity of private key/private key file is checked to support new + types + - Fix incorrect domain types in aci_domain_to_encap_pool module + minor_changes: + - Ability to add monitoring policy to epgs and anps + - Add Ansible Network ENV to fallback + - Add aci_l3out_external_path_to_member.py & aci_l3out_static_routes modules + - Add env_fallback for common connection params + - Add env_fallback for the rest of the argument spec + - Add new Subclass path support + - Add new module and test file for leaf breakout port group + - Added failure message to aci_interface_policy_leaf_policy_group + - Update README.md + - Update inventory + - aci_epg_to_domain addition of promiscuous mode (#79) + - aci_interface_policy_port_security addition of attribute:timeout (#80) + release_summary: 'Release v1.1.0 of the ``cisco.mso`` collection on 2020-10-30. + + This changelog describes all changes made to the modules and plugins included + in this collection since v1.0.1. ' + release_date: '2020-10-30' + 1.1.1: + changes: + bugfixes: + - Fix galaxy import warnings + - Fix sanity issue in aci_epg_to_domain + minor_changes: + - Add test file for aci_domain_to_encap_pool + - aci_epg_to_domain moving child configs & classes to each domain type + release_summary: 'Release v1.1.1 of the ``cisco.mso`` collection on 2020-11-23. + + This changelog describes all changes made to the modules and plugins included + in this collection since v1.1.0. ' + release_date: '2020-11-23' diff --git a/collections-debian-merged/ansible_collections/cisco/aci/changelogs/config.yaml b/collections-debian-merged/ansible_collections/cisco/aci/changelogs/config.yaml new file mode 100644 index 00000000..1d6198ea --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/changelogs/config.yaml @@ -0,0 +1,31 @@ +changelog_filename_template: ../CHANGELOG.rst +changelog_filename_version_depth: 0 +changes_file: changelog.yaml +changes_format: combined +ignore_other_fragment_extensions: true +keep_fragments: false +mention_ancestor: true +new_plugins_after_name: removed_features +notesdir: fragments +prelude_section_name: release_summary +prelude_section_title: Release Summary +sections: +- - major_changes + - Major Changes +- - minor_changes + - Minor Changes +- - breaking_changes + - Breaking Changes / Porting Guide +- - deprecated_features + - Deprecated Features +- - removed_features + - Removed Features (previously deprecated) +- - security_fixes + - Security Fixes +- - bugfixes + - Bugfixes +- - known_issues + - Known Issues +title: Cisco ACI Ansible Collection +trivial_section_name: trivial +use_fqcn: true diff --git a/collections-debian-merged/ansible_collections/cisco/aci/meta/runtime.yml b/collections-debian-merged/ansible_collections/cisco/aci/meta/runtime.yml new file mode 100644 index 00000000..1f18fd72 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/meta/runtime.yml @@ -0,0 +1,2 @@ +--- +requires_ansible: '>=2.9.10'
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/doc_fragments/__init__.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/doc_fragments/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/doc_fragments/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/doc_fragments/aci.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/doc_fragments/aci.py new file mode 100644 index 00000000..38266656 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/doc_fragments/aci.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> +# Copyright: (c) 2017, Swetha Chunduri (@schunduri) +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +class ModuleDocFragment(object): + # Standard files documentation fragment + DOCUMENTATION = r''' +options: + host: + description: + - IP Address or hostname of APIC resolvable by Ansible control host. + - If the value is not specified in the task, the value of environment variable C(ACI_HOST) will be used instead. + type: str + required: yes + aliases: [ hostname ] + port: + description: + - Port number to be used for REST connection. + - The default value depends on parameter C(use_ssl). + - If the value is not specified in the task, the value of environment variable C(ACI_PORT) will be used instead. + type: int + username: + description: + - The username to use for authentication. + - If the value is not specified in the task, the value of environment variables C(ACI_USERNAME) or C(ANSIBLE_NET_USERNAME) will be used instead. + type: str + default: admin + aliases: [ user ] + password: + description: + - The password to use for authentication. + - This option is mutual exclusive with C(private_key). If C(private_key) is provided too, it will be used instead. + - If the value is not specified in the task, the value of environment variables C(ACI_PASSWORD) or C(ANSIBLE_NET_PASSWORD) will be used instead. + type: str + private_key: + description: + - Either a PEM-formatted private key file or the private key content used for signature-based authentication. + - This value also influences the default C(certificate_name) that is used. + - This option is mutual exclusive with C(password). If C(password) is provided too, it will be ignored. + - If the value is not specified in the task, the value of environment variable C(ACI_PRIVATE_KEY) will be used instead. + type: str + aliases: [ cert_key ] + certificate_name: + description: + - The X.509 certificate name attached to the APIC AAA user used for signature-based authentication. + - If a C(private_key) filename was provided, this defaults to the C(private_key) basename, without extension. + - If PEM-formatted content was provided for C(private_key), this defaults to the C(username) value. + - If the value is not specified in the task, the value of environment variable C(ACI_CERTIFICATE_NAME) will be used instead. + type: str + aliases: [ cert_name ] + output_level: + description: + - Influence the output of this ACI module. + - C(normal) means the standard output, incl. C(current) dict + - C(info) adds informational output, incl. C(previous), C(proposed) and C(sent) dicts + - C(debug) adds debugging output, incl. C(filter_string), C(method), C(response), C(status) and C(url) information + - If the value is not specified in the task, the value of environment variable C(ACI_OUTPUT_LEVEL) will be used instead. + type: str + choices: [ debug, info, normal ] + default: normal + timeout: + description: + - The socket level timeout in seconds. + - If the value is not specified in the task, the value of environment variable C(ACI_TIMEOUT) will be used instead. + type: int + default: 30 + use_proxy: + description: + - If C(no), it will not use a proxy, even if one is defined in an environment variable on the target hosts. + - If the value is not specified in the task, the value of environment variable C(ACI_USE_PROXY) will be used instead. + type: bool + default: yes + use_ssl: + description: + - If C(no), an HTTP connection will be used instead of the default HTTPS connection. + - If the value is not specified in the task, the value of environment variable C(ACI_USE_SSL) will be used instead. + type: bool + default: yes + validate_certs: + description: + - If C(no), SSL certificates will not be validated. + - This should only set to C(no) when used on personally controlled sites using self-signed certificates. + - If the value is not specified in the task, the value of environment variable C(ACI_VALIDATE_CERTS) will be used instead. + type: bool + default: yes + output_path: + description: + - Path to a file that will be used to dump the ACI JSON configuration objects generated by the module. + - If the value is not specified in the task, the value of environment variable C(ACI_OUTPUT_PATH) will be used instead. + type: str + annotation: + description: + - User-defined string for annotating an object. + - If the value is not specified in the task, the value of environment variable C(ACI_ANNOTATION) will be used instead. + type: str + owner_key: + description: + - User-defined string for the ownerKey attribute of an ACI object. + - This attribute represents a key for enabling clients to own their data for entity correlation. + - If the value is not specified in the task, the value of environment variable C(ACI_OWNER_KEY) will be used instead. + type: str + owner_tag: + description: + - User-defined string for the ownerTag attribute of an ACI object. + - This attribute represents a tag for enabling clients to add their own data. + - For example, to indicate who created this object. + - If the value is not specified in the task, the value of environment variable C(ACI_OWNER_TAG) will be used instead. + type: str +seealso: +- ref: aci_guide + description: Detailed information on how to manage your ACI infrastructure using Ansible. +- ref: aci_dev_guide + description: Detailed guide on how to write your own Cisco ACI modules to contribute. +''' diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/module_utils/__init__.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/module_utils/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/module_utils/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/module_utils/aci.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/module_utils/aci.py new file mode 100644 index 00000000..9e0759f1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/module_utils/aci.py @@ -0,0 +1,1336 @@ +# -*- coding: utf-8 -*- + +# This code is part of Ansible, but is an independent component + +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. + +# Copyright: (c) 2017, Dag Wieers <dag@wieers.com> +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) +# Copyright: (c) 2017, Swetha Chunduri (@schunduri) +# Copyright: (c) 2019, Rob Huelga (@RobW3LGA) +# Copyright: (c) 2020, Lionel Hercot (@lhercot) <lhercot@cisco.com> +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import base64 +import json +import os +from copy import deepcopy + +from ansible.module_utils.urls import fetch_url +from ansible.module_utils._text import to_bytes, to_native +from ansible.module_utils.basic import env_fallback + +# Optional, only used for APIC signature-based authentication +try: + from OpenSSL.crypto import FILETYPE_PEM, load_privatekey, sign + HAS_OPENSSL = True +except ImportError: + HAS_OPENSSL = False + +# Optional, only used for XML payload +try: + import lxml.etree + HAS_LXML_ETREE = True +except ImportError: + HAS_LXML_ETREE = False + +# Optional, only used for XML payload +try: + from xmljson import cobra + HAS_XMLJSON_COBRA = True +except ImportError: + HAS_XMLJSON_COBRA = False + + +def aci_argument_spec(): + return dict( + host=dict(type='str', required=True, aliases=['hostname'], fallback=(env_fallback, ['ACI_HOST'])), + port=dict(type='int', required=False, fallback=(env_fallback, ['ACI_PORT'])), + username=dict(type='str', default='admin', aliases=['user'], fallback=(env_fallback, ['ACI_USERNAME', 'ANSIBLE_NET_USERNAME'])), + password=dict(type='str', no_log=True, fallback=(env_fallback, ['ACI_PASSWORD', 'ANSIBLE_NET_PASSWORD'])), + # Beware, this is not the same as client_key ! + private_key=dict(type='str', aliases=['cert_key'], no_log=True, fallback=(env_fallback, ['ACI_PRIVATE_KEY'])), + # Beware, this is not the same as client_cert ! + certificate_name=dict(type='str', aliases=['cert_name'], fallback=(env_fallback, ['ACI_CERTIFICATE_NAME'])), + output_level=dict(type='str', default='normal', choices=['debug', 'info', 'normal'], fallback=(env_fallback, ['ACI_OUTPUT_LEVEL'])), + timeout=dict(type='int', default=30, fallback=(env_fallback, ['ACI_TIMEOUT'])), + use_proxy=dict(type='bool', default=True, fallback=(env_fallback, ['ACI_USE_PROXY'])), + use_ssl=dict(type='bool', default=True, fallback=(env_fallback, ['ACI_USE_SSL'])), + validate_certs=dict(type='bool', default=True, fallback=(env_fallback, ['ACI_VALIDATE_CERTS'])), + output_path=dict(type='str', fallback=(env_fallback, ['ACI_OUTPUT_PATH'])), + annotation=dict(type='str', fallback=(env_fallback, ['ACI_ANNOTATION'])), + owner_key=dict(type='str', fallback=(env_fallback, ['ACI_OWNER_KEY'])), + owner_tag=dict(type='str', fallback=(env_fallback, ['ACI_OWNER_TAG'])), + ) + + +class ACIModule(object): + + def __init__(self, module): + self.module = module + self.params = module.params + self.result = dict(changed=False) + self.headers = dict() + self.child_classes = set() + + # error output + self.error = dict(code=None, text=None) + + # normal output + self.existing = None + + # info output + self.config = dict() + self.original = None + self.proposed = dict() + self.stdout = None + + # debug output + self.filter_string = '' + self.obj_filter = None + self.method = None + self.path = None + self.response = None + self.status = None + self.url = None + + # aci_rest output + self.imdata = None + self.totalCount = None + + # Ensure protocol is set + self.define_protocol() + + if self.module._debug: + self.module.warn('Enable debug output because ANSIBLE_DEBUG was set.') + self.params['output_level'] = 'debug' + + if self.params.get('private_key'): + # Perform signature-based authentication, no need to log on separately + if not HAS_OPENSSL: + self.module.fail_json(msg='Cannot use signature-based authentication because pyopenssl is not available') + elif self.params.get('password') is not None: + self.module.warn("When doing ACI signatured-based authentication, providing parameter 'password' is not required") + elif self.params.get('password'): + # Perform password-based authentication, log on using password + self.login() + else: + self.module.fail_json(msg="Either parameter 'password' or 'private_key' is required for authentication") + + def boolean(self, value, true='yes', false='no'): + ''' Return an acceptable value back ''' + + # When we expect value is of type=bool + if value is None: + return None + elif value is True: + return true + elif value is False: + return false + + # If all else fails, escalate back to user + self.module.fail_json(msg="Boolean value '%s' is an invalid ACI boolean value.") + + def iso8601_format(self, dt): + ''' Return an ACI-compatible ISO8601 formatted time: 2123-12-12T00:00:00.000+00:00 ''' + try: + return dt.isoformat(timespec='milliseconds') + except Exception: + tz = dt.strftime('%z') + return '%s.%03d%s:%s' % (dt.strftime('%Y-%m-%dT%H:%M:%S'), dt.microsecond / 1000, tz[:3], tz[3:]) + + def define_protocol(self): + ''' Set protocol based on use_ssl parameter ''' + + # Set protocol for further use + self.params['protocol'] = 'https' if self.params.get('use_ssl', True) else 'http' + + def define_method(self): + ''' Set method based on state parameter ''' + + # Set method for further use + state_map = dict(absent='delete', present='post', query='get') + self.params['method'] = state_map.get(self.params.get('state')) + + def login(self): + ''' Log in to APIC ''' + + # Perform login request + if self.params.get('port') is not None: + url = '%(protocol)s://%(host)s:%(port)s/api/aaaLogin.json' % self.params + else: + url = '%(protocol)s://%(host)s/api/aaaLogin.json' % self.params + payload = {'aaaUser': {'attributes': {'name': self.params.get('username'), 'pwd': self.params.get('password')}}} + resp, auth = fetch_url(self.module, url, + data=json.dumps(payload), + method='POST', + timeout=self.params.get('timeout'), + use_proxy=self.params.get('use_proxy')) + + # Handle APIC response + if auth.get('status') != 200: + self.response = auth.get('msg') + self.status = auth.get('status') + try: + # APIC error + self.response_json(auth['body']) + self.fail_json(msg='Authentication failed: %(code)s %(text)s' % self.error) + except KeyError: + # Connection error + self.fail_json(msg='Connection failed for %(url)s. %(msg)s' % auth) + + # Retain cookie for later use + self.headers['Cookie'] = resp.headers.get('Set-Cookie') + + def cert_auth(self, path=None, payload='', method=None): + ''' Perform APIC signature-based authentication, not the expected SSL client certificate authentication. ''' + + if method is None: + method = self.params.get('method').upper() + + # NOTE: ACI documentation incorrectly uses complete URL + if path is None: + path = self.path + path = '/' + path.lstrip('/') + + if payload is None: + payload = '' + + # Check if we got a private key. This allows the use of vaulting the private key. + try: + sig_key = load_privatekey(FILETYPE_PEM, self.params.get('private_key')) + except Exception: + if os.path.exists(self.params.get('private_key')): + try: + with open(self.params.get('private_key'), 'r') as fh: + private_key_content = fh.read() + except Exception: + self.module.fail_json(msg="Cannot open private key file '%(private_key)s'." % self.params) + try: + sig_key = load_privatekey(FILETYPE_PEM, private_key_content) + except Exception: + self.module.fail_json(msg="Cannot load private key file '%(private_key)s'." % self.params) + if self.params.get('certificate_name') is None: + self.params['certificate_name'] = os.path.basename(os.path.splitext(self.params.get('private_key'))[0]) + else: + self.module.fail_json(msg="Provided private key '%(private_key)s' does not appear to be a private key." % self.params) + + if self.params.get('certificate_name') is None: + self.params['certificate_name'] = self.params.get('username') + # NOTE: ACI documentation incorrectly adds a space between method and path + sig_request = method + path + payload + sig_signature = base64.b64encode(sign(sig_key, sig_request, 'sha256')) + sig_dn = 'uni/userext/user-%(username)s/usercert-%(certificate_name)s' % self.params + self.headers['Cookie'] = 'APIC-Certificate-Algorithm=v1.0; ' +\ + 'APIC-Certificate-DN=%s; ' % sig_dn +\ + 'APIC-Certificate-Fingerprint=fingerprint; ' +\ + 'APIC-Request-Signature=%s' % to_native(sig_signature) + + def response_json(self, rawoutput): + ''' Handle APIC JSON response output ''' + try: + jsondata = json.loads(rawoutput) + except Exception as e: + # Expose RAW output for troubleshooting + self.error = dict(code=-1, text="Unable to parse output as JSON, see 'raw' output. %s" % e) + self.result['raw'] = rawoutput + return + + # Extract JSON API output + self.imdata = jsondata.get('imdata') + if self.imdata is None: + self.imdata = dict() + self.totalCount = int(jsondata.get('totalCount')) + + # Handle possible APIC error information + self.response_error() + + def response_xml(self, rawoutput): + ''' Handle APIC XML response output ''' + + # NOTE: The XML-to-JSON conversion is using the "Cobra" convention + try: + xml = lxml.etree.fromstring(to_bytes(rawoutput)) + xmldata = cobra.data(xml) + except Exception as e: + # Expose RAW output for troubleshooting + self.error = dict(code=-1, text="Unable to parse output as XML, see 'raw' output. %s" % e) + self.result['raw'] = rawoutput + return + + # Reformat as ACI does for JSON API output + self.imdata = xmldata.get('imdata', {}).get('children') + if self.imdata is None: + self.imdata = dict() + self.totalCount = int(xmldata.get('imdata', {}).get('attributes', {}).get('totalCount')) + + # Handle possible APIC error information + self.response_error() + + def response_error(self): + ''' Set error information when found ''' + + # Handle possible APIC error information + if self.totalCount != '0': + try: + self.error = self.imdata[0].get('error').get('attributes') + except (AttributeError, IndexError, KeyError): + pass + + def request(self, path, payload=None): + ''' Perform a REST request ''' + + # Ensure method is set (only do this once) + self.define_method() + self.path = path + + if self.params.get('port') is not None: + self.url = '%(protocol)s://%(host)s:%(port)s/' % self.params + path.lstrip('/') + else: + self.url = '%(protocol)s://%(host)s/' % self.params + path.lstrip('/') + + # Sign and encode request as to APIC's wishes + if self.params.get('private_key'): + self.cert_auth(path=path, payload=payload) + + # Perform request + resp, info = fetch_url(self.module, self.url, + data=payload, + headers=self.headers, + method=self.params.get('method').upper(), + timeout=self.params.get('timeout'), + use_proxy=self.params.get('use_proxy')) + + self.response = info.get('msg') + self.status = info.get('status') + + # Handle APIC response + if info.get('status') != 200: + try: + # APIC error + self.response_json(info['body']) + self.fail_json(msg='APIC Error %(code)s: %(text)s' % self.error) + except KeyError: + # Connection error + self.fail_json(msg='Connection failed for %(url)s. %(msg)s' % info) + + self.response_json(resp.read()) + + def query(self, path): + ''' Perform a query with no payload ''' + + self.path = path + + if self.params.get('port') is not None: + self.url = '%(protocol)s://%(host)s:%(port)s/' % self.params + path.lstrip('/') + else: + self.url = '%(protocol)s://%(host)s/' % self.params + path.lstrip('/') + + # Sign and encode request as to APIC's wishes + if self.params.get('private_key'): + self.cert_auth(path=path, method='GET') + + # Perform request + resp, query = fetch_url(self.module, self.url, + data=None, + headers=self.headers, + method='GET', + timeout=self.params.get('timeout'), + use_proxy=self.params.get('use_proxy')) + + # Handle APIC response + if query.get('status') != 200: + self.response = query.get('msg') + self.status = query.get('status') + try: + # APIC error + self.response_json(query['body']) + self.fail_json(msg='APIC Error %(code)s: %(text)s' % self.error) + except KeyError: + # Connection error + self.fail_json(msg='Connection failed for %(url)s. %(msg)s' % query) + + query = json.loads(resp.read()) + + return json.dumps(query.get('imdata'), sort_keys=True, indent=2) + '\n' + + def request_diff(self, path, payload=None): + ''' Perform a request, including a proper diff output ''' + self.result['diff'] = dict() + self.result['diff']['before'] = self.query(path) + self.request(path, payload=payload) + # TODO: Check if we can use the request output for the 'after' diff + self.result['diff']['after'] = self.query(path) + + if self.result.get('diff', {}).get('before') != self.result.get('diff', {}).get('after'): + self.result['changed'] = True + + # TODO: This could be designed to update existing keys + def update_qs(self, params): + ''' Append key-value pairs to self.filter_string ''' + accepted_params = dict((k, v) for (k, v) in params.items() if v is not None) + if accepted_params: + if self.filter_string: + self.filter_string += '&' + else: + self.filter_string = '?' + self.filter_string += '&'.join(['%s=%s' % (k, v) for (k, v) in accepted_params.items()]) + + # TODO: This could be designed to accept multiple obj_classes and keys + def build_filter(self, obj_class, params): + ''' Build an APIC filter based on obj_class and key-value pairs ''' + accepted_params = dict((k, v) for (k, v) in params.items() if v is not None) + if len(accepted_params) == 1: + return ','.join('eq({0}.{1},"{2}")'.format(obj_class, k, v) for (k, v) in accepted_params.items()) + elif len(accepted_params) > 1: + return 'and(' + ','.join(['eq({0}.{1},"{2}")'.format(obj_class, k, v) for (k, v) in accepted_params.items()]) + ')' + + def _deep_url_path_builder(self, obj): + target_class = obj.get('target_class') + target_filter = obj.get('target_filter') + subtree_class = obj.get('subtree_class') + subtree_filter = obj.get('subtree_filter') + object_rn = obj.get('object_rn') + mo = obj.get('module_object') + add_subtree_filter = obj.get('add_subtree_filter') + add_target_filter = obj.get('add_target_filter') + + if self.module.params.get('state') in ('absent', 'present') and mo is not None: + self.path = 'api/mo/uni/{0}.json'.format(object_rn) + self.update_qs({'rsp-prop-include': 'config-only'}) + + else: + # State is 'query' + if object_rn is not None: + # Query for a specific object in the module's class + self.path = 'api/mo/uni/{0}.json'.format(object_rn) + else: + self.path = 'api/class/{0}.json'.format(target_class) + + if add_target_filter: + self.update_qs( + {'query-target-filter': self.build_filter(target_class, target_filter)}) + + if add_subtree_filter: + self.update_qs( + {'rsp-subtree-filter': self.build_filter(subtree_class, subtree_filter)}) + + if self.params.get('port') is not None: + self.url = '{protocol}://{host}:{port}/{path}'.format( + path=self.path, **self.module.params) + + else: + self.url = '{protocol}://{host}/{path}'.format( + path=self.path, **self.module.params) + + if self.child_classes: + self.update_qs( + {'rsp-subtree': 'full', 'rsp-subtree-class': ','.join(sorted(self.child_classes))}) + + def _deep_url_parent_object(self, parent_objects, parent_class): + + for parent_object in parent_objects: + if parent_object.get('aci_class') is parent_class: + return parent_object + + return None + + def construct_deep_url(self, target_object, parent_objects=None, child_classes=None): + """ + This method is used to retrieve the appropriate URL path and filter_string to make the request to the APIC. + + :param target_object: The target class dictionary containing parent_class, aci_class, aci_rn, target_filter, and module_object keys. + :param parent_objects: The parent class list of dictionaries containing parent_class, aci_class, aci_rn, target_filter, and module_object keys. + :param child_classes: The list of child classes that the module supports along with the object. + :type target_object: dict + :type parent_objects: list[dict] + :type child_classes: list[string] + :return: The path and filter_string needed to build the full URL. + """ + + self.filter_string = '' + rn_builder = None + subtree_classes = None + add_subtree_filter = False + add_target_filter = False + has_target_query = False + has_target_query_compare = False + has_target_query_difference = False + has_target_query_called = False + + if child_classes is None: + self.child_classes = set() + else: + self.child_classes = set(child_classes) + + target_parent_class = target_object.get('parent_class') + target_class = target_object.get('aci_class') + target_rn = target_object.get('aci_rn') + target_filter = target_object.get('target_filter') + target_module_object = target_object.get('module_object') + + url_path_object = dict( + target_class=target_class, + target_filter=target_filter, + subtree_class=target_class, + subtree_filter=target_filter, + module_object=target_module_object + ) + + if target_module_object is not None: + rn_builder = target_rn + else: + has_target_query = True + has_target_query_compare = True + + if parent_objects is not None: + current_parent_class = target_parent_class + has_parent_query_compare = False + has_parent_query_difference = False + is_first_parent = True + is_single_parent = None + search_classes = set() + + while current_parent_class != 'uni': + parent_object = self._deep_url_parent_object( + parent_objects=parent_objects, parent_class=current_parent_class) + + if parent_object is not None: + parent_parent_class = parent_object.get('parent_class') + parent_class = parent_object.get('aci_class') + parent_rn = parent_object.get('aci_rn') + parent_filter = parent_object.get('target_filter') + parent_module_object = parent_object.get('module_object') + + if is_first_parent: + is_single_parent = True + else: + is_single_parent = False + is_first_parent = False + + if parent_parent_class != 'uni': + search_classes.add(parent_class) + + if parent_module_object is not None: + if rn_builder is not None: + rn_builder = '{0}/{1}'.format(parent_rn, rn_builder) + else: + rn_builder = parent_rn + + url_path_object['target_class'] = parent_class + url_path_object['target_filter'] = parent_filter + + has_target_query = False + else: + rn_builder = None + subtree_classes = search_classes + + has_target_query = True + if is_single_parent: + has_parent_query_compare = True + + current_parent_class = parent_parent_class + else: + raise ValueError("Reference error for parent_class '{0}'. Each parent_class must reference a valid object".format(current_parent_class)) + + if not has_target_query_difference and not has_target_query_called: + if has_target_query is not has_target_query_compare: + has_target_query_difference = True + else: + if not has_parent_query_difference and has_target_query is not has_parent_query_compare: + has_parent_query_difference = True + has_target_query_called = True + + if not has_parent_query_difference and has_parent_query_compare and target_module_object is not None: + add_target_filter = True + + elif has_parent_query_difference and target_module_object is not None: + add_subtree_filter = True + self.child_classes.add(target_class) + + if has_target_query: + add_target_filter = True + + elif has_parent_query_difference and not has_target_query and target_module_object is None: + self.child_classes.add(target_class) + self.child_classes.update(subtree_classes) + + elif not has_parent_query_difference and not has_target_query and target_module_object is None: + self.child_classes.add(target_class) + + elif not has_target_query and is_single_parent and target_module_object is None: + self.child_classes.add(target_class) + + url_path_object['object_rn'] = rn_builder + url_path_object['add_subtree_filter'] = add_subtree_filter + url_path_object['add_target_filter'] = add_target_filter + + self._deep_url_path_builder(url_path_object) + + def construct_url( + self, root_class, subclass_1=None, subclass_2=None, subclass_3=None, + subclass_4=None, subclass_5=None, child_classes=None, config_only=True): + + """ + This method is used to retrieve the appropriate URL path and filter_string to make the request to the APIC. + + :param root_class: The top-level class dictionary containing aci_class, aci_rn, target_filter, and module_object keys. + :param sublass_1: The second-level class dictionary containing aci_class, aci_rn, target_filter, and module_object keys. + :param sublass_2: The third-level class dictionary containing aci_class, aci_rn, target_filter, and module_object keys. + :param sublass_3: The fourth-level class dictionary containing aci_class, aci_rn, target_filter, and module_object keys. + :param child_classes: The list of child classes that the module supports along with the object. + :type root_class: dict + :type subclass_1: dict + :type subclass_2: dict + :type subclass_3: dict + :type subclass_4: dict + :type subclass_5: dict + :type child_classes: list + :return: The path and filter_string needed to build the full URL. + """ + self.filter_string = '' + + if child_classes is None: + self.child_classes = set() + else: + self.child_classes = set(child_classes) + + if subclass_5 is not None: + self._construct_url_6(root_class, subclass_1, subclass_2, subclass_3, subclass_4, subclass_5, config_only) + elif subclass_4 is not None: + self._construct_url_5(root_class, subclass_1, subclass_2, subclass_3, subclass_4, config_only) + elif subclass_3 is not None: + self._construct_url_4(root_class, subclass_1, subclass_2, subclass_3, config_only) + elif subclass_2 is not None: + self._construct_url_3(root_class, subclass_1, subclass_2, config_only) + elif subclass_1 is not None: + self._construct_url_2(root_class, subclass_1, config_only) + else: + self._construct_url_1(root_class, config_only) + + if self.params.get('port') is not None: + self.url = '{protocol}://{host}:{port}/{path}'.format(path=self.path, **self.module.params) + else: + self.url = '{protocol}://{host}/{path}'.format(path=self.path, **self.module.params) + + if self.child_classes: + # Append child_classes to filter_string if filter string is empty + self.update_qs({'rsp-subtree': 'full', 'rsp-subtree-class': ','.join(sorted(self.child_classes))}) + + def _construct_url_1(self, obj, config_only=True): + """ + This method is used by construct_url when the object is the top-level class. + """ + obj_class = obj.get('aci_class') + obj_rn = obj.get('aci_rn') + obj_filter = obj.get('target_filter') + mo = obj.get('module_object') + + if self.module.params.get('state') in ('absent', 'present'): + # State is absent or present + self.path = 'api/mo/uni/{0}.json'.format(obj_rn) + if config_only: + self.update_qs({'rsp-prop-include': 'config-only'}) + self.obj_filter = obj_filter + elif mo is None: + # Query for all objects of the module's class (filter by properties) + self.path = 'api/class/{0}.json'.format(obj_class) + if obj_filter is not None: + self.update_qs({'query-target-filter': self.build_filter(obj_class, obj_filter)}) + else: + # Query for a specific object in the module's class + self.path = 'api/mo/uni/{0}.json'.format(obj_rn) + + def _construct_url_2(self, parent, obj, config_only=True): + """ + This method is used by construct_url when the object is the second-level class. + """ + parent_rn = parent.get('aci_rn') + parent_obj = parent.get('module_object') + obj_class = obj.get('aci_class') + obj_rn = obj.get('aci_rn') + obj_filter = obj.get('target_filter') + mo = obj.get('module_object') + + if self.module.params.get('state') in ('absent', 'present'): + # State is absent or present + self.path = 'api/mo/uni/{0}/{1}.json'.format(parent_rn, obj_rn) + if config_only: + self.update_qs({'rsp-prop-include': 'config-only'}) + self.obj_filter = obj_filter + elif parent_obj is None and mo is None: + # Query for all objects of the module's class + self.path = 'api/class/{0}.json'.format(obj_class) + self.update_qs({'query-target-filter': self.build_filter(obj_class, obj_filter)}) + elif parent_obj is None: # mo is known + # Query for all objects of the module's class that match the provided ID value + self.path = 'api/class/{0}.json'.format(obj_class) + self.update_qs({'query-target-filter': self.build_filter(obj_class, obj_filter)}) + elif mo is None: # parent_obj is known + # Query for all object's of the module's class that belong to a specific parent object + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}.json'.format(parent_rn) + else: + # Query for specific object in the module's class + self.path = 'api/mo/uni/{0}/{1}.json'.format(parent_rn, obj_rn) + + def _construct_url_3(self, root, parent, obj, config_only=True): + """ + This method is used by construct_url when the object is the third-level class. + """ + root_rn = root.get('aci_rn') + root_obj = root.get('module_object') + parent_class = parent.get('aci_class') + parent_rn = parent.get('aci_rn') + parent_filter = parent.get('target_filter') + parent_obj = parent.get('module_object') + obj_class = obj.get('aci_class') + obj_rn = obj.get('aci_rn') + obj_filter = obj.get('target_filter') + mo = obj.get('module_object') + + if self.module.params.get('state') in ('absent', 'present'): + # State is absent or present + self.path = 'api/mo/uni/{0}/{1}/{2}.json'.format(root_rn, parent_rn, obj_rn) + if config_only: + self.update_qs({'rsp-prop-include': 'config-only'}) + self.obj_filter = obj_filter + elif root_obj is None and parent_obj is None and mo is None: + # Query for all objects of the module's class + self.path = 'api/class/{0}.json'.format(obj_class) + self.update_qs({'query-target-filter': self.build_filter(obj_class, obj_filter)}) + elif root_obj is None and parent_obj is None: # mo is known + # Query for all objects of the module's class matching the provided ID value of the object + self.path = 'api/class/{0}.json'.format(obj_class) + self.update_qs({'query-target-filter': self.build_filter(obj_class, obj_filter)}) + elif root_obj is None and mo is None: # parent_obj is known + # Query for all objects of the module's class that belong to any parent class + # matching the provided ID value for the parent object + self.child_classes.add(obj_class) + self.path = 'api/class/{0}.json'.format(parent_class) + self.update_qs({'query-target-filter': self.build_filter(parent_class, parent_filter)}) + elif parent_obj is None and mo is None: # root_obj is known + # Query for all objects of the module's class that belong to a specific root object + self.child_classes.update([parent_class, obj_class]) + self.path = 'api/mo/uni/{0}.json'.format(root_rn) + # NOTE: No need to select by root_filter + # self.update_qs({'query-target-filter': self.build_filter(root_class, root_filter)}) + elif root_obj is None: # mo and parent_obj are known + # Query for all objects of the module's class that belong to any parent class + # matching the provided ID values for both object and parent object + self.child_classes.add(obj_class) + self.path = 'api/class/{0}.json'.format(parent_class) + self.update_qs({'query-target-filter': self.build_filter(parent_class, parent_filter)}) + self.update_qs({'rsp-subtree-filter': self.build_filter(obj_class, obj_filter)}) + elif parent_obj is None: # mo and root_obj are known + # Query for all objects of the module's class that match the provided ID value and belong to a specific root object + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}.json'.format(root_rn) + # NOTE: No need to select by root_filter + # self.update_qs({'query-target-filter': self.build_filter(root_class, root_filter)}) + # TODO: Filter by parent_filter and obj_filter + self.update_qs({'rsp-subtree-filter': self.build_filter(obj_class, obj_filter)}) + elif mo is None: # root_obj and parent_obj are known + # Query for all objects of the module's class that belong to a specific parent object + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}/{1}.json'.format(root_rn, parent_rn) + # NOTE: No need to select by parent_filter + # self.update_qs({'query-target-filter': self.build_filter(parent_class, parent_filter)}) + else: + # Query for a specific object of the module's class + self.path = 'api/mo/uni/{0}/{1}/{2}.json'.format(root_rn, parent_rn, obj_rn) + + def _construct_url_4(self, root, sec, parent, obj, config_only=True): + """ + This method is used by construct_url when the object is the fourth-level class. + """ + root_rn = root.get('aci_rn') + root_obj = root.get('module_object') + sec_rn = sec.get('aci_rn') + sec_obj = sec.get('module_object') + parent_rn = parent.get('aci_rn') + parent_obj = parent.get('module_object') + obj_class = obj.get('aci_class') + obj_rn = obj.get('aci_rn') + obj_filter = obj.get('target_filter') + mo = obj.get('module_object') + + if self.child_classes is None: + self.child_classes = [obj_class] + + if self.module.params.get('state') in ('absent', 'present'): + # State is absent or present + self.path = 'api/mo/uni/{0}/{1}/{2}/{3}.json'.format(root_rn, sec_rn, parent_rn, obj_rn) + if config_only: + self.update_qs({'rsp-prop-include': 'config-only'}) + self.obj_filter = obj_filter + # TODO: Add all missing cases + elif root_obj is None: + self.child_classes.add(obj_class) + self.path = 'api/class/{0}.json'.format(obj_class) + self.update_qs({'query-target-filter': self.build_filter(obj_class, obj_filter)}) + elif sec_obj is None: + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}.json'.format(root_rn) + # NOTE: No need to select by root_filter + # self.update_qs({'query-target-filter': self.build_filter(root_class, root_filter)}) + # TODO: Filter by sec_filter, parent and obj_filter + self.update_qs({'rsp-subtree-filter': self.build_filter(obj_class, obj_filter)}) + elif parent_obj is None: + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}/{1}.json'.format(root_rn, sec_rn) + # NOTE: No need to select by sec_filter + # self.update_qs({'query-target-filter': self.build_filter(sec_class, sec_filter)}) + # TODO: Filter by parent_filter and obj_filter + self.update_qs({'rsp-subtree-filter': self.build_filter(obj_class, obj_filter)}) + elif mo is None: + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}/{1}/{2}.json'.format(root_rn, sec_rn, parent_rn) + # NOTE: No need to select by parent_filter + # self.update_qs({'query-target-filter': self.build_filter(parent_class, parent_filter)}) + else: + # Query for a specific object of the module's class + self.path = 'api/mo/uni/{0}/{1}/{2}/{3}.json'.format(root_rn, sec_rn, parent_rn, obj_rn) + + def _construct_url_5(self, root, ter, sec, parent, obj, config_only=True): + """ + This method is used by construct_url when the object is the fourth-level class. + """ + + root_rn = root.get('aci_rn') + root_obj = root.get('module_object') + ter_rn = ter.get('aci_rn') + ter_obj = ter.get('module_object') + sec_rn = sec.get('aci_rn') + sec_obj = sec.get('module_object') + parent_rn = parent.get('aci_rn') + parent_obj = parent.get('module_object') + obj_class = obj.get('aci_class') + obj_rn = obj.get('aci_rn') + obj_filter = obj.get('target_filter') + mo = obj.get('module_object') + + if self.child_classes is None: + self.child_classes = [obj_class] + + if self.module.params.get('state') in ('absent', 'present'): + # State is absent or present + self.path = 'api/mo/uni/{0}/{1}/{2}/{3}{4}.json'.format(root_rn, ter_rn, sec_rn, parent_rn, obj_rn) + if config_only: + self.update_qs({'rsp-prop-include': 'config-only'}) + self.obj_filter = obj_filter + # TODO: Add all missing cases + elif root_obj is None: + self.child_classes.add(obj_class) + self.path = 'api/class/{0}.json'.format(obj_class) + self.update_qs({'query-target-filter': self.build_filter(obj_class, obj_filter)}) + elif ter_obj is None: + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}.json'.format(root_rn) + # NOTE: No need to select by root_filter + # self.update_qs({'query-target-filter': self.build_filter(root_class, root_filter)}) + # TODO: Filter by ter_filter, parent and obj_filter + self.update_qs({'rsp-subtree-filter': self.build_filter(obj_class, obj_filter)}) + elif sec_obj is None: + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}{1}.json'.format(root_rn, ter_rn) + # NOTE: No need to select by ter_filter + # self.update_qs({'query-target-filter': self.build_filter(ter_class, ter_filter)}) + # TODO: Filter by sec_filter, parent and obj_filter + self.update_qs({'rsp-subtree-filter': self.build_filter(obj_class, obj_filter)}) + elif parent_obj is None: + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}/{1}{2}.json'.format(root_rn, ter_rn, sec_rn) + # NOTE: No need to select by sec_filter + # self.update_qs({'query-target-filter': self.build_filter(sec_class, sec_filter)}) + # TODO: Filter by parent_filter and obj_filter + self.update_qs({'rsp-subtree-filter': self.build_filter(obj_class, obj_filter)}) + elif mo is None: + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}/{1}/{2}{3}.json'.format(root_rn, ter_rn, sec_rn, parent_rn) + # NOTE: No need to select by parent_filter + # self.update_qs({'query-target-filter': self.build_filter(parent_class, parent_filter)}) + else: + # Query for a specific object of the module's class + self.path = 'api/mo/uni/{0}/{1}/{2}/{3}{4}.json'.format(root_rn, ter_rn, sec_rn, parent_rn, obj_rn) + + def _construct_url_6(self, root, quad, ter, sec, parent, obj, config_only=True): + """ + This method is used by construct_url when the object is the fourth-level class. + """ + root_rn = root.get('aci_rn') + root_obj = root.get('module_object') + quad_rn = quad.get('aci_rn') + quad_obj = quad.get('module_object') + ter_rn = ter.get('aci_rn') + ter_obj = ter.get('module_object') + sec_rn = sec.get('aci_rn') + sec_obj = sec.get('module_object') + parent_rn = parent.get('aci_rn') + parent_obj = parent.get('module_object') + obj_class = obj.get('aci_class') + obj_rn = obj.get('aci_rn') + obj_filter = obj.get('target_filter') + mo = obj.get('module_object') + + if self.child_classes is None: + self.child_classes = [obj_class] + + if self.module.params.get('state') in ('absent', 'present'): + # State is absent or present + self.path = 'api/mo/uni/{0}/{1}/{2}/{3}{4}{5}.json'.format(root_rn, quad_rn, ter_rn, sec_rn, parent_rn, obj_rn) + if config_only: + self.update_qs({'rsp-prop-include': 'config-only'}) + self.obj_filter = obj_filter + # TODO: Add all missing cases + elif root_obj is None: + self.child_classes.add(obj_class) + self.path = 'api/class/{0}.json'.format(obj_class) + self.update_qs({'query-target-filter': self.build_filter(obj_class, obj_filter)}) + elif quad_obj is None: + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}.json'.format(root_rn) + # NOTE: No need to select by root_filter + # self.update_qs({'query-target-filter': self.build_filter(root_class, root_filter)}) + # TODO: Filter by quad_filter, parent and obj_filter + self.update_qs({'rsp-subtree-filter': self.build_filter(obj_class, obj_filter)}) + elif ter_obj is None: + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}{1}.json'.format(root_rn, quad_rn) + # NOTE: No need to select by quad_filter + # self.update_qs({'query-target-filter': self.build_filter(quad_class, quad_filter)}) + # TODO: Filter by ter_filter, parent and obj_filter + self.update_qs({'rsp-subtree-filter': self.build_filter(obj_class, obj_filter)}) + elif sec_obj is None: + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}{1}{2}.json'.format(root_rn, quad_rn, ter_rn) + # NOTE: No need to select by ter_filter + # self.update_qs({'query-target-filter': self.build_filter(ter_class, ter_filter)}) + # TODO: Filter by sec_filter, parent and obj_filter + self.update_qs({'rsp-subtree-filter': self.build_filter(obj_class, obj_filter)}) + elif parent_obj is None: + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}/{1}{2}{3}.json'.format(root_rn, quad_rn, ter_rn, sec_rn) + # NOTE: No need to select by sec_filter + # self.update_qs({'query-target-filter': self.build_filter(sec_class, sec_filter)}) + # TODO: Filter by parent_filter and obj_filter + self.update_qs({'rsp-subtree-filter': self.build_filter(obj_class, obj_filter)}) + elif mo is None: + self.child_classes.add(obj_class) + self.path = 'api/mo/uni/{0}/{1}/{2}{3}{4}.json'.format(root_rn, quad_rn, ter_rn, sec_rn, parent_rn) + # NOTE: No need to select by parent_filter + # self.update_qs({'query-target-filter': self.build_filter(parent_class, parent_filter)}) + else: + # Query for a specific object of the module's class + self.path = 'api/mo/uni/{0}/{1}/{2}/{3}{4}{5}.json'.format(root_rn, quad_rn, ter_rn, sec_rn, parent_rn, obj_rn) + + def delete_config(self): + """ + This method is used to handle the logic when the modules state is equal to absent. The method only pushes a change if + the object exists, and if check_mode is False. A successful change will mark the module as changed. + """ + self.proposed = dict() + + if not self.existing: + return + + elif not self.module.check_mode: + # Sign and encode request as to APIC's wishes + if self.params['private_key']: + self.cert_auth(method='DELETE') + + resp, info = fetch_url(self.module, self.url, + headers=self.headers, + method='DELETE', + timeout=self.params.get('timeout'), + use_proxy=self.params.get('use_proxy')) + + self.response = info.get('msg') + self.status = info.get('status') + self.method = 'DELETE' + + # Handle APIC response + if info.get('status') == 200: + self.result['changed'] = True + self.response_json(resp.read()) + else: + try: + # APIC error + self.response_json(info['body']) + self.fail_json(msg='APIC Error %(code)s: %(text)s' % self.error) + except KeyError: + # Connection error + self.fail_json(msg='Connection failed for %(url)s. %(msg)s' % info) + else: + self.result['changed'] = True + self.method = 'DELETE' + + def get_diff(self, aci_class): + """ + This method is used to get the difference between the proposed and existing configurations. Each module + should call the get_existing method before this method, and add the proposed config to the module results + using the module's config parameters. The new config will added to the self.result dictionary. + + :param aci_class: Type str. + This is the root dictionary key for the MO's configuration body, or the ACI class of the MO. + """ + proposed_config = self.proposed[aci_class]['attributes'] + if self.existing: + existing_config = self.existing[0][aci_class]['attributes'] + config = {} + + # values are strings, so any diff between proposed and existing can be a straight replace + for key, value in proposed_config.items(): + existing_field = existing_config.get(key) + if value != existing_field: + config[key] = value + + # add name back to config only if the configs do not match + if config: + # TODO: If URLs are built with the object's name, then we should be able to leave off adding the name back + # config['name'] = proposed_config.get('name') + config = {aci_class: {'attributes': config}} + + # check for updates to child configs and update new config dictionary + children = self.get_diff_children(aci_class) + if children and config: + config[aci_class].update({'children': children}) + elif children: + config = {aci_class: {'attributes': {}, 'children': children}} + + else: + config = self.proposed + + self.config = config + + @staticmethod + def get_diff_child(child_class, proposed_child, existing_child): + """ + This method is used to get the difference between a proposed and existing child configs. The get_nested_config() + method should be used to return the proposed and existing config portions of child. + + :param child_class: Type str. + The root class (dict key) for the child dictionary. + :param proposed_child: Type dict. + The config portion of the proposed child dictionary. + :param existing_child: Type dict. + The config portion of the existing child dictionary. + :return: The child config with only values that are updated. If the proposed dictionary has no updates to make + to what exists on the APIC, then None is returned. + """ + update_config = {child_class: {'attributes': {}}} + for key, value in proposed_child.items(): + existing_field = existing_child.get(key) + if value != existing_field: + update_config[child_class]['attributes'][key] = value + + if not update_config[child_class]['attributes']: + return None + + return update_config + + def get_diff_children(self, aci_class): + """ + This method is used to retrieve the updated child configs by comparing the proposed children configs + agains the objects existing children configs. + + :param aci_class: Type str. + This is the root dictionary key for the MO's configuration body, or the ACI class of the MO. + :return: The list of updated child config dictionaries. None is returned if there are no changes to the child + configurations. + """ + proposed_children = self.proposed[aci_class].get('children') + if proposed_children: + child_updates = [] + existing_children = self.existing[0][aci_class].get('children', []) + + # Loop through proposed child configs and compare against existing child configuration + for child in proposed_children: + child_class, proposed_child, existing_child = self.get_nested_config(child, existing_children) + + if existing_child is None: + child_update = child + else: + child_update = self.get_diff_child(child_class, proposed_child, existing_child) + + # Update list of updated child configs only if the child config is different than what exists + if child_update: + child_updates.append(child_update) + else: + return None + + return child_updates + + def get_existing(self): + """ + This method is used to get the existing object(s) based on the path specified in the module. Each module should + build the URL so that if the object's name is supplied, then it will retrieve the configuration for that particular + object, but if no name is supplied, then it will retrieve all MOs for the class. Following this method will ensure + that this method can be used to supply the existing configuration when using the get_diff method. The response, status, + and existing configuration will be added to the self.result dictionary. + """ + uri = self.url + self.filter_string + + # Sign and encode request as to APIC's wishes + if self.params.get('private_key'): + self.cert_auth(path=self.path + self.filter_string, method='GET') + + resp, info = fetch_url(self.module, uri, + headers=self.headers, + method='GET', + timeout=self.params.get('timeout'), + use_proxy=self.params.get('use_proxy')) + self.response = info.get('msg') + self.status = info.get('status') + self.method = 'GET' + + # Handle APIC response + if info.get('status') == 200: + self.existing = json.loads(resp.read())['imdata'] + else: + try: + # APIC error + self.response_json(info['body']) + self.fail_json(msg='APIC Error %(code)s: %(text)s' % self.error) + except KeyError: + # Connection error + self.fail_json(msg='Connection failed for %(url)s. %(msg)s' % info) + + @staticmethod + def get_nested_config(proposed_child, existing_children): + """ + This method is used for stiping off the outer layers of the child dictionaries so only the configuration + key, value pairs are returned. + + :param proposed_child: Type dict. + The dictionary that represents the child config. + :param existing_children: Type list. + The list of existing child config dictionaries. + :return: The child's class as str (root config dict key), the child's proposed config dict, and the child's + existing configuration dict. + """ + for key in proposed_child.keys(): + child_class = key + proposed_config = proposed_child[key]['attributes'] + existing_config = None + + # FIXME: Design causes issues for repeated child_classes + # get existing dictionary from the list of existing to use for comparison + for child in existing_children: + if child.get(child_class): + existing_config = child[key]['attributes'] + # NOTE: This is an ugly fix + # Return the one that is a subset match + if set(proposed_config.items()).issubset(set(existing_config.items())): + break + existing_config = None + + return child_class, proposed_config, existing_config + + def payload(self, aci_class, class_config, child_configs=None): + """ + This method is used to dynamically build the proposed configuration dictionary from the config related parameters + passed into the module. All values that were not passed values from the playbook task will be removed so as to not + inadvertently change configurations. + + :param aci_class: Type str + This is the root dictionary key for the MO's configuration body, or the ACI class of the MO. + :param class_config: Type dict + This is the configuration of the MO using the dictionary keys expected by the API + :param child_configs: Type list + This is a list of child dictionaries associated with the MOs config. The list should only + include child objects that are used to associate two MOs together. Children that represent + MOs should have their own module. + """ + proposed = dict((k, str(v)) for k, v in class_config.items() if v is not None) + if self.params.get('annotation') is not None: + proposed['annotation'] = self.params.get('annotation') + if self.params.get('owner_key') is not None: + proposed['ownerKey'] = self.params.get('owner_key') + if self.params.get('owner_tag') is not None: + proposed['ownerTag'] = self.params.get('owner_tag') + self.proposed = {aci_class: {'attributes': proposed}} + + # add child objects to proposed + if child_configs: + children = [] + for child in child_configs: + child_copy = deepcopy(child) + has_value = False + for root_key in child_copy.keys(): + for final_keys, values in child_copy[root_key]['attributes'].items(): + if values is None: + child[root_key]['attributes'].pop(final_keys) + else: + child[root_key]['attributes'][final_keys] = str(values) + has_value = True + if has_value: + children.append(child) + + if children: + self.proposed[aci_class].update(dict(children=children)) + + def post_config(self): + """ + This method is used to handle the logic when the modules state is equal to present. The method only pushes a change if + the object has differences than what exists on the APIC, and if check_mode is False. A successful change will mark the + module as changed. + """ + if not self.config: + return + elif not self.module.check_mode: + # Sign and encode request as to APIC's wishes + if self.params.get('private_key'): + self.cert_auth(method='POST', payload=json.dumps(self.config)) + + resp, info = fetch_url(self.module, self.url, + data=json.dumps(self.config), + headers=self.headers, + method='POST', + timeout=self.params.get('timeout'), + use_proxy=self.params.get('use_proxy')) + + self.response = info.get('msg') + self.status = info.get('status') + self.method = 'POST' + + # Handle APIC response + if info.get('status') == 200: + self.result['changed'] = True + self.response_json(resp.read()) + else: + try: + # APIC error + self.response_json(info['body']) + self.fail_json(msg='APIC Error %(code)s: %(text)s' % self.error) + except KeyError: + # Connection error + self.fail_json(msg='Connection failed for %(url)s. %(msg)s' % info) + else: + self.result['changed'] = True + self.method = 'POST' + + def exit_json(self, **kwargs): + + if 'state' in self.params: + if self.params.get('state') in ('absent', 'present'): + if self.params.get('output_level') in ('debug', 'info'): + self.result['previous'] = self.existing + self.dump_json() + + # Return the gory details when we need it + if self.params.get('output_level') == 'debug': + if 'state' in self.params: + self.result['filter_string'] = self.filter_string + self.result['method'] = self.method + # self.result['path'] = self.path # Adding 'path' in result causes state: absent in output + self.result['response'] = self.response + self.result['status'] = self.status + self.result['url'] = self.url + if self.stdout: + self.result['stdout'] = self.stdout + + if 'state' in self.params: + self.original = self.existing + if self.params.get('state') in ('absent', 'present'): + self.get_existing() + + # if self.module._diff and self.original != self.existing: + # self.result['diff'] = dict( + # before=json.dumps(self.original, sort_keys=True, indent=4), + # after=json.dumps(self.existing, sort_keys=True, indent=4), + # ) + self.result['current'] = self.existing + + if self.params.get('output_level') in ('debug', 'info'): + self.result['sent'] = self.config + self.result['proposed'] = self.proposed + + self.result.update(**kwargs) + self.module.exit_json(**self.result) + + def fail_json(self, msg, **kwargs): + + # Return error information, if we have it + if self.error.get('code') is not None and self.error.get('text') is not None: + self.result['error'] = self.error + + if 'state' in self.params: + if self.params.get('state') in ('absent', 'present'): + if self.params.get('output_level') in ('debug', 'info'): + self.result['previous'] = self.existing + if self.stdout: + self.result['stdout'] = self.stdout + + # Return the gory details when we need it + if self.params.get('output_level') == 'debug': + if self.imdata is not None: + self.result['imdata'] = self.imdata + self.result['totalCount'] = self.totalCount + + if self.params.get('output_level') == 'debug': + if self.url is not None: + if 'state' in self.params: + self.result['filter_string'] = self.filter_string + self.result['method'] = self.method + # self.result['path'] = self.path # Adding 'path' in result causes state: absent in output + self.result['response'] = self.response + self.result['status'] = self.status + self.result['url'] = self.url + + if 'state' in self.params: + if self.params.get('output_level') in ('debug', 'info'): + self.result['sent'] = self.config + self.result['proposed'] = self.proposed + + self.result.update(**kwargs) + self.module.fail_json(msg=msg, **self.result) + + def dump_json(self): + if self.params.get('state') in ('absent', 'present'): + dn_path = (self.url).split('/mo/')[-1] + if dn_path[-5:] == '.json': + dn_path = dn_path[:-5] + mo = {} + if(self.proposed): + mo = self.proposed + for aci_class in mo: + mo[aci_class]['attributes']['dn'] = dn_path + if self.obj_filter is not None: + if 'tDn' in self.obj_filter: + mo[aci_class]['attributes']['tDn'] = self.obj_filter['tDn'] + + elif(self.params.get('state') == 'absent' and self.existing): + for aci_class in self.existing[0]: + mo[aci_class] = dict(attributes=dict(dn=dn_path, status="deleted")) + + self.result['mo'] = mo + output_path = self.params.get('output_path') + if(output_path is not None): + with open(output_path, "a") as output_file: + json.dump([mo], output_file) diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/__init__.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user.py new file mode 100644 index 00000000..a951ad4f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user.py @@ -0,0 +1,367 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_aaa_user +short_description: Manage AAA users (aaa:User) +description: +- Manage AAA users on Cisco ACI fabrics. +requirements: +- python-dateutil +options: + aaa_password: + description: + - The password of the locally-authenticated user. + type: str + aaa_password_lifetime: + description: + - The lifetime of the locally-authenticated user password. + type: int + aaa_password_update_required: + description: + - Whether this account needs password update. + type: bool + aaa_user: + description: + - The name of the locally-authenticated user user to add. + type: str + aliases: [ name, user ] + clear_password_history: + description: + - Whether to clear the password history of a locally-authenticated user. + type: bool + description: + description: + - Description for the AAA user. + type: str + aliases: [ descr ] + email: + description: + - The email address of the locally-authenticated user. + type: str + enabled: + description: + - The status of the locally-authenticated user account. + type: bool + expiration: + description: + - The expiration date of the locally-authenticated user account. + type: str + expires: + description: + - Whether to enable an expiration date for the locally-authenticated user account. + type: bool + first_name: + description: + - The first name of the locally-authenticated user. + type: str + last_name: + description: + - The last name of the locally-authenticated user. + type: str + phone: + description: + - The phone number of the locally-authenticated user. + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- This module is not idempotent when C(aaa_password) is being used + (even if that password was already set identically). This + appears to be an inconsistency wrt. the idempotent nature + of the APIC REST API. The vendor has been informed. + More information in :ref:`the ACI documentation <aci_guide_known_issues>`. +seealso: +- module: cisco.aci.aci_aaa_user_certificate +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(aaa:User). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Add a user + cisco.aci.aci_aaa_user: + host: apic + username: admin + password: SomeSecretPassword + aaa_user: dag + aaa_password: AnotherSecretPassword + expiration: never + expires: no + email: dag@wieers.com + phone: 1-234-555-678 + first_name: Dag + last_name: Wieers + state: present + delegate_to: localhost + +- name: Remove a user + cisco.aci.aci_aaa_user: + host: apic + username: admin + password: SomeSecretPassword + aaa_user: dag + state: absent + delegate_to: localhost + +- name: Query a user + cisco.aci.aci_aaa_user: + host: apic + username: admin + password: SomeSecretPassword + aaa_user: dag + state: query + delegate_to: localhost + register: query_result + +- name: Query all users + cisco.aci.aci_aaa_user: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: '?rsp-prop-include=config-only' +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +try: + from dateutil.tz import tzutc + import dateutil.parser + HAS_DATEUTIL = True +except ImportError: + HAS_DATEUTIL = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + aaa_password=dict(type='str', no_log=True), + aaa_password_lifetime=dict(type='int'), + aaa_password_update_required=dict(type='bool'), + aaa_user=dict(type='str', aliases=['name']), # Not required for querying all objects + clear_password_history=dict(type='bool'), + description=dict(type='str', aliases=['descr']), + email=dict(type='str'), + enabled=dict(type='bool'), + expiration=dict(type='str'), + expires=dict(type='bool'), + first_name=dict(type='str'), + last_name=dict(type='str'), + phone=dict(type='str'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['aaa_user']], + ['state', 'present', ['aaa_user']], + ['expires', True, ['expiration']], + ], + ) + + aci = ACIModule(module) + + if not HAS_DATEUTIL: + module.fail_json(msg='dateutil required for this module') + + aaa_password = module.params.get('aaa_password') + aaa_password_lifetime = module.params.get('aaa_password_lifetime') + aaa_password_update_required = aci.boolean(module.params.get('aaa_password_update_required')) + aaa_user = module.params.get('aaa_user') + clear_password_history = aci.boolean(module.params.get('clear_password_history'), 'yes', 'no') + description = module.params.get('description') + email = module.params.get('email') + enabled = aci.boolean(module.params.get('enabled'), 'active', 'inactive') + expires = aci.boolean(module.params.get('expires')) + first_name = module.params.get('first_name') + last_name = module.params.get('last_name') + phone = module.params.get('phone') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + expiration = module.params.get('expiration') + if expiration is not None and expiration != 'never': + try: + expiration = aci.iso8601_format(dateutil.parser.parse(expiration).replace(tzinfo=tzutc())) + except Exception as e: + module.fail_json(msg="Failed to parse date format '%s', %s" % (module.params.get('expiration'), e)) + + aci.construct_url( + root_class=dict( + aci_class='aaaUser', + aci_rn='userext/user-{0}'.format(aaa_user), + module_object=aaa_user, + target_filter={'name': aaa_user}, + ), + ) + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='aaaUser', + class_config=dict( + accountStatus=enabled, + clearPwdHistory=clear_password_history, + descr=description, + email=email, + expiration=expiration, + expires=expires, + firstName=first_name, + lastName=last_name, + name=aaa_user, + phone=phone, + pwd=aaa_password, + pwdLifeTime=aaa_password_lifetime, + pwdUpdateRequired=aaa_password_update_required, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='aaaUser') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_certificate.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_certificate.py new file mode 100644 index 00000000..fc15e56f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_certificate.py @@ -0,0 +1,298 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_aaa_user_certificate +short_description: Manage AAA user certificates (aaa:UserCert) +description: +- Manage AAA user certificates on Cisco ACI fabrics. +options: + aaa_user: + description: + - The name of the user to add a certificate to. + type: str + required: yes + aaa_user_type: + description: + - Whether this is a normal user or an appuser. + type: str + choices: [ appuser, user ] + default: user + certificate: + description: + - The PEM format public key extracted from the X.509 certificate. + type: str + aliases: [ cert_data, certificate_data ] + certificate_name: + description: + - The name of the user certificate entry in ACI. + type: str + aliases: [ cert_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(aaa_user) must exist before using this module in your playbook. + The M(cisco.aci.aci_aaa_user) module can be used for this. +seealso: +- module: cisco.aci.aci_aaa_user +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(aaa:UserCert). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Add a certificate to user + cisco.aci.aci_aaa_user_certificate: + host: apic + username: admin + password: SomeSecretPassword + aaa_user: admin + certificate_name: admin + certificate_data: '{{ lookup("file", "pki/admin.crt") }}' + state: present + delegate_to: localhost + +- name: Remove a certificate of a user + cisco.aci.aci_aaa_user_certificate: + host: apic + username: admin + password: SomeSecretPassword + aaa_user: admin + certificate_name: admin + state: absent + delegate_to: localhost + +- name: Query a certificate of a user + cisco.aci.aci_aaa_user_certificate: + host: apic + username: admin + password: SomeSecretPassword + aaa_user: admin + certificate_name: admin + state: query + delegate_to: localhost + register: query_result + +- name: Query all certificates of a user + cisco.aci.aci_aaa_user_certificate: + host: apic + username: admin + password: SomeSecretPassword + aaa_user: admin + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +ACI_MAPPING = dict( + appuser=dict( + aci_class='aaaAppUser', + aci_mo='userext/appuser-', + ), + user=dict( + aci_class='aaaUser', + aci_mo='userext/user-', + ), +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + aaa_user=dict(type='str', required=True), + aaa_user_type=dict(type='str', default='user', choices=['appuser', 'user']), + certificate=dict(type='str', aliases=['cert_data', 'certificate_data']), + certificate_name=dict(type='str', aliases=['cert_name']), # Not required for querying all objects + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['aaa_user', 'certificate_name']], + ['state', 'present', ['aaa_user', 'certificate', 'certificate_name']], + ], + ) + + aaa_user = module.params.get('aaa_user') + aaa_user_type = module.params.get('aaa_user_type') + certificate = module.params.get('certificate') + certificate_name = module.params.get('certificate_name') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class=ACI_MAPPING.get(aaa_user_type).get('aci_class'), + aci_rn=ACI_MAPPING.get(aaa_user_type).get('aci_mo') + aaa_user, + module_object=aaa_user, + target_filter={'name': aaa_user}, + ), + subclass_1=dict( + aci_class='aaaUserCert', + aci_rn='usercert-{0}'.format(certificate_name), + module_object=certificate_name, + target_filter={'name': certificate_name}, + ), + ) + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='aaaUserCert', + class_config=dict( + data=certificate, + name=certificate_name, + nameAlias=name_alias, + + ), + ) + + aci.get_diff(aci_class='aaaUserCert') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_access_port_block_to_access_port.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_access_port_block_to_access_port.py new file mode 100644 index 00000000..246c82f4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_access_port_block_to_access_port.py @@ -0,0 +1,437 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2018, Simon Metzger <smnmtzgr@gmail.com> +# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com> +# Copyright: (c) 2020, Zak Lantz (@manofcolombia) <zakodewald@gmail.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_access_port_block_to_access_port +short_description: Manage port blocks of Fabric interface policy leaf profile interface selectors (infra:HPortS, infra:PortBlk) +description: +- Manage port blocks of Fabric interface policy leaf profile interface selectors on Cisco ACI fabrics. +options: + interface_profile: + description: + - The name of the Fabric access policy leaf interface profile. + type: str + aliases: [ leaf_interface_profile_name, leaf_interface_profile, interface_profile_name ] + access_port_selector: + description: + - The name of the Fabric access policy leaf interface profile access port selector. + type: str + aliases: [ name, access_port_selector_name ] + port_blk: + description: + - The name of the Fabric access policy leaf interface profile access port block. + type: str + aliases: [ leaf_port_blk_name, leaf_port_blk ] + port_blk_description: + description: + - The description to assign to the C(leaf_port_blk). + type: str + aliases: [ leaf_port_blk_description ] + from_port: + description: + - The beginning (from-range) of the port range block for the leaf access port block. + type: str + aliases: [ from, fromPort, from_port_range ] + to_port: + description: + - The end (to-range) of the port range block for the leaf access port block. + type: str + aliases: [ to, toPort, to_port_range ] + from_card: + description: + - The beginning (from-range) of the card range block for the leaf access port block. + type: str + aliases: [ from_card_range ] + to_card: + description: + - The end (to-range) of the card range block for the leaf access port block. + type: str + aliases: [ to_card_range ] + type: + description: + - The type of access port block to be created under respective access port. + type: str + choices: [ fex, leaf ] + default: leaf + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(interface_profile) and C(access_port_selector) must exist before using this module in your playbook. + The M(cisco.aci.aci_interface_policy_leaf_profile) and M(cisco.aci.aci_access_port_to_interface_policy_leaf_profile) modules can be used for this. +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(infra:HPortS) and B(infra:PortBlk). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Simon Metzger (@smnmtzgr) +''' + +EXAMPLES = r''' +- name: Associate an access port block (single port) to an interface selector + cisco.aci.aci_access_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname + access_port_selector: accessportselectorname + port_blk: leafportblkname + from_port: 13 + to_port: 13 + state: present + delegate_to: localhost + +- name: Associate an access port block (port range) to an interface selector + cisco.aci.aci_access_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname + access_port_selector: accessportselectorname + port_blk: leafportblkname + from_port: 13 + to_port: 16 + state: present + delegate_to: localhost + +- name: Associate an access port block (single port) to an interface selector of type fex + cisco.aci.aci_access_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + type: fex + interface_profile: leafintprfname_fex + access_port_selector: accessportselectorname_fex + port_blk: leafportblkname_fex + from_port: 13 + to_port: 13 + state: present + delegate_to: localhost + +- name: Associate an access port block (port range) to an interface selector of type fex + cisco.aci.aci_access_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + type: fex + interface_profile: leafintprfname_fex + access_port_selector: accessportselectorname_fex + port_blk: leafportblkname_fex + from_port: 13 + to_port: 16 + state: present + delegate_to: localhost + +- name: Remove an access port block from an interface selector + cisco.aci.aci_access_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname + access_port_selector: accessportselectorname + port_blk: leafportblkname + from_port: 13 + to_port: 13 + state: absent + delegate_to: localhost + +- name: Remove an access port block from an interface selector of type fex + cisco.aci.aci_access_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + type: fex + interface_profile: leafintprfname_fex + access_port_selector: accessportselectorname_fex + port_blk: leafportblkname_fex + from_port: 13 + to_port: 13 + state: absent + delegate_to: localhost + +- name: Query Specific access port block under given access port selector + cisco.aci.aci_access_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname + access_port_selector: accessportselectorname + port_blk: leafportblkname + state: query + delegate_to: localhost + register: query_result + +- name: Query Specific access port block under given access port selector of type fex + cisco.aci.aci_access_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + type: fex + interface_profile: leafintprfname_fex + access_port_selector: accessportselectorname_fex + port_blk: leafportblkname_fex + state: query + delegate_to: localhost + register: query_result + +- name: Query all access port blocks under given leaf interface profile + cisco.aci.aci_access_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname + state: query + delegate_to: localhost + register: query_result + +- name: Query all access port blocks under given leaf interface profile of type fex + cisco.aci.aci_access_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + type: fex + interface_profile: leafintprfname_fex + state: query + delegate_to: localhost + register: query_result + +- name: Query all access port blocks in the fabric + cisco.aci.aci_access_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result + +- name: Query all access port blocks in the fabric of type fex + cisco.aci.aci_access_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + type: fex + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + interface_profile=dict(type='str', aliases=['leaf_interface_profile_name', 'leaf_interface_profile', 'interface_profile_name']), + access_port_selector=dict(type='str', aliases=['name', 'access_port_selector_name']), # Not required for querying all objects + port_blk=dict(type='str', aliases=['leaf_port_blk_name', 'leaf_port_blk']), # Not required for querying all objects + port_blk_description=dict(type='str', aliases=['leaf_port_blk_description']), + from_port=dict(type='str', aliases=['from', 'fromPort', 'from_port_range']), + to_port=dict(type='str', aliases=['to', 'toPort', 'to_port_range']), + from_card=dict(type='str', aliases=['from_card_range']), + to_card=dict(type='str', aliases=['to_card_range']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + type=dict(type='str', default='leaf', choices=['fex', 'leaf']), # This parameter is not required for querying all objects + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['access_port_selector', 'port_blk', 'interface_profile']], + ['state', 'present', ['access_port_selector', 'port_blk', 'from_port', 'to_port', 'interface_profile']], + ], + ) + + interface_profile = module.params.get('interface_profile') + access_port_selector = module.params.get('access_port_selector') + port_blk = module.params.get('port_blk') + port_blk_description = module.params.get('port_blk_description') + from_port = module.params.get('from_port') + to_port = module.params.get('to_port') + from_card = module.params.get('from_card') + to_card = module.params.get('to_card') + state = module.params.get('state') + type_port = module.params.get('type') + + aci = ACIModule(module) + aci_class = 'infraAccPortP' + aci_rn = 'accportprof' + if type_port == 'fex': + aci_class = 'infraFexP' + aci_rn = 'fexprof' + aci.construct_url( + root_class=dict( + aci_class=aci_class, + aci_rn='infra/' + aci_rn + '-{0}'.format(interface_profile), + module_object=interface_profile, + target_filter={'name': interface_profile}, + ), + subclass_1=dict( + aci_class='infraHPortS', + # NOTE: normal rn: hports-{name}-typ-{type}, hence here hardcoded to range for purposes of module + aci_rn='hports-{0}-typ-range'.format(access_port_selector), + module_object=access_port_selector, + target_filter={'name': access_port_selector}, + ), + subclass_2=dict( + aci_class='infraPortBlk', + aci_rn='portblk-{0}'.format(port_blk), + module_object=port_blk, + target_filter={'name': port_blk}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='infraPortBlk', + class_config=dict( + descr=port_blk_description, + name=port_blk, + fromPort=from_port, + toPort=to_port, + fromCard=from_card, + toCard=to_card, + # type='range', + ), + ) + + aci.get_diff(aci_class='infraPortBlk') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_access_port_to_interface_policy_leaf_profile.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_access_port_to_interface_policy_leaf_profile.py new file mode 100644 index 00000000..f419c8b9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_access_port_to_interface_policy_leaf_profile.py @@ -0,0 +1,429 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> +# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_access_port_to_interface_policy_leaf_profile +short_description: Manage Fabric interface policy leaf profile interface selectors (infra:HPortS, infra:RsAccBaseGrp, infra:PortBlk) +description: +- Manage Fabric interface policy leaf profile interface selectors on Cisco ACI fabrics. +options: + interface_profile: + description: + - The name of the Fabric access policy leaf interface profile. + type: str + aliases: [ leaf_interface_profile_name, leaf_interface_profile, interface_profile_name ] + access_port_selector: + description: + - The name of the Fabric access policy leaf interface profile access port selector. + type: str + aliases: [ name, access_port_selector_name ] + description: + description: + - The description to assign to the C(access_port_selector) + type: str + port_blk: + description: + - B(Deprecated) + - Starting with Ansible 2.8 we recommend using M(cisco.aci.aci_access_port_block_to_access_port) instead. + - The parameter will be removed in Ansible 2.12. + - HORIZONTALLINE + - The name of the Fabric access policy leaf interface profile access port block. + type: str + aliases: [ leaf_port_blk_name, leaf_port_blk, port_blk_name ] + leaf_port_blk_description: + description: + - B(Deprecated) + - Starting with Ansible 2.8 we recommend using M(cisco.aci.aci_access_port_block_to_access_port) instead. + - The parameter will be removed in Ansible 2.12. + - HORIZONTALLINE + - The description to assign to the C(leaf_port_blk) + type: str + from_port: + description: + - B(Deprecated) + - Starting with Ansible 2.8 we recommend using M(cisco.aci.aci_access_port_block_to_access_port) instead. + - The parameter will be removed in Ansible 2.12. + - HORIZONTALLINE + - The beginning (from-range) of the port range block for the leaf access port block. + type: str + aliases: [ from, fromPort, from_port_range ] + to_port: + description: + - B(Deprecated) + - Starting with Ansible 2.8 we recommend using M(cisco.aci.aci_access_port_block_to_access_port) instead. + - The parameter will be removed in Ansible 2.12. + - HORIZONTALLINE + - The end (to-range) of the port range block for the leaf access port block. + type: str + aliases: [ to, toPort, to_port_range ] + from_card: + description: + - B(Deprecated) + - Starting with Ansible 2.8 we recommend using M(cisco.aci.aci_access_port_block_to_access_port) instead. + - The parameter will be removed in Ansible 2.12. + - HORIZONTALLINE + - The beginning (from-range) of the card range block for the leaf access port block. + type: str + aliases: [ from_card_range ] + to_card: + description: + - B(Deprecated) + - Starting with Ansible 2.8 we recommend using M(cisco.aci.aci_access_port_block_to_access_port) instead. + - The parameter will be removed in Ansible 2.12. + - HORIZONTALLINE + - The end (to-range) of the card range block for the leaf access port block. + type: str + aliases: [ to_card_range ] + policy_group: + description: + - The name of the fabric access policy group to be associated with the leaf interface profile interface selector. + type: str + aliases: [ policy_group_name ] + interface_type: + description: + - The type of interface for the static EPG deployment. + type: str + choices: [ breakout, fex, port_channel, switch_port, vpc ] + default: switch_port + type: + description: + - The type of access port to be created under respective profile. + type: str + choices: [ fex, leaf ] + default: leaf + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(interface_profile) must exist before using this module in your playbook. + The M(cisco.aci.aci_interface_policy_leaf_profile) modules can be used for this. +seealso: +- module: cisco.aci.aci_access_port_block_to_access_port +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(infra:HPortS), B(infra:RsAccBaseGrp) and B(infra:PortBlk). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Bruno Calogero (@brunocalogero) +- Shreyas Srish (@shrsr) +''' + +EXAMPLES = r''' +- name: Associate an Interface Access Port Selector to an Interface Policy Leaf Profile with a Policy Group + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname + access_port_selector: accessportselectorname + port_blk: leafportblkname + from_port: 13 + to_port: 16 + policy_group: policygroupname + state: present + delegate_to: localhost + +- name: Associate an interface access port selector to an Interface Policy Leaf Profile (w/o policy group) (check if this works) + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname + access_port_selector: accessportselectorname + port_blk: leafportblkname + from_port: 13 + to_port: 16 + state: present + delegate_to: localhost + +- name: Remove an interface access port selector associated with an Interface Policy Leaf Profile + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname + access_port_selector: accessportselectorname + state: absent + delegate_to: localhost + +- name: Remove an interface access port selector associated with an Interface Policy Fex Profile + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: fexintprfname + access_port_selector: accessportselectorname + state: absent + delegate_to: localhost + +- name: Query Specific access_port_selector under given leaf_interface_profile + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname + access_port_selector: accessportselectorname + state: query + delegate_to: localhost + register: query_result + +- name: Query Specific access_port_selector under given Fex leaf_interface_profile + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: fexintprfname + access_port_selector: accessportselectorname + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +INTERFACE_TYPE_MAPPING = dict( + breakout='uni/infra/funcprof/brkoutportgrp-{0}', + fex='uni/infra/funcprof/accportgrp-{0}', + port_channel='uni/infra/funcprof/accbundle-{0}', + switch_port='uni/infra/funcprof/accportgrp-{0}', + vpc='uni/infra/funcprof/accbundle-{0}', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + interface_profile=dict(type='str', aliases=['leaf_interface_profile_name', 'leaf_interface_profile', 'interface_profile_name']), + access_port_selector=dict(type='str', aliases=['name', 'access_port_selector_name']), # Not required for querying all objects + description=dict(type='str'), + port_blk=dict(type='str', aliases=['leaf_port_blk_name', 'leaf_port_blk', 'port_blk_name']), + leaf_port_blk_description=dict(type='str'), + from_port=dict(type='str', aliases=['from', 'fromPort', 'from_port_range']), + to_port=dict(type='str', aliases=['to', 'toPort', 'to_port_range']), + from_card=dict(type='str', aliases=['from_card_range']), + to_card=dict(type='str', aliases=['to_card_range']), + policy_group=dict(type='str', aliases=['policy_group_name']), + interface_type=dict(type='str', default='switch_port', choices=['breakout', 'fex', 'port_channel', 'switch_port', 'vpc']), + type=dict(type='str', default='leaf', choices=['fex', 'leaf']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['interface_profile', 'access_port_selector']], + ['state', 'present', ['interface_profile', 'access_port_selector']], + ], + ) + + interface_profile = module.params.get('interface_profile') + access_port_selector = module.params.get('access_port_selector') + description = module.params.get('description') + port_blk = module.params.get('port_blk') + leaf_port_blk_description = module.params.get('leaf_port_blk_description') + from_port = module.params.get('from_port') + to_port = module.params.get('to_port') + from_card = module.params.get('from_card') + to_card = module.params.get('to_card') + policy_group = module.params.get('policy_group') + interface_type = module.params.get('interface_type') + state = module.params.get('state') + type_profile = module.params.get('type') + + # Build child_configs dynamically + child_configs = [dict( + infraPortBlk=dict( + attributes=dict( + descr=leaf_port_blk_description, + name=port_blk, + fromPort=from_port, + toPort=to_port, + fromCard=from_card, + toCard=to_card, + ), + ), + )] + + # Add infraRsAccBaseGrp only when policy_group was defined + if policy_group is not None: + child_configs.append(dict( + infraRsAccBaseGrp=dict( + attributes=dict( + tDn=INTERFACE_TYPE_MAPPING[interface_type].format(policy_group), + ), + ), + )) + + aci = ACIModule(module) + aci_class = 'infraAccPortP' + aci_rn = 'accportprof' + if type_profile == 'fex': + aci_class = 'infraFexP' + aci_rn = 'fexprof' + aci.construct_url( + root_class=dict( + aci_class=aci_class, + aci_rn='infra/' + aci_rn + '-{0}'.format(interface_profile), + module_object=interface_profile, + target_filter={'name': interface_profile}, + ), + subclass_1=dict( + aci_class='infraHPortS', + # NOTE: normal rn: hports-{name}-typ-{type}, hence here hardcoded to range for purposes of module + aci_rn='hports-{0}-typ-range'.format(access_port_selector), + module_object=access_port_selector, + target_filter={'name': access_port_selector}, + ), + child_classes=['infraPortBlk', 'infraRsAccBaseGrp'], + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='infraHPortS', + class_config=dict( + descr=description, + name=access_port_selector, + # type='range', + ), + child_configs=child_configs, + ) + + aci.get_diff(aci_class='infraHPortS') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_access_sub_port_block_to_access_port.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_access_sub_port_block_to_access_port.py new file mode 100644 index 00000000..7cbfdeab --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_access_sub_port_block_to_access_port.py @@ -0,0 +1,364 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2019, Simon Metzger <smnmtzgr@gmail.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_access_sub_port_block_to_access_port +short_description: Manage sub port blocks of Fabric interface policy leaf profile interface selectors (infra:HPortS, infra:SubPortBlk) +description: +- Manage sub port blocks of Fabric interface policy leaf profile interface selectors on Cisco ACI fabrics. +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(infra:HPortS) and B(infra:SubPortBlk). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Simon Metzger (@smnmtzgr) +options: + leaf_interface_profile: + description: + - The name of the Fabric access policy leaf interface profile. + type: str + aliases: [ leaf_interface_profile_name ] + access_port_selector: + description: + - The name of the Fabric access policy leaf interface profile access port selector. + type: str + aliases: [ name, access_port_selector_name ] + leaf_port_blk: + description: + - The name of the Fabric access policy leaf interface profile access port block. + type: str + aliases: [ leaf_port_blk_name ] + leaf_port_blk_description: + description: + - The description to assign to the C(leaf_port_blk). + type: str + from_port: + description: + - The beginning (from-range) of the port range block for the leaf access port block. + type: str + aliases: [ from, fromPort, from_port_range ] + to_port: + description: + - The end (to-range) of the port range block for the leaf access port block. + type: str + aliases: [ to, toPort, to_port_range ] + from_sub_port: + description: + - The beginning (from-range) of the sub port range block for the leaf access port block. + type: str + aliases: [ fromSubPort, from_sub_port_range ] + to_sub_port: + description: + - The end (to-range) of the sub port range block for the leaf access port block. + type: str + aliases: [ toSubPort, to_sub_port_range ] + from_card: + description: + - The beginning (from-range) of the card range block for the leaf access port block. + type: str + aliases: [ from_card_range ] + to_card: + description: + - The end (to-range) of the card range block for the leaf access port block. + type: str + aliases: [ to_card_range ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci + +''' + +EXAMPLES = r''' +- name: Associate an access sub port block (single port) to an interface selector + cisco.aci.aci_access_sub_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + leaf_interface_profile: leafintprfname + access_port_selector: accessportselectorname + leaf_port_blk: leafportblkname + from_port: 13 + to_port: 13 + from_sub_port: 1 + to_sub_port: 1 + state: present + delegate_to: localhost + +- name: Associate an access sub port block (port range) to an interface selector + cisco.aci.aci_access_sub_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + leaf_interface_profile: leafintprfname + access_port_selector: accessportselectorname + leaf_port_blk: leafportblkname + from_port: 13 + to_port: 13 + from_sub_port: 1 + to_sub_port: 3 + state: present + delegate_to: localhost + +- name: Remove an access sub port block from an interface selector + cisco.aci.aci_access_sub_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + leaf_interface_profile: leafintprfname + access_port_selector: accessportselectorname + leaf_port_blk: leafportblkname + from_port: 13 + to_port: 13 + from_sub_port: 1 + to_sub_port: 1 + state: absent + delegate_to: localhost + +- name: Query Specific access sub port block under given access port selector + cisco.aci.aci_access_sub_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + leaf_interface_profile: leafintprfname + access_port_selector: accessportselectorname + leaf_port_blk: leafportblkname + state: query + delegate_to: localhost + register: query_result + +- name: Query all access sub port blocks under given leaf interface profile + cisco.aci.aci_access_sub_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + leaf_interface_profile: leafintprfname + state: query + delegate_to: localhost + register: query_result + +- name: Query all access sub port blocks in the fabric + cisco.aci.aci_access_sub_port_block_to_access_port: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + leaf_interface_profile=dict(type='str', aliases=['leaf_interface_profile_name']), # Not required for querying all objects + access_port_selector=dict(type='str', aliases=['name', 'access_port_selector_name']), # Not required for querying all objects + leaf_port_blk=dict(type='str', aliases=['leaf_port_blk_name']), # Not required for querying all objects + leaf_port_blk_description=dict(type='str'), + from_port=dict(type='str', aliases=['from', 'fromPort', 'from_port_range']), # Not required for querying all objects and deleting sub port blocks + to_port=dict(type='str', aliases=['to', 'toPort', 'to_port_range']), # Not required for querying all objects and deleting sub port blocks + from_sub_port=dict(type='str', aliases=['fromSubPort', 'from_sub_port_range']), # Not required for querying all objects and deleting sub port blocks + to_sub_port=dict(type='str', aliases=['toSubPort', 'to_sub_port_range']), # Not required for querying all objects and deleting sub port blocks + from_card=dict(type='str', aliases=['from_card_range']), + to_card=dict(type='str', aliases=['to_card_range']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['access_port_selector', 'leaf_port_blk', 'leaf_interface_profile']], + ['state', 'present', ['access_port_selector', 'leaf_port_blk', 'from_port', 'to_port', 'from_sub_port', 'to_sub_port', 'leaf_interface_profile']], + ], + ) + + leaf_interface_profile = module.params.get('leaf_interface_profile') + access_port_selector = module.params.get('access_port_selector') + leaf_port_blk = module.params.get('leaf_port_blk') + leaf_port_blk_description = module.params.get('leaf_port_blk_description') + from_port = module.params.get('from_port') + to_port = module.params.get('to_port') + from_sub_port = module.params.get('from_sub_port') + to_sub_port = module.params.get('to_sub_port') + from_card = module.params.get('from_card') + to_card = module.params.get('to_card') + state = module.params.get('state') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='infraAccPortP', + aci_rn='infra/accportprof-{0}'.format(leaf_interface_profile), + module_object=leaf_interface_profile, + target_filter={'name': leaf_interface_profile}, + ), + subclass_1=dict( + aci_class='infraHPortS', + # NOTE: normal rn: hports-{name}-typ-{type}, hence here hardcoded to range for purposes of module + aci_rn='hports-{0}-typ-range'.format(access_port_selector), + module_object=access_port_selector, + target_filter={'name': access_port_selector}, + ), + subclass_2=dict( + aci_class='infraSubPortBlk', + aci_rn='subportblk-{0}'.format(leaf_port_blk), + module_object=leaf_port_blk, + target_filter={'name': leaf_port_blk}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='infraSubPortBlk', + class_config=dict( + descr=leaf_port_blk_description, + name=leaf_port_blk, + fromPort=from_port, + toPort=to_port, + fromSubPort=from_sub_port, + toSubPort=to_sub_port, + fromCard=from_card, + toCard=to_card, + # type='range', + ), + ) + + aci.get_diff(aci_class='infraSubPortBlk') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_aep.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_aep.py new file mode 100644 index 00000000..29db95a5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_aep.py @@ -0,0 +1,286 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_aep +short_description: Manage attachable Access Entity Profile (AEP) objects (infra:AttEntityP, infra:ProvAcc) +description: +- Connect to external virtual and physical domains by using + attachable Access Entity Profiles (AEP) on Cisco ACI fabrics. +options: + aep: + description: + - The name of the Attachable Access Entity Profile. + type: str + aliases: [ aep_name, name ] + description: + description: + - Description for the AEP. + type: str + aliases: [ descr ] + infra_vlan: + description: + - Enable infrastructure VLAN. + - The hypervisor functions of the AEP. + - C(no) will disable the infrastructure vlan if it is enabled. + type: bool + aliases: [ infrastructure_vlan ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + default: present + choices: [ absent, present, query ] + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- module: cisco.aci.aci_aep_to_domain +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(infra:AttEntityP) and B(infra:ProvAcc). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Swetha Chunduri (@schunduri) +- Shreyas Srish (@shrsr) +''' + +EXAMPLES = r''' +- name: Add a new AEP + cisco.aci.aci_aep: + host: apic + username: admin + password: SomeSecretPassword + aep: ACI-AEP + description: default + infra_vlan: true + state: present + delegate_to: localhost + +- name: Remove an existing AEP + cisco.aci.aci_aep: + host: apic + username: admin + password: SomeSecretPassword + aep: ACI-AEP + state: absent + delegate_to: localhost + +- name: Query all AEPs + cisco.aci.aci_aep: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result + +- name: Query a specific AEP + cisco.aci.aci_aep: + host: apic + username: admin + password: SomeSecretPassword + aep: ACI-AEP + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + aep=dict(type='str', aliases=['name', 'aep_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + infra_vlan=dict(type='bool', aliases=['infrastructure_vlan']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['aep']], + ['state', 'present', ['aep']], + ], + ) + + aep = module.params.get('aep') + description = module.params.get('description') + infra_vlan = module.params.get('infra_vlan') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + if infra_vlan: + child_configs = [dict(infraProvAcc=dict(attributes=dict(name='provacc')))] + elif infra_vlan is False: + child_configs = [dict(infraProvAcc=dict(attributes=dict(name='provacc', status='deleted')))] + else: + child_configs = [] + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='infraAttEntityP', + aci_rn='infra/attentp-{0}'.format(aep), + module_object=aep, + target_filter={'name': aep}, + ), + child_classes=['infraProvAcc'] + ) + + aci.get_existing() + + try: + if len(aci.existing[0]['infraAttEntityP']) == 1 and infra_vlan is False: + child_configs = [] + except Exception: + pass + + if state == 'present': + + aci.payload( + aci_class='infraAttEntityP', + class_config=dict( + name=aep, + descr=description, + nameAlias=name_alias, + ), + child_configs=child_configs, + ) + + aci.get_diff(aci_class='infraAttEntityP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_aep_to_domain.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_aep_to_domain.py new file mode 100644 index 00000000..716906d0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_aep_to_domain.py @@ -0,0 +1,313 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Dag Wieers <dag@wieers.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_aep_to_domain +short_description: Bind AEPs to Physical or Virtual Domains (infra:RsDomP) +description: +- Bind AEPs to Physical or Virtual Domains on Cisco ACI fabrics. +options: + aep: + description: + - The name of the Attachable Access Entity Profile. + type: str + aliases: [ aep_name ] + domain: + description: + - Name of the physical or virtual domain being associated with the AEP. + type: str + aliases: [ domain_name, domain_profile ] + domain_type: + description: + - Determines if the Domain is physical (phys) or virtual (vmm). + type: str + choices: [ fc, l2dom, l3dom, phys, vmm ] + aliases: [ type ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + vm_provider: + description: + - The VM platform for VMM Domains. + - Support for Kubernetes was added in ACI v3.0. + - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1. + type: str + choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ] +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(aep) and C(domain) parameters should exist before using this module. + The M(cisco.aci.aci_aep) and M(cisco.aci.aci_domain) can be used for these. +seealso: +- module: cisco.aci.aci_aep +- module: cisco.aci.aci_domain +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(infra:RsDomP). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Add AEP to domain binding + cisco.aci.aci_aep_to_domain: &binding_present + host: apic + username: admin + password: SomeSecretPassword + aep: test_aep + domain: phys_dom + domain_type: phys + state: present + delegate_to: localhost + +- name: Remove AEP to domain binding + cisco.aci.aci_aep_to_domain: &binding_absent + host: apic + username: admin + password: SomeSecretPassword + aep: test_aep + domain: phys_dom + domain_type: phys + state: absent + delegate_to: localhost + +- name: Query our AEP to domain binding + cisco.aci.aci_aep_to_domain: + host: apic + username: admin + password: SomeSecretPassword + aep: test_aep + domain: phys_dom + domain_type: phys + state: query + delegate_to: localhost + register: query_result + +- name: Query all AEP to domain bindings + cisco.aci.aci_aep_to_domain: &binding_query + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +VM_PROVIDER_MAPPING = dict( + cloudfoundry='CloudFoundry', + kubernetes='Kubernetes', + microsoft='Microsoft', + openshift='OpenShift', + openstack='OpenStack', + redhat='Redhat', + vmware='VMware', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + aep=dict(type='str', aliases=['aep_name']), # Not required for querying all objects + domain=dict(type='str', aliases=['domain_name', 'domain_profile']), # Not required for querying all objects + domain_type=dict(type='str', choices=['fc', 'l2dom', 'l3dom', 'phys', 'vmm'], aliases=['type']), # Not required for querying all objects + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + vm_provider=dict(type='str', choices=['cloudfoundry', 'kubernetes', 'microsoft', 'openshift', 'openstack', 'redhat', 'vmware']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['domain_type', 'vmm', ['vm_provider']], + ['state', 'absent', ['aep', 'domain', 'domain_type']], + ['state', 'present', ['aep', 'domain', 'domain_type']], + ], + required_together=[ + ['domain', 'domain_type'], + ], + ) + + aep = module.params.get('aep') + domain = module.params.get('domain') + domain_type = module.params.get('domain_type') + vm_provider = module.params.get('vm_provider') + state = module.params.get('state') + + # Report when vm_provider is set when type is not virtual + if domain_type != 'vmm' and vm_provider is not None: + module.fail_json(msg="Domain type '{0}' cannot have a 'vm_provider'".format(domain_type)) + + # Compile the full domain for URL building + if domain_type == 'fc': + domain_mo = 'uni/fc-{0}'.format(domain) + elif domain_type == 'l2dom': + domain_mo = 'uni/l2dom-{0}'.format(domain) + elif domain_type == 'l3dom': + domain_mo = 'uni/l3dom-{0}'.format(domain) + elif domain_type == 'phys': + domain_mo = 'uni/phys-{0}'.format(domain) + elif domain_type == 'vmm': + domain_mo = 'uni/vmmp-{0}/dom-{1}'.format(VM_PROVIDER_MAPPING[vm_provider], domain) + else: + domain_mo = None + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='infraAttEntityP', + aci_rn='infra/attentp-{0}'.format(aep), + module_object=aep, + target_filter={'name': aep}, + ), + subclass_1=dict( + aci_class='infraRsDomP', + aci_rn='rsdomP-[{0}]'.format(domain_mo), + module_object=domain_mo, + target_filter={'tDn': domain_mo}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='infraRsDomP', + class_config=dict(tDn=domain_mo), + ) + + aci.get_diff(aci_class='infraRsDomP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_ap.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_ap.py new file mode 100644 index 00000000..0c10fbd4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_ap.py @@ -0,0 +1,290 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_ap +short_description: Manage top level Application Profile (AP) objects (fv:Ap) +description: +- Manage top level Application Profile (AP) objects on Cisco ACI fabrics +options: + tenant: + description: + - The name of an existing tenant. + type: str + aliases: [ tenant_name ] + ap: + description: + - The name of the application network profile. + type: str + aliases: [ app_profile, app_profile_name, name ] + description: + description: + - Description for the AP. + type: str + aliases: [ descr ] + monitoring_policy: + description: + - The name of the monitoring policy. + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- This module does not manage EPGs, see M(cisco.aci.aci_epg) to do this. +- The used C(tenant) must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fv:Ap). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Swetha Chunduri (@schunduri) +- Shreyas Srish (@shrsr) +''' + +EXAMPLES = r''' +- name: Add a new AP + cisco.aci.aci_ap: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + ap: default + description: default ap + monitoring_policy: default + state: present + delegate_to: localhost + +- name: Remove an AP + cisco.aci.aci_ap: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + ap: default + state: absent + delegate_to: localhost + +- name: Query an AP + cisco.aci.aci_ap: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + ap: default + state: query + delegate_to: localhost + register: query_result + +- name: Query all APs + cisco.aci.aci_ap: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + ap=dict(type='str', aliases=['app_profile', 'app_profile_name', 'name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + monitoring_policy=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['tenant', 'ap']], + ['state', 'present', ['tenant', 'ap']], + ], + ) + + ap = module.params.get('ap') + description = module.params.get('description') + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + monitoring_policy = module.params.get('monitoring_policy') + + child_configs = [dict(fvRsApMonPol=dict(attributes=dict(tnMonEPGPolName=monitoring_policy)))] + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='fvAp', + aci_rn='ap-{0}'.format(ap), + module_object=ap, + target_filter={'name': ap}, + ), + child_classes=['fvRsApMonPol'], + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fvAp', + class_config=dict( + name=ap, + descr=description, + nameAlias=name_alias, + ), + child_configs=child_configs, + ) + + aci.get_diff(aci_class='fvAp') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_bd.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_bd.py new file mode 100644 index 00000000..02ba1aeb --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_bd.py @@ -0,0 +1,461 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_bd +short_description: Manage Bridge Domains (BD) objects (fv:BD) +description: +- Manages Bridge Domains (BD) on Cisco ACI fabrics. +options: + arp_flooding: + description: + - Determines if the Bridge Domain should flood ARP traffic. + - The APIC defaults to C(no) when unset during creation. + type: bool + bd: + description: + - The name of the Bridge Domain. + type: str + aliases: [ bd_name, name ] + bd_type: + description: + - The type of traffic on the Bridge Domain. + - The APIC defaults to C(ethernet) when unset during creation. + type: str + choices: [ ethernet, fc ] + description: + description: + - Description for the Bridge Domain. + type: str + enable_multicast: + description: + - Determines if PIM is enabled. + - The APIC defaults to C(no) when unset during creation. + type: bool + enable_routing: + description: + - Determines if IP forwarding should be allowed. + - The APIC defaults to C(yes) when unset during creation. + type: bool + endpoint_clear: + description: + - Clears all End Points in all Leaves when C(yes). + - The value is not reset to disabled once End Points have been cleared; that requires a second task. + - The APIC defaults to C(no) when unset during creation. + type: bool + endpoint_move_detect: + description: + - Determines if GARP should be enabled to detect when End Points move. + - The APIC defaults to C(garp) when unset during creation. + type: str + choices: [ default, garp ] + endpoint_retention_action: + description: + - Determines if the Bridge Domain should inherit or resolve the End Point Retention Policy. + - The APIC defaults to C(resolve) when unset during creation. + type: str + choices: [ inherit, resolve ] + endpoint_retention_policy: + description: + - The name of the End Point Retention Policy the Bridge Domain should use when + overriding the default End Point Retention Policy. + type: str + igmp_snoop_policy: + description: + - The name of the IGMP Snooping Policy the Bridge Domain should use when + overriding the default IGMP Snooping Policy. + type: str + ip_learning: + description: + - Determines if the Bridge Domain should learn End Point IPs. + - The APIC defaults to C(yes) when unset during creation. + type: bool + ipv6_nd_policy: + description: + - The name of the IPv6 Neighbor Discovery Policy the Bridge Domain should use when + overridding the default IPV6 ND Policy. + type: str + l2_unknown_unicast: + description: + - Determines what forwarding method to use for unknown l2 destinations. + - The APIC defaults to C(proxy) when unset during creation. + type: str + choices: [ proxy, flood ] + l3_unknown_multicast: + description: + - Determines the forwarding method to use for unknown multicast destinations. + - The APIC defaults to C(flood) when unset during creation. + type: str + choices: [ flood, opt-flood ] + limit_ip_learn: + description: + - Determines if the BD should limit IP learning to only subnets owned by the Bridge Domain. + - The APIC defaults to C(yes) when unset during creation. + type: bool + mac_address: + description: + - The MAC Address to assign to the C(bd) instead of using the default. + - The APIC defaults to C(00:22:BD:F8:19:FF) when unset during creation. + type: str + aliases: [ mac ] + multi_dest: + description: + - Determines the forwarding method for L2 multicast, broadcast, and link layer traffic. + - The APIC defaults to C(bd-flood) when unset during creation. + type: str + choices: [ bd-flood, drop, encap-flood ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str + tenant: + description: + - The name of the Tenant. + type: str + aliases: [ tenant_name ] + vrf: + description: + - The name of the VRF. + type: str + aliases: [ vrf_name ] +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fv:BD). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +EXAMPLES = r''' +- name: Add Bridge Domain + cisco.aci.aci_bd: + host: "{{ inventory_hostname }}" + username: "{{ username }}" + password: "{{ password }}" + validate_certs: no + tenant: prod + bd: web_servers + mac_address: 00:22:BD:F8:19:FE + vrf: prod_vrf + state: present + delegate_to: localhost + +- name: Add an FC Bridge Domain + cisco.aci.aci_bd: + host: "{{ inventory_hostname }}" + username: "{{ username }}" + password: "{{ password }}" + validate_certs: no + tenant: prod + bd: storage + bd_type: fc + vrf: fc_vrf + enable_routing: no + state: present + delegate_to: localhost + +- name: Modify a Bridge Domain + cisco.aci.aci_bd: + host: "{{ inventory_hostname }}" + username: "{{ username }}" + password: "{{ password }}" + validate_certs: yes + tenant: prod + bd: web_servers + arp_flooding: yes + l2_unknown_unicast: flood + state: present + delegate_to: localhost + +- name: Query All Bridge Domains + cisco.aci.aci_bd: + host: "{{ inventory_hostname }}" + username: "{{ username }}" + password: "{{ password }}" + validate_certs: yes + state: query + delegate_to: localhost + register: query_result + +- name: Query a Bridge Domain + cisco.aci.aci_bd: + host: "{{ inventory_hostname }}" + username: "{{ username }}" + password: "{{ password }}" + validate_certs: yes + tenant: prod + bd: web_servers + state: query + delegate_to: localhost + register: query_result + +- name: Delete a Bridge Domain + cisco.aci.aci_bd: + host: "{{ inventory_hostname }}" + username: "{{ username }}" + password: "{{ password }}" + validate_certs: yes + tenant: prod + bd: web_servers + state: absent + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + arp_flooding=dict(type='bool'), + bd=dict(type='str', aliases=['bd_name', 'name']), # Not required for querying all objects + bd_type=dict(type='str', choices=['ethernet', 'fc']), + description=dict(type='str'), + enable_multicast=dict(type='bool'), + enable_routing=dict(type='bool'), + endpoint_clear=dict(type='bool'), + endpoint_move_detect=dict(type='str', choices=['default', 'garp']), + endpoint_retention_action=dict(type='str', choices=['inherit', 'resolve']), + endpoint_retention_policy=dict(type='str'), + igmp_snoop_policy=dict(type='str'), + ip_learning=dict(type='bool'), + ipv6_nd_policy=dict(type='str'), + l2_unknown_unicast=dict(type='str', choices=['proxy', 'flood']), + l3_unknown_multicast=dict(type='str', choices=['flood', 'opt-flood']), + limit_ip_learn=dict(type='bool'), + mac_address=dict(type='str', aliases=['mac']), + multi_dest=dict(type='str', choices=['bd-flood', 'drop', 'encap-flood']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + vrf=dict(type='str', aliases=['vrf_name']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['bd', 'tenant']], + ['state', 'present', ['bd', 'tenant']], + ], + ) + + aci = ACIModule(module) + + arp_flooding = aci.boolean(module.params.get('arp_flooding')) + bd = module.params.get('bd') + bd_type = module.params.get('bd_type') + if bd_type == 'ethernet': + # ethernet type is represented as regular, but that is not clear to the users + bd_type = 'regular' + description = module.params.get('description') + enable_multicast = aci.boolean(module.params.get('enable_multicast')) + enable_routing = aci.boolean(module.params.get('enable_routing')) + endpoint_clear = aci.boolean(module.params.get('endpoint_clear')) + endpoint_move_detect = module.params.get('endpoint_move_detect') + if endpoint_move_detect == 'default': + # the ACI default setting is an empty string, but that is not a good input value + endpoint_move_detect = '' + endpoint_retention_action = module.params.get('endpoint_retention_action') + endpoint_retention_policy = module.params.get('endpoint_retention_policy') + igmp_snoop_policy = module.params.get('igmp_snoop_policy') + ip_learning = aci.boolean(module.params.get('ip_learning')) + ipv6_nd_policy = module.params.get('ipv6_nd_policy') + l2_unknown_unicast = module.params.get('l2_unknown_unicast') + l3_unknown_multicast = module.params.get('l3_unknown_multicast') + limit_ip_learn = aci.boolean(module.params.get('limit_ip_learn')) + mac_address = module.params.get('mac_address') + multi_dest = module.params.get('multi_dest') + state = module.params.get('state') + tenant = module.params.get('tenant') + vrf = module.params.get('vrf') + name_alias = module.params.get('name_alias') + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='fvBD', + aci_rn='BD-{0}'.format(bd), + module_object=bd, + target_filter={'name': bd}, + ), + child_classes=['fvRsCtx', 'fvRsIgmpsn', 'fvRsBDToNdP', 'fvRsBdToEpRet'], + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fvBD', + class_config=dict( + arpFlood=arp_flooding, + descr=description, + epClear=endpoint_clear, + epMoveDetectMode=endpoint_move_detect, + ipLearning=ip_learning, + limitIpLearnToSubnets=limit_ip_learn, + mac=mac_address, + mcastAllow=enable_multicast, + multiDstPktAct=multi_dest, + name=bd, + type=bd_type, + unicastRoute=enable_routing, + unkMacUcastAct=l2_unknown_unicast, + unkMcastAct=l3_unknown_multicast, + nameAlias=name_alias, + ), + child_configs=[ + {'fvRsCtx': {'attributes': {'tnFvCtxName': vrf}}}, + {'fvRsIgmpsn': {'attributes': {'tnIgmpSnoopPolName': igmp_snoop_policy}}}, + {'fvRsBDToNdP': {'attributes': {'tnNdIfPolName': ipv6_nd_policy}}}, + {'fvRsBdToEpRet': {'attributes': {'resolveAct': endpoint_retention_action, 'tnFvEpRetPolName': endpoint_retention_policy}}}, + ], + ) + + aci.get_diff(aci_class='fvBD') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_bd_dhcp_label.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_bd_dhcp_label.py new file mode 100644 index 00000000..7b226200 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_bd_dhcp_label.py @@ -0,0 +1,312 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Dag Wieers (@dagwieers) +# Copyright: (c) 2020, sig9org (@sig9org) +# 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 + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: aci_bd_dhcp_label +short_description: Manage DHCP Labels (dhcp:Lbl) +description: +- Manage DHCP Labels on Cisco ACI fabrics. +options: + bd: + description: + - The name of the Bridge Domain. + type: str + aliases: [ bd_name ] + description: + description: + - The description for the DHCP Label. + type: str + aliases: [ descr ] + dhcp_label: + description: + - The name of the DHCP Relay Label. + type: str + aliases: [ name ] + dhcp_option: + description: + - Name of the DHCP Option Policy to be associated with the DCHP Relay Policy. + This policy need to be present in the same tenant as the bridge domain. + - The DHCP option is used to supply DHCP clients with configuration parameters + such as a domain, name server, subnet, and network address. + type: str + scope: + description: + - Represents the target relay servers ownership. + type: str + choices: [ infra, tenant ] + default: infra + aliases: [ owner ] + tenant: + description: + - The name of the Tenant. + type: str + aliases: [ tenant_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: cisco.aci.aci +notes: +- A DHCP relay label contains a C(name) for the label, the C(scope), and a DHCP option policy. + The scope is the C(owner) of the relay server and the DHCP option policy supplies DHCP clients + with configuration parameters such as domain, nameserver, and subnet router addresses. +- The C(tenant) and C(bd) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module and M(cisco.aci.aci_bd) can be used for these. +seealso: +- module: cisco.aci.aci_bd +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(dhcp:Lbl). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- sig9 (@sig9org) +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Create a new DHCP Relay Label to a Bridge Domain + cisco.aci.aci_bd_dhcp_label: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + bd: database + dhcp_label: label1 + scope: infra + state: present + +- name: Query a DHCP Relay Label of a Bridge Domain + cisco.aci.aci_bd_dhcp_label: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + bd: database + dhcp_label: label1 + scope: infra + state: query + +- name: Query all DHCP Relay Labels of a Bridge Domain + cisco.aci.aci_bd_dhcp_label: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + bd: database + state: query + +- name: Remove a DHCP Relay Label for a Bridge Domain + cisco.aci.aci_bd_dhcp_label: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + bd: database + dhcp_label: label1 + scope: infra + state: absent +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + bd=dict(type='str', aliases=['bd_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + dhcp_label=dict(type='str', aliases=['name']), # Not required for querying all objects + dhcp_option=dict(type='str'), + scope=dict(type='str', default='infra', choices=['infra', 'tenant'], aliases=['owner']), # Not required for querying all objects + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['bd', 'tenant', 'dhcp_label', 'scope']], + ['state', 'present', ['bd', 'tenant', 'dhcp_label', 'scope']], + ], + ) + + tenant = module.params.get('tenant') + bd = module.params.get('bd') + description = module.params.get('description') + dhcp_label = module.params.get('dhcp_label') + dhcp_option = module.params.get('dhcp_option') + scope = module.params.get('scope') + state = module.params.get('state') + + aci = ACIModule(module) + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='fvBD', + aci_rn='BD-{0}'.format(bd), + module_object=bd, + target_filter={'name': bd}, + ), + subclass_2=dict( + aci_class='dhcpLbl', + aci_rn='dhcplbl-{0}'.format(dhcp_label), + module_object=dhcp_label, + target_filter={'name': dhcp_label}, + ), + child_classes=['dhcpRsDhcpOptionPol'], + ) + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='dhcpLbl', + class_config=dict( + descr=description, + name=dhcp_label, + owner=scope, + ), + child_configs=[ + {'dhcpRsDhcpOptionPol': {'attributes': {'tnDhcpOptionPolName': dhcp_option}}}, + ], + ) + + aci.get_diff(aci_class='dhcpLbl') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_bd_subnet.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_bd_subnet.py new file mode 100644 index 00000000..ac305922 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_bd_subnet.py @@ -0,0 +1,467 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_bd_subnet +short_description: Manage Subnets (fv:Subnet) +description: +- Manage Subnets on Cisco ACI fabrics. +options: + bd: + description: + - The name of the Bridge Domain. + type: str + aliases: [ bd_name ] + description: + description: + - The description for the Subnet. + type: str + aliases: [ descr ] + enable_vip: + description: + - Determines if the Subnet should be treated as a VIP; used when the BD is extended to multiple sites. + - The APIC defaults to C(no) when unset during creation. + type: bool + gateway: + description: + - The IPv4 or IPv6 gateway address for the Subnet. + type: str + aliases: [ gateway_ip ] + mask: + description: + - The subnet mask for the Subnet. + - This is the number associated with CIDR notation. + - For IPv4 addresses, accepted values range between C(0) and C(32). + - For IPv6 addresses, accepted Values range between C(0) and C(128). + type: int + aliases: [ subnet_mask ] + nd_prefix_policy: + description: + - The IPv6 Neighbor Discovery Prefix Policy to associate with the Subnet. + type: str + preferred: + description: + - Determines if the Subnet is preferred over all available Subnets. Only one Subnet per Address Family (IPv4/IPv6). + can be preferred in the Bridge Domain. + - The APIC defaults to C(no) when unset during creation. + type: bool + route_profile: + description: + - The Route Profile to the associate with the Subnet. + type: str + route_profile_l3_out: + description: + - The L3 Out that contains the associated Route Profile. + type: str + scope: + description: + - Determines the scope of the Subnet. + - The C(private) option only allows communication with hosts in the same VRF. + - The C(public) option allows the Subnet to be advertised outside of the ACI Fabric, and allows communication with + hosts in other VRFs. + - The shared option limits communication to hosts in either the same VRF or the shared VRF. + - The value is a list of options, C(private) and C(public) are mutually exclusive, but both can be used with C(shared). + - The APIC defaults to C(private) when unset during creation. + type: list + elements: str + choices: + - private + - public + - shared + subnet_control: + description: + - Determines the Subnet's Control State. + - The C(querier_ip) option is used to treat the gateway_ip as an IGMP querier source IP. + - The C(nd_ra) option is used to treat the gateway_ip address as a Neighbor Discovery Router Advertisement Prefix. + - The C(no_gw) option is used to remove default gateway functionality from the gateway address. + - The APIC defaults to C(nd_ra) when unset during creation. + type: str + choices: [ nd_ra, no_gw, querier_ip, unspecified ] + subnet_name: + description: + - The name of the Subnet. + type: str + aliases: [ name ] + tenant: + description: + - The name of the Tenant. + type: str + aliases: [ tenant_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(gateway) parameter is the root key used to access the Subnet (not name), so the C(gateway) + is required when the state is C(absent) or C(present). +- The C(tenant) and C(bd) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module and M(cisco.aci.aci_bd) can be used for these. +seealso: +- module: cisco.aci.aci_bd +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fv:Subnet). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +EXAMPLES = r''' +- name: Create a tenant + cisco.aci.aci_tenant: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + state: present + delegate_to: localhost + +- name: Create a bridge domain + cisco.aci.aci_bd: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + bd: database + state: present + delegate_to: localhost + +- name: Create a subnet + cisco.aci.aci_bd_subnet: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + bd: database + gateway: 10.1.1.1 + mask: 24 + state: present + delegate_to: localhost + +- name: Create a subnet with options + cisco.aci.aci_bd_subnet: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + bd: database + subnet_name: sql + gateway: 10.1.2.1 + mask: 23 + description: SQL Servers + scope: public + route_profile_l3_out: corp + route_profile: corp_route_profile + state: present + delegate_to: localhost + +- name: Update a subnets scope to private and shared + cisco.aci.aci_bd_subnet: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + bd: database + gateway: 10.1.1.1 + mask: 24 + scope: [private, shared] + state: present + delegate_to: localhost + +- name: Get all subnets + cisco.aci.aci_bd_subnet: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + +- name: Get all subnets of specific gateway in specified tenant + cisco.aci.aci_bd_subnet: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + gateway: 10.1.1.1 + mask: 24 + state: query + delegate_to: localhost + register: query_result + +- name: Get specific subnet + cisco.aci.aci_bd_subnet: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + bd: database + gateway: 10.1.1.1 + mask: 24 + state: query + delegate_to: localhost + register: query_result + +- name: Delete a subnet + cisco.aci.aci_bd_subnet: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + bd: database + gateway: 10.1.1.1 + mask: 24 + state: absent + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +SUBNET_CONTROL_MAPPING = dict( + nd_ra='nd', + no_gw='no-default-gateway', + querier_ip='querier', + unspecified='', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + bd=dict(type='str', aliases=['bd_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + enable_vip=dict(type='bool'), + gateway=dict(type='str', aliases=['gateway_ip']), # Not required for querying all objects + mask=dict(type='int', aliases=['subnet_mask']), # Not required for querying all objects + subnet_name=dict(type='str', aliases=['name']), + nd_prefix_policy=dict(type='str'), + preferred=dict(type='bool'), + route_profile=dict(type='str'), + route_profile_l3_out=dict(type='str'), + scope=dict(type='list', elements='str', choices=['private', 'public', 'shared']), + subnet_control=dict(type='str', choices=['nd_ra', 'no_gw', 'querier_ip', 'unspecified']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_together=[['gateway', 'mask']], + required_if=[ + ['state', 'present', ['bd', 'gateway', 'mask', 'tenant']], + ['state', 'absent', ['bd', 'gateway', 'mask', 'tenant']], + ], + ) + + aci = ACIModule(module) + + description = module.params.get('description') + enable_vip = aci.boolean(module.params.get('enable_vip')) + tenant = module.params.get('tenant') + bd = module.params.get('bd') + gateway = module.params.get('gateway') + mask = module.params.get('mask') + if mask is not None and mask not in range(0, 129): + # TODO: split checks between IPv4 and IPv6 Addresses + module.fail_json(msg='Valid Subnet Masks are 0 to 32 for IPv4 Addresses and 0 to 128 for IPv6 addresses') + if gateway is not None: + gateway = '{0}/{1}'.format(gateway, str(mask)) + subnet_name = module.params.get('subnet_name') + nd_prefix_policy = module.params.get('nd_prefix_policy') + preferred = aci.boolean(module.params.get('preferred')) + route_profile = module.params.get('route_profile') + route_profile_l3_out = module.params.get('route_profile_l3_out') + scope = module.params.get('scope') + if scope is not None: + if 'private' in scope and 'public' in scope: + module.fail_json(msg="Parameter 'scope' cannot be both 'private' and 'public', got: %s" % scope) + else: + scope = ','.join(sorted(scope)) + state = module.params.get('state') + subnet_control = module.params.get('subnet_control') + if subnet_control: + subnet_control = SUBNET_CONTROL_MAPPING[subnet_control] + name_alias = module.params.get('name_alias') + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='fvBD', + aci_rn='BD-{0}'.format(bd), + module_object=bd, + target_filter={'name': bd}, + ), + subclass_2=dict( + aci_class='fvSubnet', + aci_rn='subnet-[{0}]'.format(gateway), + module_object=gateway, + target_filter={'ip': gateway}, + ), + child_classes=['fvRsBDSubnetToProfile', 'fvRsNdPfxPol'], + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fvSubnet', + class_config=dict( + ctrl=subnet_control, + descr=description, + ip=gateway, + name=subnet_name, + preferred=preferred, + scope=scope, + virtual=enable_vip, + nameAlias=name_alias, + ), + child_configs=[ + {'fvRsBDSubnetToProfile': {'attributes': {'tnL3extOutName': route_profile_l3_out, 'tnRtctrlProfileName': route_profile}}}, + {'fvRsNdPfxPol': {'attributes': {'tnNdPfxPolName': nd_prefix_policy}}}, + ], + ) + + aci.get_diff(aci_class='fvSubnet') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_bd_to_l3out.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_bd_to_l3out.py new file mode 100644 index 00000000..c50bafad --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_bd_to_l3out.py @@ -0,0 +1,240 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_bd_to_l3out +short_description: Bind Bridge Domain to L3 Out (fv:RsBDToOut) +description: +- Bind Bridge Domain to L3 Out on Cisco ACI fabrics. +options: + bd: + description: + - The name of the Bridge Domain. + type: str + aliases: [ bd_name, bridge_domain ] + l3out: + description: + - The name of the l3out to associate with th Bridge Domain. + type: str + tenant: + description: + - The name of the Tenant. + type: str + aliases: [ tenant_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(bd) and C(l3out) parameters should exist before using this module. + The M(cisco.aci.aci_bd) and C(aci_l3out) can be used for these. +seealso: +- module: cisco.aci.aci_bd +- module: cisco.aci.aci_l3out +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fv:RsBDToOut). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +# FIXME: Add examples +EXAMPLES = r''' # ''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +SUBNET_CONTROL_MAPPING = dict( + nd_ra='nd', + no_gw='no-default-gateway', + querier_ip='querier', + unspecified='', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + bd=dict(type='str', aliases=['bd_name', 'bridge_domain']), # Not required for querying all objects + l3out=dict(type='str'), # Not required for querying all objects + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['bd', 'l3out', 'tenant']], + ['state', 'absent', ['bd', 'l3out', 'tenant']], + ], + ) + + bd = module.params.get('bd') + l3out = module.params.get('l3out') + state = module.params.get('state') + tenant = module.params.get('tenant') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='fvBD', + aci_rn='BD-{0}'.format(bd), + module_object=bd, + target_filter={'name': bd}, + ), + subclass_2=dict( + aci_class='fvRsBDToOut', + aci_rn='rsBDToOut-{0}'.format(l3out), + module_object=l3out, + target_filter={'tnL3extOutName': l3out}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fvRsBDToOut', + class_config=dict(tnL3extOutName=l3out), + ) + + aci.get_diff(aci_class='fvRsBDToOut') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_cidr.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_cidr.py new file mode 100644 index 00000000..8a8cbc49 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_cidr.py @@ -0,0 +1,292 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, nkatarmal-crest <nirav.katarmal@crestdatasys.com> +# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: aci_cloud_cidr +short_description: Manage CIDR under Cloud Context Profile (cloud:Cidr) +description: +- Manage Cloud CIDR on Cisco Cloud ACI. +author: +- Nirav (@nirav) +- Cindy Zhao (@cizhao) +options: + address: + description: + - CIDR ip and its netmask. + type: str + aliases: [ cidr ] + description: + description: + - Description of the Cloud CIDR. + type: str + name_alias: + description: + - An alias for the name of the current object. This relates to the nameAlias field in ACI and is used to rename object without changing the DN. + type: str + tenant: + description: + - The name of the Tenant. + type: str + required: yes + cloud_context_profile: + description: + - The name of the Cloud Context Profile. + type: str + required: yes + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + choices: [ absent, present, query ] + default: present + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- This module is only used to manage non_primary Cloud CIDR, see M(cisco.aci.aci_cloud_ctx_profile) to create the primary CIDR. +- More information about the internal APIC class B(cloud:Cidr) from + L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/). +''' + +EXAMPLES = r''' +- name: Create non_primary CIDR + cisco.aci.aci_cloud_cidr: + host: apic + username: admin + password: SomeSecretPassword + tenant: tenantName + address: 10.10.0.0/16 + cloud_context_profile: ctxProfileName + state: present + delegate_to: localhost + +- name: Remove non_primary CIDR + cisco.aci.aci_cloud_cidr: + host: apic + username: admin + password: SomeSecretPassword + tenant: tenantName + address: 10.10.0.0/16 + cloud_context_profile: ctxProfileName + state: absent + delegate_to: localhost + +- name: Query all CIDRs under given cloud context profile + cisco.aci.aci_cloud_cidr: + host: apic + username: admin + password: SomeSecretPassword + tenant: tenantName + cloud_context_profile: ctxProfileName + state: query + delegate_to: localhost + +- name: Query specific CIDR under given cloud context profile + cisco.aci.aci_cloud_cidr: + host: apic + username: admin + password: SomeSecretPassword + tenant: tenantName + cloud_context_profile: ctxProfileName + address: 10.10.0.0/16 + state: query + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + address=dict(type='str', aliases=['cidr']), + description=dict(type='str'), + name_alias=dict(type='str'), + tenant=dict(type='str', required=True), + cloud_context_profile=dict(type='str', required=True), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['address']], + ['state', 'present', ['address']], + ], + ) + + address = module.params.get('address') + description = module.params.get('description') + name_alias = module.params.get('name_alias') + tenant = module.params.get('tenant') + cloud_context_profile = module.params.get('cloud_context_profile') + state = module.params.get('state') + child_configs = [] + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + target_filter='eq(fvTenant.name, "{0}")'.format(tenant), + module_object=tenant + ), + subclass_1=dict( + aci_class='cloudCtxProfile', + aci_rn='ctxprofile-{0}'.format(cloud_context_profile), + target_filter='eq(cloudCtxProfile.name, "{0}")'.format(cloud_context_profile), + module_object=cloud_context_profile + ), + subclass_2=dict( + aci_class='cloudCidr', + aci_rn='cidr-[{0}]'.format(address), + target_filter='eq(cloudCidr.addr, "{0}")'.format(address), + module_object=address + ), + + child_classes=[] + + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='cloudCidr', + class_config=dict( + addr=address, + descr=description, + nameAlias=name_alias, + primary='no', + ), + child_configs=child_configs + ) + + aci.get_diff(aci_class='cloudCidr') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_ctx_profile.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_ctx_profile.py new file mode 100644 index 00000000..7fdb3b08 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_ctx_profile.py @@ -0,0 +1,318 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, nkatarmal-crest <nirav.katarmal@crestdatasys.com> +# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: aci_cloud_ctx_profile +short_description: Manage Cloud Context Profile (cloud:CtxProfile) +description: +- Manage the Cloud Context Profile objects on Cisco Cloud ACI. +notes: +- More information about the internal APIC class B(cloud:CtxProfile) from + L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/). +author: +- Nirav (@crestdatasys) +- Cindy Zhao (@cizhao) +options: + name: + description: + - The name of the Cloud Context Profile + type: str + aliases: [ cloud_context_profile ] + description: + description: + - Description of the Cloud Context Profile + type: str + name_alias: + description: + - An alias for the name of the current object. This relates to the nameAlias field in ACI and is used to rename object without changing the DN + type: str + tenant: + description: + - The name of the Tenant. + type: str + primary_cidr: + description: + - The subnet with netmask to use as primary CIDR block for the Cloud Context Profile. + type: str + vrf: + description: + - The name of the VRF. + type: str + region: + description: + - The name of the cloud region in which to deploy the network construct. + type: str + cloud: + description: + - The cloud vendor in which the controller runs. + choices: [ aws, azure ] + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + choices: [ absent, present, query ] + type: str + default: present + +extends_documentation_fragment: +- cisco.aci.aci +''' + +EXAMPLES = r''' +- name: Add a new aci cloud ctx profile + cisco.aci.aci_cloud_ctx_profile: + host: apic_host + username: admin + password: SomeSecretPassword + tenant: tenant_1 + name: cloud_ctx_profile + vrf: VRF1 + region: us-west-1 + cloud: aws + primary_cidr: '10.0.10.1/16' + state: present + delegate_to: localhost + +- name: Remove an aci cloud ctx profile + cisco.aci.aci_cloud_ctx_profile: + host: apic_host + username: admin + password: SomeSecretPassword + tenant: tenant_1 + name: cloud_ctx_profile + state: absent + delegate_to: localhost + +- name: Query a specific aci cloud ctx profile + cisco.aci.aci_cloud_ctx_profile: + host: apic_host + username: admin + password: SomeSecretPassword + tenant: anstest + name: ctx_profile_1 + state: query + delegate_to: localhost + +- name: Query all aci cloud ctx profile + cisco.aci.aci_cloud_ctx_profile: + host: apic_host + username: admin + password: SomeSecretPassword + tenant: anstest + state: query + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + description=dict(type='str',), + name=dict(type='str', aliases=['cloud_context_profile']), + name_alias=dict(type='str',), + tenant=dict(type='str',), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + primary_cidr=dict(type='str',), + # FIXME: didn't find the flow_log in UI + # flow_log=dict(type='str'), + vrf=dict(type='str'), + region=dict(type='str'), + cloud=dict(type='str', choices=['aws', 'azure']) + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['name', 'tenant']], + ['state', 'present', ['name', 'tenant', 'vrf', 'region', 'primary_cidr', 'cloud']], + ], + ) + + description = module.params.get('description') + name = module.params.get('name') + name_alias = module.params.get('name_alias') + tenant = module.params.get('tenant') + state = module.params.get('state') + primary_cidr = module.params.get('primary_cidr') + child_configs = [] + + vrf = module.params.get('vrf') + region = module.params.get('region') + cloud = module.params.get('cloud') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + target_filter='eq(fvTenant.name, "{0}")'.format(tenant), + module_object=tenant + ), + subclass_1=dict( + aci_class='cloudCtxProfile', + aci_rn='ctxprofile-{0}'.format(name), + target_filter='eq(cloudCtxProfile.name, "{0}")'.format(name), + module_object=name + ), + child_classes=['cloudRsToCtx', 'cloudRsCtxProfileToRegion', 'cloudRouterP', 'cloudCidr'] + ) + + aci.get_existing() + + if state == 'present': + child_configs.append(dict(cloudRsToCtx=dict(attributes=dict(tnFvCtxName=vrf)))) + child_configs.append(dict( + cloudRsCtxProfileToRegion=dict( + attributes=dict( + tDn="uni/clouddomp/provp-{0}/region-{1}".format(cloud, region) + ) + ) + )) + child_configs.append(dict( + cloudCidr=dict( + attributes=dict( + addr=primary_cidr, + primary="yes" + ) + ) + )) + aci.payload( + aci_class='cloudCtxProfile', + class_config=dict( + descr=description, + name=name, + name_alias=name_alias, + type='regular', + ), + child_configs=child_configs + ) + + aci.get_diff(aci_class='cloudCtxProfile') + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_provider.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_provider.py new file mode 100644 index 00000000..a236d34d --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_provider.py @@ -0,0 +1,178 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Lionel Hercot (@lhercot) <lhercot@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: aci_cloud_provider +short_description: Query Cloud Provider information (cloud:ProvP) +description: +- Query Cloud Provider information (cloud:ProvP) on Cisco Cloud ACI. +author: +- Lionel Hercot (@lhercot) +options: + state: + description: + - Use C(query) for listing an object or multiple objects. + choices: [ query ] + default: query + type: str +notes: +- More information about the internal APIC class B(cloud:ProvP) from + L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/). +- This module is used to query Cloud Provider information. + +extends_documentation_fragment: +- cisco.aci.aci +''' + +EXAMPLES = r''' +- name: Query cloud provider information + cisco.aci.aci_cloud_provider: + host: apic + username: userName + password: somePassword + validate_certs: no + state: query + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + state=dict(type='str', default='query', choices=['query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='cloudProvP' + ) + ) + + aci.get_existing() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_region.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_region.py new file mode 100644 index 00000000..6ae8dad0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_region.py @@ -0,0 +1,219 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, nkatarmal-crest <nirav.katarmal@crestdatasys.com> +# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: aci_cloud_region +short_description: Manage Cloud Providers Region (cloud:Region) +description: +- Manage Cloud Providers Region on Cisco Cloud ACI. +author: +- Nirav (@nirav) +- Cindy Zhao (@cizhao) +options: + region: + description: + - The name of the cloud provider's region. + aliases: [ name ] + type: str + cloud: + description: + - The vendor of the controller + choices: [ aws, azure ] + type: str + required: yes + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + choices: [ query ] + default: query + type: str +notes: +- More information about the internal APIC class B(cloud:Region) from + L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/). +- This module is used to query Cloud Providers Region. + +extends_documentation_fragment: +- cisco.aci.aci +''' + +EXAMPLES = r''' +- name: Query all regions + cisco.aci.aci_cloud_region: + host: apic + username: userName + password: somePassword + validate_certs: no + cloud: 'aws' + state: query + delegate_to: localhost + +- name: Query a specific region + cisco.aci.aci_cloud_region: + host: apic + username: userName + password: somePassword + validate_certs: no + cloud: 'aws' + region: eu-west-2 + state: query + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + region=dict(type='str', aliases=["name"]), + cloud=dict(type='str', choices=['aws', 'azure'], required=True), + state=dict(type='str', default='query', choices=['query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + region = module.params.get('region') + cloud = module.params.get('cloud') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='cloudProvP', + aci_rn='clouddomp/provp-{0}'.format(cloud), + target_filter='eq(cloudProvP.vendor, "{0}")'.format(cloud), + module_object=cloud + ), + subclass_1=dict( + aci_class='cloudRegion', + aci_rn='region-{0}'.format(region), + target_filter='eq(cloudRegion.name, "{0}")'.format(region), + module_object=region + ), + child_classes=[] + ) + + aci.get_existing() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_subnet.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_subnet.py new file mode 100644 index 00000000..4d17d80b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_subnet.py @@ -0,0 +1,330 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, nkatarmal-crest <nirav.katarmal@crestdatasys.com> +# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: aci_cloud_subnet +short_description: Manage Cloud Subnet (cloud:Subnet) +description: +- Manage Cloud Subnet on Cisco Cloud ACI. +notes: +- More information about the internal APIC class B(cloud:Subnet) from + L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/). +author: +- Nirav (@nirav) +- Cindy Zhao (@cizhao) +options: + name: + description: + - The name of the Cloud Subnet. + type: str + description: + description: + - Description of the Cloud Subnet. + type: str + address: + description: + - Ip address of the Cloud Subnet. + type: str + aliases: [subnet] + name_alias: + description: + - An alias for the name of the current object. This relates to the nameAlias field in ACI and is used to rename object without changing the DN. + type: str + tenant: + description: + - The name of tenant. + type: str + required: yes + cloud_context_profile: + description: + - The name of cloud context profile. + type: str + required: yes + cidr: + description: + - Address of cloud cidr. + type: str + required: yes + availability_zone: + description: + - The cloud zone which is attached to the given cloud context profile. + - Only used when it is an aws cloud apic. + type: str + vnet_gateway: + description: + - Determine if a vNet Gateway Router will be deployed or not. + - Only used when it is an azure cloud apic. + type: bool + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + choices: [ absent, present, query ] + default: present + type: str + +extends_documentation_fragment: +- cisco.aci.aci +''' + +EXAMPLES = r''' +- name: Create aci cloud subnet + cisco.aci.aci_cloud_subnet: + host: apic + username: userName + password: somePassword + validate_certs: no + tenant: anstest + cloud_context_profile: aws_cloudCtxProfile + cidr: '10.10.0.0/16' + availability_zone: us-west-1a + address: 10.10.0.1 + delegate_to: localhost + +- name: Query a specific subnet + cisco.aci.aci_cloud_subnet: + host: apic + username: userName + password: somePassword + validate_certs: no + tenant: anstest + cloud_context_profile: ctx_profile_1 + cidr: '10.10.0.0/16' + address: 10.10.0.1 + state: query + delegate_to: localhost + +- name: Query all subnets + cisco.aci.aci_cloud_subnet: + host: apic + username: userName + password: somePassword + validate_certs: no + tenant: anstest + cloud_context_profile: ctx_profile_1 + cidr: '10.10.0.0/16' + state: query + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + name=dict(type='str', aliases=['subnet']), + description=dict(type='str'), + address=dict(type='str'), + name_alias=dict(type='str'), + vnet_gateway=dict(type='bool', default=False), + tenant=dict(type='str', required=True), + cloud_context_profile=dict(type='str', required=True), + cidr=dict(type='str', required=True), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + availability_zone=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['address']], + ['state', 'present', ['address']], + ], + ) + + name = module.params.get('name') + description = module.params.get('description') + address = module.params.get('address') + name_alias = module.params.get('name_alias') + vnet_gateway = module.params.get('vnet_gateway') + tenant = module.params.get('tenant') + cloud_context_profile = module.params.get('cloud_context_profile') + cidr = module.params.get('cidr') + state = module.params.get('state') + availability_zone = module.params.get('availability_zone') + child_configs = [] + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + target_filter='eq(fvTenant.name, "{0}")'.format(tenant), + module_object=tenant + ), + subclass_1=dict( + aci_class='cloudCtxProfile', + aci_rn='ctxprofile-{0}'.format(cloud_context_profile), + target_filter='eq(cloudCtxProfile.name, "{0}")'.format(cloud_context_profile), + module_object=cloud_context_profile + ), + subclass_2=dict( + aci_class='cloudCidr', + aci_rn='cidr-[{0}]'.format(cidr), + target_filter='eq(cloudCidr.addr, "{0}")'.format(cidr), + module_object=cidr + ), + subclass_3=dict( + aci_class='cloudSubnet', + aci_rn='subnet-[{0}]'.format(address), + target_filter='eq(cloudSubnet.ip, "{0}")'.format(address), + module_object=address + ), + child_classes=['cloudRsZoneAttach'] + ) + + aci.get_existing() + + if state == 'present': + # in aws cloud apic + if availability_zone: + region = availability_zone[:-1] + tDn = 'uni/clouddomp/provp-aws/region-{0}/zone-{1}'.format(region, availability_zone) + child_configs.append({'cloudRsZoneAttach': {'attributes': {'tDn': tDn}}}) + # in azure cloud apic + if vnet_gateway: + usage = 'gateway' + else: + usage = 'user' + + aci.payload( + aci_class='cloudSubnet', + class_config=dict( + name=name, + descr=description, + ip=address, + nameAlias=name_alias, + scope='private', + usage=usage, + ), + child_configs=child_configs + ) + + aci.get_diff(aci_class='cloudSubnet') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_vpn_gateway.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_vpn_gateway.py new file mode 100644 index 00000000..9b286dc8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_vpn_gateway.py @@ -0,0 +1,253 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: aci_cloud_vpn_gateway +short_description: Manage cloudRouterP in Cloud Context Profile (cloud:cloudRouterP) +description: +- Manage cloudRouterP objects on Cisco Cloud ACI. +notes: +- More information about the internal APIC class B(cloud:cloudRouterP) from + L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/). +author: +- Cindy Zhao (@cizhao) +options: + tenant: + description: + - The name of tenant. + type: str + required: yes + cloud_context_profile: + description: + - The name of cloud context profile. + type: str + required: yes + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + choices: [ absent, present, query ] + type: str + default: query + +extends_documentation_fragment: +- cisco.aci.aci +''' + +EXAMPLES = r''' +- name: Enable VpnGateway + cisco.aci.aci_cloud_vpn_gateway: + host: apic_host + username: admin + password: SomeSecretPassword + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + state: present + delegate_to: localhost + +- name: Disable VpnGateway + cisco.aci.aci_cloud_vpn_gateway: + host: apic_host + username: admin + password: SomeSecretPassword + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + state: absent + delegate_to: localhost + +- name: Query VpnGateway + cisco.aci.aci_cloud_vpn_gateway: + host: apic_host + username: admin + password: SomeSecretPassword + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', required=True), + cloud_context_profile=dict(type='str', required=True), + state=dict(type='str', default='query', choices=['absent', 'present', 'query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + tenant = module.params.get('tenant') + cloud_context_profile = module.params.get('cloud_context_profile') + state = module.params.get('state') + child_configs = [] + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + target_filter='eq(fvTenant.name, "{0}")'.format(tenant), + module_object=tenant + ), + subclass_1=dict( + aci_class='cloudCtxProfile', + aci_rn='ctxprofile-{0}'.format(cloud_context_profile), + target_filter='eq(cloudCtxProfile.name, "{0}")'.format(cloud_context_profile), + module_object=cloud_context_profile + ), + subclass_2=dict( + aci_class='cloudRouterP', + aci_rn="routerp-default", + target_filter='eq(cloudRouterP.name, "default")', + module_object="default" + ), + child_classes=['cloudRsToVpnGwPol', 'cloudRsToHostRouterPol', 'cloudIntNetworkP'] + ) + + aci.get_existing() + + if state == 'present': + child_configs.append(dict( + cloudIntNetworkP=dict( + attributes=dict( + name="default" + ) + ) + )) + aci.payload( + aci_class='cloudRouterP', + class_config=dict( + name="default" + ), + child_configs=child_configs + ) + + aci.get_diff(aci_class='cloudRouterP') + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_zone.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_zone.py new file mode 100644 index 00000000..be85f18e --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_cloud_zone.py @@ -0,0 +1,233 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, nkatarmal-crest <nirav.katarmal@crestdatasys.com> +# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: aci_cloud_zone +short_description: Manage Cloud Availability Zone (cloud:Zone) +description: +- Manage Cloud Availability Zone on Cisco Cloud ACI. +notes: +- More information about the internal APIC class B(cloud:Zone) from + L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/). +- This module is used to query Cloud Availability Zone. +author: +- Nirav (@nirav) +- Cindy Zhao (@cizhao) +options: + name: + description: + - object name + aliases: [ zone ] + type: str + cloud: + description: + - The cloud provider. + choices: [ aws, azure ] + type: str + required: yes + region: + description: + - The name of the cloud provider's region. + type: str + required: yes + state: + description: + - Use C(query) for listing an object or multiple objects. + choices: [ query ] + default: query + type: str + +extends_documentation_fragment: +- cisco.aci.aci +''' + +EXAMPLES = r''' +- name: Query all zones in a region + cisco.aci.aci_cloud_zone: + host: apic + username: userName + password: somePassword + validate_certs: no + cloud: 'aws' + region: regionName + state: query + delegate_to: localhost + +- name: Query a specific zone + cisco.aci.aci_cloud_zone: + host: apic + username: userName + password: somePassword + validate_certs: no + cloud: 'aws' + region: regionName + zone: zoneName + state: query + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + name=dict(type='str', aliases=['zone']), + cloud=dict(type='str', choices=['aws', 'azure'], required=True), + region=dict(type='str', required=True), + state=dict(type='str', default='query', choices=['query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + name = module.params.get('name') + cloud = module.params.get('cloud') + region = module.params.get('region') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='cloudProvP', + aci_rn='clouddomp/provp-{0}'.format(cloud), + target_filter='eq(cloudProvP.vendor, "{0}")'.format(cloud), + module_object=cloud + ), + subclass_1=dict( + aci_class='cloudRegion', + aci_rn='region-{0}'.format(region), + target_filter='eq(cloudRegion.name, "{0}")'.format(region), + module_object=region + ), + subclass_2=dict( + aci_class='cloudZone', + aci_rn='zone-{0}'.format(name), + target_filter='eq(cloudZone.name, "{0}")'.format(name), + module_object=name + ), + child_classes=[] + ) + + aci.get_existing() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_config_rollback.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_config_rollback.py new file mode 100644 index 00000000..fb8ffc19 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_config_rollback.py @@ -0,0 +1,321 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_config_rollback +short_description: Provides rollback and rollback preview functionality (config:ImportP) +description: +- Provides rollback and rollback preview functionality for Cisco ACI fabrics. +- Config Rollbacks are done using snapshots C(aci_snapshot) with the configImportP class. +options: + compare_export_policy: + description: + - The export policy that the C(compare_snapshot) is associated to. + type: str + compare_snapshot: + description: + - The name of the snapshot to compare with C(snapshot). + type: str + description: + description: + - The description for the Import Policy. + type: str + aliases: [ descr ] + export_policy: + description: + - The export policy that the C(snapshot) is associated to. + type: str + fail_on_decrypt: + description: + - Determines if the APIC should fail the rollback if unable to decrypt secured data. + - The APIC defaults to C(yes) when unset. + type: bool + import_mode: + description: + - Determines how the import should be handled by the APIC. + - The APIC defaults to C(atomic) when unset. + type: str + choices: [ atomic, best-effort ] + import_policy: + description: + - The name of the Import Policy to use for config rollback. + type: str + import_type: + description: + - Determines how the current and snapshot configuration should be compared for replacement. + - The APIC defaults to C(replace) when unset. + type: str + choices: [ merge, replace ] + snapshot: + description: + - The name of the snapshot to rollback to, or the base snapshot to use for comparison. + - The C(aci_snapshot) module can be used to query the list of available snapshots. + type: str + required: yes + state: + description: + - Use C(preview) for previewing the diff between two snapshots. + - Use C(rollback) for reverting the configuration to a previous snapshot. + type: str + choices: [ preview, rollback ] + default: rollback +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- module: cisco.aci.aci_config_snapshot +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(config:ImportP). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +EXAMPLES = r''' +--- +- name: Create a Snapshot + cisco.aci.aci_config_snapshot: + host: apic + username: admin + password: SomeSecretPassword + export_policy: config_backup + state: present + delegate_to: localhost + +- name: Query Existing Snapshots + cisco.aci.aci_config_snapshot: + host: apic + username: admin + password: SomeSecretPassword + export_policy: config_backup + state: query + delegate_to: localhost + +- name: Compare Snapshot Files + cisco.aci.aci_config_rollback: + host: apic + username: admin + password: SomeSecretPassword + export_policy: config_backup + snapshot: run-2017-08-28T06-24-01 + compare_export_policy: config_backup + compare_snapshot: run-2017-08-27T23-43-56 + state: preview + delegate_to: localhost + +- name: Rollback Configuration + cisco.aci.aci_config_rollback: + host: apic + username: admin + password: SomeSecretPassword + import_policy: rollback_config + export_policy: config_backup + snapshot: run-2017-08-28T06-24-01 + state: rollback + delegate_to: localhost + +- name: Rollback Configuration + cisco.aci.aci_config_rollback: + host: apic + username: admin + password: SomeSecretPassword + import_policy: rollback_config + export_policy: config_backup + snapshot: run-2017-08-28T06-24-01 + description: Rollback 8-27 changes + import_mode: atomic + import_type: replace + fail_on_decrypt: yes + state: rollback + delegate_to: localhost +''' + +RETURN = r''' +preview: + description: A preview between two snapshots + returned: when state is preview + type: str +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_bytes +from ansible.module_utils.urls import fetch_url + +# Optional, only used for rollback preview +try: + import lxml.etree + from xmljson import cobra + XML_TO_JSON = True +except ImportError: + XML_TO_JSON = False + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + compare_export_policy=dict(type='str'), + compare_snapshot=dict(type='str'), + description=dict(type='str', aliases=['descr']), + export_policy=dict(type='str'), + fail_on_decrypt=dict(type='bool'), + import_mode=dict(type='str', choices=['atomic', 'best-effort']), + import_policy=dict(type='str'), + import_type=dict(type='str', choices=['merge', 'replace']), + snapshot=dict(type='str', required=True), + state=dict(type='str', default='rollback', choices=['preview', 'rollback']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=False, + required_if=[ + ['state', 'preview', ['compare_export_policy', 'compare_snapshot']], + ['state', 'rollback', ['import_policy']], + ], + ) + + aci = ACIModule(module) + + description = module.params.get('description') + export_policy = module.params.get('export_policy') + fail_on_decrypt = aci.boolean(module.params.get('fail_on_decrypt')) + import_mode = module.params.get('import_mode') + import_policy = module.params.get('import_policy') + import_type = module.params.get('import_type') + snapshot = module.params.get('snapshot') + state = module.params.get('state') + + if state == 'rollback': + if snapshot.startswith('run-'): + snapshot = snapshot.replace('run-', '', 1) + + if not snapshot.endswith('.tar.gz'): + snapshot += '.tar.gz' + + filename = 'ce2_{0}-{1}'.format(export_policy, snapshot) + + aci.construct_url( + root_class=dict( + aci_class='configImportP', + aci_rn='fabric/configimp-{0}'.format(import_policy), + module_object=import_policy, + target_filter={'name': import_policy}, + ), + ) + + aci.get_existing() + + aci.payload( + aci_class='configImportP', + class_config=dict( + adminSt='triggered', + descr=description, + failOnDecryptErrors=fail_on_decrypt, + fileName=filename, + importMode=import_mode, + importType=import_type, + name=import_policy, + snapshot='yes', + ), + ) + + aci.get_diff(aci_class='configImportP') + + aci.post_config() + + elif state == 'preview': + aci.url = '%(protocol)s://%(host)s/mqapi2/snapshots.diff.xml' % module.params + aci.filter_string = ( + '?s1dn=uni/backupst/snapshots-[uni/fabric/configexp-%(export_policy)s]/snapshot-%(snapshot)s&' + 's2dn=uni/backupst/snapshots-[uni/fabric/configexp-%(compare_export_policy)s]/snapshot-%(compare_snapshot)s' + ) % module.params + + # Generate rollback comparison + get_preview(aci) + + aci.exit_json() + + +def get_preview(aci): + ''' + This function is used to generate a preview between two snapshots and add the parsed results to the aci module return data. + ''' + uri = aci.url + aci.filter_string + resp, info = fetch_url(aci.module, uri, headers=aci.headers, method='GET', timeout=aci.module.params.get('timeout'), + use_proxy=aci.module.params.get('use_proxy')) + aci.method = 'GET' + aci.response = info.get('msg') + aci.status = info.get('status') + + # Handle APIC response + if info.get('status') == 200: + xml_to_json(aci, resp.read()) + else: + aci.result['raw'] = resp.read() + aci.fail_json(msg="Request failed: %(code)s %(text)s (see 'raw' output)" % aci.error) + + +def xml_to_json(aci, response_data): + ''' + This function is used to convert preview XML data into JSON. + ''' + if XML_TO_JSON: + xml = lxml.etree.fromstring(to_bytes(response_data)) + xmldata = cobra.data(xml) + aci.result['preview'] = xmldata + else: + aci.result['preview'] = response_data + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_config_snapshot.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_config_snapshot.py new file mode 100644 index 00000000..8f71ca01 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_config_snapshot.py @@ -0,0 +1,337 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_config_snapshot +short_description: Manage Config Snapshots (config:Snapshot, config:ExportP) +description: +- Manage Config Snapshots on Cisco ACI fabrics. +- Creating new Snapshots is done using the configExportP class. +- Removing Snapshots is done using the configSnapshot class. +options: + description: + description: + - The description for the Config Export Policy. + type: str + aliases: [ descr ] + export_policy: + description: + - The name of the Export Policy to use for Config Snapshots. + type: str + aliases: [ name ] + format: + description: + - Sets the config backup to be formatted in JSON or XML. + - The APIC defaults to C(json) when unset. + type: str + choices: [ json, xml ] + include_secure: + description: + - Determines if secure information should be included in the backup. + - The APIC defaults to C(yes) when unset. + type: bool + max_count: + description: + - Determines how many snapshots can exist for the Export Policy before the APIC starts to rollover. + - Accepted values range between C(1) and C(10). + - The APIC defaults to C(3) when unset. + type: int + snapshot: + description: + - The name of the snapshot to delete. + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The APIC does not provide a mechanism for naming the snapshots. +- 'Snapshot files use the following naming structure: ce_<config export policy name>-<yyyy>-<mm>-<dd>T<hh>:<mm>:<ss>.<mss>+<hh>:<mm>.' +- 'Snapshot objects use the following naming structure: run-<yyyy>-<mm>-<dd>T<hh>-<mm>-<ss>.' +seealso: +- module: cisco.aci.aci_config_rollback +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(config:Snapshot) and B(config:ExportP). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +EXAMPLES = r''' +- name: Create a Snapshot + cisco.aci.aci_config_snapshot: + host: apic + username: admin + password: SomeSecretPassword + state: present + export_policy: config_backup + max_count: 10 + description: Backups taken before new configs are applied. + delegate_to: localhost + +- name: Query all Snapshots + cisco.aci.aci_config_snapshot: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result + +- name: Query Snapshots associated with a particular Export Policy + cisco.aci.aci_config_snapshot: + host: apic + username: admin + password: SomeSecretPassword + export_policy: config_backup + state: query + delegate_to: localhost + register: query_result + +- name: Delete a Snapshot + cisco.aci.aci_config_snapshot: + host: apic + username: admin + password: SomeSecretPassword + export_policy: config_backup + snapshot: run-2017-08-24T17-20-05 + state: absent + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + description=dict(type='str', aliases=['descr']), + export_policy=dict(type='str', aliases=['name']), # Not required for querying all objects + format=dict(type='str', choices=['json', 'xml']), + include_secure=dict(type='bool'), + max_count=dict(type='int'), + snapshot=dict(type='str'), + state=dict(type='str', choices=['absent', 'present', 'query'], default='present'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=False, + required_if=[ + ['state', 'absent', ['export_policy', 'snapshot']], + ['state', 'present', ['export_policy']], + ], + ) + + aci = ACIModule(module) + + description = module.params.get('description') + export_policy = module.params.get('export_policy') + file_format = module.params.get('format') + include_secure = aci.boolean(module.params.get('include_secure')) + max_count = module.params.get('max_count') + if max_count is not None: + if max_count in range(1, 11): + max_count = str(max_count) + else: + module.fail_json(msg="Parameter 'max_count' must be a number between 1 and 10") + snapshot = module.params.get('snapshot') + if snapshot is not None and not snapshot.startswith('run-'): + snapshot = 'run-' + snapshot + state = module.params.get('state') + + if state == 'present': + aci.construct_url( + root_class=dict( + aci_class='configExportP', + aci_rn='fabric/configexp-{0}'.format(export_policy), + module_object=export_policy, + target_filter={'name': export_policy}, + ), + ) + + aci.get_existing() + + aci.payload( + aci_class='configExportP', + class_config=dict( + adminSt='triggered', + descr=description, + format=file_format, + includeSecureFields=include_secure, + maxSnapshotCount=max_count, + name=export_policy, + snapshot='yes', + ), + ) + + aci.get_diff('configExportP') + + # Create a new Snapshot + aci.post_config() + + else: + # Prefix the proper url to export_policy + if export_policy is not None: + export_policy = 'uni/fabric/configexp-{0}'.format(export_policy) + + aci.construct_url( + root_class=dict( + aci_class='configSnapshotCont', + aci_rn='backupst/snapshots-[{0}]'.format(export_policy), + module_object=export_policy, + target_filter={'name': export_policy}, + ), + subclass_1=dict( + aci_class='configSnapshot', + aci_rn='snapshot-{0}'.format(snapshot), + module_object=snapshot, + target_filter={'name': snapshot}, + ), + ) + + aci.get_existing() + + if state == 'absent': + # Build POST request to used to remove Snapshot + aci.payload( + aci_class='configSnapshot', + class_config=dict( + name=snapshot, + retire="yes", + ), + ) + + if aci.existing: + aci.get_diff('configSnapshot') + + # Mark Snapshot for Deletion + aci.post_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_contract.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_contract.py new file mode 100644 index 00000000..36f6e00f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_contract.py @@ -0,0 +1,312 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_contract +short_description: Manage contract resources (vz:BrCP) +description: +- Manage Contract resources on Cisco ACI fabrics. +options: + contract: + description: + - The name of the contract. + type: str + aliases: [ contract_name, name ] + description: + description: + - Description for the contract. + type: str + aliases: [ descr ] + tenant: + description: + - The name of the tenant. + type: str + aliases: [ tenant_name ] + scope: + description: + - The scope of a service contract. + - The APIC defaults to C(context) when unset during creation. + type: str + choices: [ application-profile, context, global, tenant ] + priority: + description: + - The desired QoS class to be used. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ level1, level2, level3, unspecified ] + dscp: + description: + - The target Differentiated Service (DSCP) value. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ AF11, AF12, AF13, AF21, AF22, AF23, AF31, AF32, AF33, AF41, AF42, AF43, CS0, CS1, CS2, CS3, CS4, CS5, CS6, CS7, EF, VA, unspecified ] + aliases: [ target ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- This module does not manage Contract Subjects, see M(cisco.aci.aci_contract_subject) to do this. + Contract Subjects can still be removed using this module. +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_contract_subject +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(vz:BrCP). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Add a new contract + cisco.aci.aci_contract: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + contract: web_to_db + description: Communication between web-servers and database + scope: application-profile + state: present + delegate_to: localhost + +- name: Remove an existing contract + cisco.aci.aci_contract: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + contract: web_to_db + state: absent + delegate_to: localhost + +- name: Query a specific contract + cisco.aci.aci_contract: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + contract: web_to_db + state: query + delegate_to: localhost + register: query_result + +- name: Query all contracts + cisco.aci.aci_contract: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + contract=dict(type='str', aliases=['contract_name', 'name']), # Not required for querying all objects + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + scope=dict(type='str', choices=['application-profile', 'context', 'global', 'tenant']), + priority=dict(type='str', choices=['level1', 'level2', 'level3', 'unspecified']), # No default provided on purpose + dscp=dict(type='str', + choices=['AF11', 'AF12', 'AF13', 'AF21', 'AF22', 'AF23', 'AF31', 'AF32', 'AF33', 'AF41', 'AF42', 'AF43', + 'CS0', 'CS1', 'CS2', 'CS3', 'CS4', 'CS5', 'CS6', 'CS7', 'EF', 'VA', 'unspecified'], + aliases=['target']), # No default provided on purpose + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['contract', 'tenant']], + ['state', 'present', ['contract', 'tenant']], + ], + ) + + contract = module.params.get('contract') + description = module.params.get('description') + scope = module.params.get('scope') + priority = module.params.get('priority') + dscp = module.params.get('dscp') + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='vzBrCP', + aci_rn='brc-{0}'.format(contract), + module_object=contract, + target_filter={'name': contract}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='vzBrCP', + class_config=dict( + name=contract, + descr=description, + scope=scope, + prio=priority, + targetDscp=dscp, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='vzBrCP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject.py new file mode 100644 index 00000000..24fc8ed0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject.py @@ -0,0 +1,358 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_contract_subject +short_description: Manage initial Contract Subjects (vz:Subj) +description: +- Manage initial Contract Subjects on Cisco ACI fabrics. +options: + tenant: + description: + - The name of the tenant. + type: str + aliases: [ tenant_name ] + subject: + description: + - The contract subject name. + type: str + aliases: [ contract_subject, name, subject_name ] + contract: + description: + - The name of the Contract. + type: str + aliases: [ contract_name ] + reverse_filter: + description: + - Determines if the APIC should reverse the src and dst ports to allow the + return traffic back, since ACI is stateless filter. + - The APIC defaults to C(yes) when unset during creation. + type: bool + priority: + description: + - The QoS class. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ level1, level2, level3, unspecified ] + dscp: + description: + - The target DSCP. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ AF11, AF12, AF13, AF21, AF22, AF23, AF31, AF32, AF33, AF41, AF42, AF43, + CS0, CS1, CS2, CS3, CS4, CS5, CS6, CS7, EF, VA, unspecified ] + aliases: [ target ] + description: + description: + - Description for the contract subject. + type: str + aliases: [ descr ] + consumer_match: + description: + - The match criteria across consumers. + - The APIC defaults to C(at_least_one) when unset during creation. + type: str + choices: [ all, at_least_one, at_most_one, none ] + provider_match: + description: + - The match criteria across providers. + - The APIC defaults to C(at_least_one) when unset during creation. + type: str + choices: [ all, at_least_one, at_most_one, none ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) and C(contract) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_contract) modules can be used for this. +seealso: +- module: cisco.aci.aci_contract +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(vz:Subj). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Swetha Chunduri (@schunduri) +''' + +EXAMPLES = r''' +- name: Add a new contract subject + cisco.aci.aci_contract_subject: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + contract: web_to_db + subject: default + description: test + reverse_filter: yes + priority: level1 + dscp: unspecified + state: present + register: query_result + +- name: Remove a contract subject + cisco.aci.aci_contract_subject: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + contract: web_to_db + subject: default + state: absent + delegate_to: localhost + +- name: Query a contract subject + cisco.aci.aci_contract_subject: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + contract: web_to_db + subject: default + state: query + delegate_to: localhost + register: query_result + +- name: Query all contract subjects + cisco.aci.aci_contract_subject: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +MATCH_MAPPING = dict( + all='All', + at_least_one='AtleastOne', + at_most_one='AtmostOne', + none='None', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + contract=dict(type='str', aliases=['contract_name']), # Not required for querying all objects + subject=dict(type='str', aliases=['contract_subject', 'name', 'subject_name']), # Not required for querying all objects + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + priority=dict(type='str', choices=['unspecified', 'level1', 'level2', 'level3']), + reverse_filter=dict(type='bool'), + dscp=dict(type='str', aliases=['target'], + choices=['AF11', 'AF12', 'AF13', 'AF21', 'AF22', 'AF23', 'AF31', 'AF32', 'AF33', 'AF41', 'AF42', 'AF43', + 'CS0', 'CS1', 'CS2', 'CS3', 'CS4', 'CS5', 'CS6', 'CS7', 'EF', 'VA', 'unspecified']), + description=dict(type='str', aliases=['descr']), + consumer_match=dict(type='str', choices=['all', 'at_least_one', 'at_most_one', 'none']), + provider_match=dict(type='str', choices=['all', 'at_least_one', 'at_most_one', 'none']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['contract', 'subject', 'tenant']], + ['state', 'present', ['contract', 'subject', 'tenant']], + ], + ) + + aci = ACIModule(module) + + subject = module.params.get('subject') + priority = module.params.get('priority') + reverse_filter = aci.boolean(module.params.get('reverse_filter')) + contract = module.params.get('contract') + dscp = module.params.get('dscp') + description = module.params.get('description') + consumer_match = module.params.get('consumer_match') + if consumer_match is not None: + consumer_match = MATCH_MAPPING.get(consumer_match) + provider_match = module.params.get('provider_match') + if provider_match is not None: + provider_match = MATCH_MAPPING.get(provider_match) + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='vzBrCP', + aci_rn='brc-{0}'.format(contract), + module_object=contract, + target_filter={'name': contract}, + ), + subclass_2=dict( + aci_class='vzSubj', + aci_rn='subj-{0}'.format(subject), + module_object=subject, + target_filter={'name': subject}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='vzSubj', + class_config=dict( + name=subject, + prio=priority, + revFltPorts=reverse_filter, + targetDscp=dscp, + consMatchT=consumer_match, + provMatchT=provider_match, + descr=description, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='vzSubj') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject_to_filter.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject_to_filter.py new file mode 100644 index 00000000..5698295a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject_to_filter.py @@ -0,0 +1,319 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_contract_subject_to_filter +short_description: Bind Contract Subjects to Filters (vz:RsSubjFiltAtt) +description: +- Bind Contract Subjects to Filters on Cisco ACI fabrics. +options: + contract: + description: + - The name of the contract. + type: str + aliases: [ contract_name ] + filter: + description: + - The name of the Filter to bind to the Subject. + type: str + aliases: [ filter_name ] + log: + description: + - Determines if the binding should be set to log. + - The APIC defaults to C(none) when unset during creation. + type: str + choices: [ log, none ] + aliases: [ directive ] + subject: + description: + - The name of the Contract Subject. + type: str + aliases: [ contract_subject, subject_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + tenant: + description: + - The name of the tenant. + type: str + aliases: [ tenant_name ] +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant), C(contract), C(subject), and C(filter_name) must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant), M(cisco.aci.aci_contract), M(cisco.aci.aci_contract_subject), and M(cisco.aci.aci_filter) modules can be used for these. +seealso: +- module: cisco.aci.aci_contract_subject +- module: cisco.aci.aci_filter +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(vz:RsSubjFiltAtt). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +EXAMPLES = r''' +- name: Add a new contract subject to filer binding + cisco.aci.aci_contract_subject_to_filter: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + contract: web_to_db + subject: test + filter: '{{ filter }}' + log: '{{ log }}' + state: present + delegate_to: localhost + +- name: Remove an existing contract subject to filter binding + cisco.aci.aci_contract_subject_to_filter: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + contract: web_to_db + subject: test + filter: '{{ filter }}' + log: '{{ log }}' + state: present + delegate_to: localhost + +- name: Query a specific contract subject to filter binding + cisco.aci.aci_contract_subject_to_filter: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + contract: web_to_db + subject: test + filter: '{{ filter }}' + state: query + delegate_to: localhost + register: query_result + +- name: Query all contract subject to filter bindings + cisco.aci.aci_contract_subject_to_filter: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + contract: web_to_db + subject: test + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + contract=dict(type='str', aliases=['contract_name']), # Not required for querying all objects + filter=dict(type='str', aliases=['filter_name']), # Not required for querying all objects + subject=dict(type='str', aliases=['contract_subject', 'subject_name']), # Not required for querying all objects + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + log=dict(type='str', choices=['log', 'none'], aliases=['directive']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['contract', 'filter', 'subject', 'tenant']], + ['state', 'present', ['contract', 'filter', 'subject', 'tenant']], + ], + ) + + contract = module.params.get('contract') + filter_name = module.params.get('filter') + log = module.params.get('log') + subject = module.params.get('subject') + tenant = module.params.get('tenant') + state = module.params.get('state') + + # Add subject_filter key to modul.params for building the URL + module.params['subject_filter'] = filter_name + + # Convert log to empty string if none, as that is what API expects. An empty string is not a good option to present the user. + if log == 'none': + log = '' + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='vzBrCP', + aci_rn='brc-{0}'.format(contract), + module_object=contract, + target_filter={'name': contract}, + ), + subclass_2=dict( + aci_class='vzSubj', + aci_rn='subj-{0}'.format(subject), + module_object=subject, + target_filter={'name': subject}, + ), + subclass_3=dict( + aci_class='vzRsSubjFiltAtt', + aci_rn='rssubjFiltAtt-{0}'.format(filter_name), + module_object=filter_name, + target_filter={'tnVzFilterName': filter_name}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='vzRsSubjFiltAtt', + class_config=dict( + tnVzFilterName=filter_name, + directives=log, + ), + ) + + aci.get_diff(aci_class='vzRsSubjFiltAtt') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + # Remove subject_filter used to build URL from module.params + module.params.pop('subject_filter') + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_domain.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_domain.py new file mode 100644 index 00000000..82ee9a32 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_domain.py @@ -0,0 +1,395 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_domain +short_description: Manage physical, virtual, bridged, routed or FC domain profiles (phys:DomP, vmm:DomP, l2ext:DomP, l3ext:DomP, fc:DomP) +description: +- Manage physical, virtual, bridged, routed or FC domain profiles on Cisco ACI fabrics. +options: + domain: + description: + - Name of the physical, virtual, bridged routed or FC domain profile. + type: str + aliases: [ domain_name, domain_profile, name ] + domain_type: + description: + - The type of domain profile. + - 'C(fc): The FC domain profile is a policy pertaining to single FC Management domain' + - 'C(l2dom): The external bridged domain profile is a policy for managing L2 bridged infrastructure bridged outside the fabric.' + - 'C(l3dom): The external routed domain profile is a policy for managing L3 routed infrastructure outside the fabric.' + - 'C(phys): The physical domain profile stores the physical resources and encap resources that should be used for EPGs associated with this domain.' + - 'C(vmm): The VMM domain profile is a policy for grouping VM controllers with similar networking policy requirements.' + type: str + required: yes + choices: [ fc, l2dom, l3dom, phys, vmm ] + aliases: [ type ] + dscp: + description: + - The target Differentiated Service (DSCP) value. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ AF11, AF12, AF13, AF21, AF22, AF23, AF31, AF32, AF33, AF41, AF42, AF43, CS0, CS1, CS2, CS3, CS4, CS5, CS6, CS7, EF, VA, unspecified ] + aliases: [ target ] + encap_mode: + description: + - The layer 2 encapsulation protocol to use with the virtual switch. + type: str + choices: [ unknown, vlan, vxlan ] + multicast_address: + description: + - The multicast IP address to use for the virtual switch. + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str + vm_provider: + description: + - The VM platform for VMM Domains. + - Support for Kubernetes was added in ACI v3.0. + - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1. + type: str + choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ] + vswitch: + description: + - The virtual switch to use for vmm domains. + - The APIC defaults to C(default) when unset during creation. + type: str + choices: [ avs, default, dvs, unknown ] +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- module: cisco.aci.aci_aep_to_domain +- module: cisco.aci.aci_domain_to_encap_pool +- module: cisco.aci.aci_domain_to_vlan_pool +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(phys:DomP), + B(vmm:DomP), B(l2ext:DomP), B(l3ext:DomP) and B(fc:DomP) + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Add a new physical domain + cisco.aci.aci_domain: + host: apic + username: admin + password: SomeSecretPassword + domain: phys_dom + domain_type: phys + state: present + +- name: Remove a physical domain + cisco.aci.aci_domain: + host: apic + username: admin + password: SomeSecretPassword + domain: phys_dom + domain_type: phys + state: absent + +- name: Add a new VMM domain + cisco.aci.aci_domain: + host: apic + username: admin + password: SomeSecretPassword + domain: hyperv_dom + domain_type: vmm + vm_provider: microsoft + state: present + delegate_to: localhost + +- name: Remove a VMM domain + cisco.aci.aci_domain: + host: apic + username: admin + password: SomeSecretPassword + domain: hyperv_dom + domain_type: vmm + vm_provider: microsoft + state: absent + delegate_to: localhost + +- name: Query a specific physical domain + cisco.aci.aci_domain: + host: apic + username: admin + password: SomeSecretPassword + domain: phys_dom + domain_type: phys + state: query + delegate_to: localhost + register: query_result + +- name: Query all domains + cisco.aci.aci_domain: + host: apic + username: admin + password: SomeSecretPassword + domain_type: phys + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +VM_PROVIDER_MAPPING = dict( + cloudfoundry='CloudFoundry', + kubernetes='Kubernetes', + microsoft='Microsoft', + openshift='OpenShift', + openstack='OpenStack', + redhat='Redhat', + vmware='VMware', +) + +VSWITCH_MAPPING = dict( + avs='n1kv', + default='default', + dvs='default', + unknown='unknown', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + domain_type=dict(type='str', required=True, choices=['fc', 'l2dom', 'l3dom', 'phys', 'vmm'], aliases=['type']), + domain=dict(type='str', aliases=['domain_name', 'domain_profile', 'name']), # Not required for querying all objects + dscp=dict(type='str', + choices=['AF11', 'AF12', 'AF13', 'AF21', 'AF22', 'AF23', 'AF31', 'AF32', 'AF33', 'AF41', 'AF42', 'AF43', + 'CS0', 'CS1', 'CS2', 'CS3', 'CS4', 'CS5', 'CS6', 'CS7', 'EF', 'VA', 'unspecified'], + aliases=['target']), + encap_mode=dict(type='str', choices=['unknown', 'vlan', 'vxlan']), + multicast_address=dict(type='str'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + vm_provider=dict(type='str', choices=['cloudfoundry', 'kubernetes', 'microsoft', 'openshift', 'openstack', 'redhat', 'vmware']), + vswitch=dict(type='str', choices=['avs', 'default', 'dvs', 'unknown']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['domain_type', 'vmm', ['vm_provider']], + ['state', 'absent', ['domain', 'domain_type']], + ['state', 'present', ['domain', 'domain_type']], + ], + ) + + dscp = module.params.get('dscp') + domain = module.params.get('domain') + domain_type = module.params.get('domain_type') + encap_mode = module.params.get('encap_mode') + multicast_address = module.params.get('multicast_address') + vm_provider = module.params.get('vm_provider') + vswitch = module.params.get('vswitch') + if vswitch is not None: + vswitch = VSWITCH_MAPPING.get(vswitch) + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + if domain_type != 'vmm': + if vm_provider is not None: + module.fail_json(msg="Domain type '{0}' cannot have parameter 'vm_provider'".format(domain_type)) + if encap_mode is not None: + module.fail_json(msg="Domain type '{0}' cannot have parameter 'encap_mode'".format(domain_type)) + if multicast_address is not None: + module.fail_json(msg="Domain type '{0}' cannot have parameter 'multicast_address'".format(domain_type)) + if vswitch is not None: + module.fail_json(msg="Domain type '{0}' cannot have parameter 'vswitch'".format(domain_type)) + + if dscp is not None and domain_type not in ['l2dom', 'l3dom']: + module.fail_json(msg="DSCP values can only be assigned to 'l2ext and 'l3ext' domains") + + # Compile the full domain for URL building + if domain_type == 'fc': + domain_class = 'fcDomP' + domain_mo = 'uni/fc-{0}'.format(domain) + domain_rn = 'fc-{0}'.format(domain) + elif domain_type == 'l2dom': + domain_class = 'l2extDomP' + domain_mo = 'uni/l2dom-{0}'.format(domain) + domain_rn = 'l2dom-{0}'.format(domain) + elif domain_type == 'l3dom': + domain_class = 'l3extDomP' + domain_mo = 'uni/l3dom-{0}'.format(domain) + domain_rn = 'l3dom-{0}'.format(domain) + elif domain_type == 'phys': + domain_class = 'physDomP' + domain_mo = 'uni/phys-{0}'.format(domain) + domain_rn = 'phys-{0}'.format(domain) + elif domain_type == 'vmm': + domain_class = 'vmmDomP' + domain_mo = 'uni/vmmp-{0}/dom-{1}'.format(VM_PROVIDER_MAPPING.get(vm_provider), domain) + domain_rn = 'vmmp-{0}/dom-{1}'.format(VM_PROVIDER_MAPPING.get(vm_provider), domain) + + # Ensure that querying all objects works when only domain_type is provided + if domain is None: + domain_mo = None + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class=domain_class, + aci_rn=domain_rn, + module_object=domain_mo, + target_filter={'name': domain}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class=domain_class, + class_config=dict( + encapMode=encap_mode, + mcastAddr=multicast_address, + mode=vswitch, + name=domain, + targetDscp=dscp, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class=domain_class) + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_domain_to_encap_pool.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_domain_to_encap_pool.py new file mode 100644 index 00000000..a571cfb3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_domain_to_encap_pool.py @@ -0,0 +1,378 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Dag Wieers <dag@wieers.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_domain_to_encap_pool +short_description: Bind Domain to Encap Pools (infra:RsVlanNs) +description: +- Bind Domain to Encap Pools on Cisco ACI fabrics. +notes: +- The C(domain) and C(encap_pool) parameters should exist before using this module. + The M(cisco.aci.aci_domain) and M(cisco.aci.aci_encap_pool) can be used for these. +options: + domain: + description: + - Name of the domain being associated with the Encap Pool. + type: str + aliases: [ domain_name, domain_profile ] + domain_type: + description: + - Determines if the Domain is physical (phys) or virtual (vmm). + type: str + required: yes + choices: [ fc, l2dom, l3dom, phys, vmm ] + pool: + description: + - The name of the pool. + type: str + aliases: [ pool_name ] + pool_allocation_mode: + description: + - The method used for allocating encaps to resources. + - Only vlan and vsan support allocation modes. + type: str + choices: [ dynamic, static] + aliases: [ allocation_mode, mode ] + pool_type: + description: + - The encap type of C(pool). + type: str + required: yes + choices: [ vlan, vsan, vxlan ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + vm_provider: + description: + - The VM platform for VMM Domains. + - Support for Kubernetes was added in ACI v3.0. + - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1. + type: str + choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ] +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- module: cisco.aci.aci_domain +- module: cisco.aci.aci_encap_pool +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(infra:RsVlanNs). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Add domain to VLAN pool binding + cisco.aci.aci_domain_to_encap_pool: + host: apic + username: admin + password: SomeSecretPassword + domain: phys_dom + domain_type: phys + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + state: present + delegate_to: localhost + +- name: Remove domain to VLAN pool binding + cisco.aci.aci_domain_to_encap_pool: + host: apic + username: admin + password: SomeSecretPassword + domain: phys_dom + domain_type: phys + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + state: absent + delegate_to: localhost + +- name: Query our domain to VLAN pool binding + cisco.aci.aci_domain_to_encap_pool: + host: apic + username: admin + password: SomeSecretPassword + domain: phys_dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + state: query + delegate_to: localhost + register: query_result + +- name: Query all domain to VLAN pool bindings + cisco.aci.aci_domain_to_encap_pool: + host: apic + username: admin + password: SomeSecretPassword + domain_type: phys + pool_type: vlan + pool_allocation_mode: dynamic + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +VM_PROVIDER_MAPPING = dict( + cloudfoundry='CloudFoundry', + kubernetes='Kubernetes', + microsoft='Microsoft', + openshift='OpenShift', + openstack='OpenStack', + redhat='Redhat', + vmware='VMware', +) + +POOL_MAPPING = dict( + vlan=dict( + aci_mo='uni/infra/vlanns-{0}', + child_class='infraRsVlanNs', + ), + vxlan=dict( + aci_mo='uni/infra/vxlanns-{0}', + child_class='vmmRsVxlanNs', + ), + vsan=dict( + aci_mo='uni/infra/vsanns-{0}', + child_class='fcRsVsanNs', + ), +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + domain_type=dict(type='str', required=True, choices=['fc', 'l2dom', 'l3dom', 'phys', 'vmm']), + pool_type=dict(type='str', required=True, choices=['vlan', 'vsan', 'vxlan']), + domain=dict(type='str', aliases=['domain_name', 'domain_profile']), # Not required for querying all objects + pool=dict(type='str', aliases=['pool_name']), # Not required for querying all objects + pool_allocation_mode=dict(type='str', aliases=['allocation_mode', 'mode'], choices=['dynamic', 'static']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + vm_provider=dict(type='str', choices=['cloudfoundry', 'kubernetes', 'microsoft', 'openshift', 'openstack', 'redhat', 'vmware']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['domain_type', 'vmm', ['vm_provider']], + ['state', 'absent', ['domain', 'domain_type', 'pool', 'pool_type']], + ['state', 'present', ['domain', 'domain_type', 'pool', 'pool_type']], + ], + ) + + domain = module.params.get('domain') + domain_type = module.params.get('domain_type') + pool = module.params.get('pool') + pool_allocation_mode = module.params.get('pool_allocation_mode') + pool_type = module.params.get('pool_type') + vm_provider = module.params.get('vm_provider') + state = module.params.get('state') + + # Report when vm_provider is set when type is not virtual + if domain_type != 'vmm' and vm_provider is not None: + module.fail_json(msg="Domain type '{0}' cannot have a 'vm_provider'".format(domain_type)) + + # ACI Pool URL requires the allocation mode for vlan and vsan pools (ex: uni/infra/vlanns-[poolname]-static) + pool_name = pool + if pool_type != 'vxlan' and pool is not None: + if pool_allocation_mode is not None: + pool_name = '[{0}]-{1}'.format(pool, pool_allocation_mode) + else: + module.fail_json(msg="ACI requires the 'pool_allocation_mode' for 'pool_type' of 'vlan' and 'vsan' when 'pool' is provided") + + # Vxlan pools do not support allocation modes + if pool_type == 'vxlan' and pool_allocation_mode is not None: + module.fail_json(msg='vxlan pools do not support setting the allocation_mode; please remove this parameter from the task') + + # Compile the full domain for URL building + if domain_type == 'fc': + domain_class = 'fcDomP' + domain_mo = 'uni/fc-{0}'.format(domain) + domain_rn = 'fc-{0}'.format(domain) + elif domain_type == 'l2dom': + domain_class = 'l2extDomP' + domain_mo = 'uni/l2dom-{0}'.format(domain) + domain_rn = 'l2dom-{0}'.format(domain) + elif domain_type == 'l3dom': + domain_class = 'l3extDomP' + domain_mo = 'uni/l3dom-{0}'.format(domain) + domain_rn = 'l3dom-{0}'.format(domain) + elif domain_type == 'phys': + domain_class = 'physDomP' + domain_mo = 'uni/phys-{0}'.format(domain) + domain_rn = 'phys-{0}'.format(domain) + elif domain_type == 'vmm': + domain_class = 'vmmDomP' + domain_mo = 'uni/vmmp-{0}/dom-{1}'.format(VM_PROVIDER_MAPPING[vm_provider], domain) + domain_rn = 'vmmp-{0}/dom-{1}'.format(VM_PROVIDER_MAPPING[vm_provider], domain) + + # Ensure that querying all objects works when only domain_type is provided + if domain is None: + domain_mo = None + + pool_mo = POOL_MAPPING[pool_type]['aci_mo'].format(pool_name) + child_class = POOL_MAPPING[pool_type]['child_class'] + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class=domain_class, + aci_rn=domain_rn, + module_object=domain_mo, + target_filter={'name': domain}, + ), + child_classes=[child_class], + ) + + aci.get_existing() + + if state == 'present': + # Filter out module params with null values + aci.payload( + aci_class=domain_class, + class_config=dict(name=domain), + child_configs=[ + {child_class: {'attributes': {'tDn': pool_mo}}}, + ] + ) + + # Generate config diff which will be used as POST request body + aci.get_diff(aci_class=domain_class) + + # Submit changes if module not in check_mode and the proposed is different than existing + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_domain_to_vlan_pool.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_domain_to_vlan_pool.py new file mode 100644 index 00000000..0e213128 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_domain_to_vlan_pool.py @@ -0,0 +1,366 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Dag Wieers <dag@wieers.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_domain_to_vlan_pool +short_description: Bind Domain to VLAN Pools (infra:RsVlanNs) +description: +- Bind Domain to VLAN Pools on Cisco ACI fabrics. +options: + domain: + description: + - Name of the domain being associated with the VLAN Pool. + type: str + aliases: [ domain_name, domain_profile ] + domain_type: + description: + - Determines if the Domain is physical (phys) or virtual (vmm). + type: str + required: yes + choices: [ fc, l2dom, l3dom, phys, vmm ] + pool: + description: + - The name of the pool. + type: str + aliases: [ pool_name, vlan_pool ] + pool_allocation_mode: + description: + - The method used for allocating VLANs to resources. + type: str + required: yes + choices: [ dynamic, static] + aliases: [ allocation_mode, mode ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + vm_provider: + description: + - The VM platform for VMM Domains. + - Support for Kubernetes was added in ACI v3.0. + - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1. + type: str + choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ] +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(domain) and C(vlan_pool) parameters should exist before using this module. + The M(cisco.aci.aci_domain) and M(cisco.aci.aci_vlan_pool) can be used for these. +seealso: +- module: cisco.aci.aci_domain +- module: cisco.aci.aci_vlan_pool +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(infra:RsVlanNs). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Bind a VMM domain to VLAN pool + cisco.aci.aci_domain_to_vlan_pool: + host: apic + username: admin + password: SomeSecretPassword + domain: vmw_dom + domain_type: vmm + pool: vmw_pool + pool_allocation_mode: dynamic + vm_provider: vmware + state: present + delegate_to: localhost + +- name: Remove a VMM domain to VLAN pool binding + cisco.aci.aci_domain_to_vlan_pool: + host: apic + username: admin + password: SomeSecretPassword + domain: vmw_dom + domain_type: vmm + pool: vmw_pool + pool_allocation_mode: dynamic + vm_provider: vmware + state: absent + delegate_to: localhost + +- name: Bind a physical domain to VLAN pool + cisco.aci.aci_domain_to_vlan_pool: + host: apic + username: admin + password: SomeSecretPassword + domain: phys_dom + domain_type: phys + pool: phys_pool + pool_allocation_mode: static + state: present + delegate_to: localhost + +- name: Bind a physical domain to VLAN pool + cisco.aci.aci_domain_to_vlan_pool: + host: apic + username: admin + password: SomeSecretPassword + domain: phys_dom + domain_type: phys + pool: phys_pool + pool_allocation_mode: static + state: absent + delegate_to: localhost + +- name: Query an domain to VLAN pool binding + cisco.aci.aci_domain_to_vlan_pool: + host: apic + username: admin + password: SomeSecretPassword + domain: phys_dom + domain_type: phys + pool: phys_pool + pool_allocation_mode: static + state: query + delegate_to: localhost + register: query_result + +- name: Query all domain to VLAN pool bindings + cisco.aci.aci_domain_to_vlan_pool: + host: apic + username: admin + password: SomeSecretPassword + domain_type: phys + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +VM_PROVIDER_MAPPING = dict( + cloudfoundry='CloudFoundry', + kubernetes='Kubernetes', + microsoft='Microsoft', + openshift='OpenShift', + openstack='OpenStack', + redhat='Redhat', + vmware='VMware', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + domain_type=dict(type='str', required=True, choices=['fc', 'l2dom', 'l3dom', 'phys', 'vmm']), + domain=dict(type='str', aliases=['domain_name', 'domain_profile']), # Not required for querying all objects + pool=dict(type='str', aliases=['pool_name', 'vlan_pool']), # Not required for querying all objects + pool_allocation_mode=dict(type='str', required=True, aliases=['allocation_mode', 'mode'], choices=['dynamic', 'static']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + vm_provider=dict(type='str', choices=['cloudfoundry', 'kubernetes', 'microsoft', 'openshift', 'openstack', 'redhat', 'vmware']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['domain_type', 'vmm', ['vm_provider']], + ['state', 'absent', ['domain', 'domain_type', 'pool']], + ['state', 'present', ['domain', 'domain_type', 'pool']], + ], + ) + + domain = module.params.get('domain') + domain_type = module.params.get('domain_type') + pool = module.params.get('pool') + pool_allocation_mode = module.params.get('pool_allocation_mode') + vm_provider = module.params.get('vm_provider') + state = module.params.get('state') + + # Report when vm_provider is set when type is not virtual + if domain_type != 'vmm' and vm_provider is not None: + module.fail_json(msg="Domain type '{0}' cannot have a 'vm_provider'".format(domain_type)) + + # ACI Pool URL requires the allocation mode for vlan and vsan pools (ex: uni/infra/vlanns-[poolname]-static) + pool_name = pool + if pool is not None: + pool_name = '[{0}]-{1}'.format(pool, pool_allocation_mode) + + # Compile the full domain for URL building + if domain_type == 'fc': + domain_class = 'fcDomP' + domain_mo = 'uni/fc-{0}'.format(domain) + domain_rn = 'fc-{0}'.format(domain) + elif domain_type == 'l2dom': + domain_class = 'l2extDomP' + domain_mo = 'uni/l2dom-{0}'.format(domain) + domain_rn = 'l2dom-{0}'.format(domain) + elif domain_type == 'l3dom': + domain_class = 'l3extDomP' + domain_mo = 'uni/l3dom-{0}'.format(domain) + domain_rn = 'l3dom-{0}'.format(domain) + elif domain_type == 'phys': + domain_class = 'physDomP' + domain_mo = 'uni/phys-{0}'.format(domain) + domain_rn = 'phys-{0}'.format(domain) + elif domain_type == 'vmm': + domain_class = 'vmmDomP' + domain_mo = 'uni/vmmp-{0}/dom-{1}'.format(VM_PROVIDER_MAPPING[vm_provider], domain) + domain_rn = 'vmmp-{0}/dom-{1}'.format(VM_PROVIDER_MAPPING[vm_provider], domain) + + # Ensure that querying all objects works when only domain_type is provided + if domain is None: + domain_mo = None + + aci_mo = 'uni/infra/vlanns-{0}'.format(pool_name) + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class=domain_class, + aci_rn=domain_rn, + module_object=domain_mo, + target_filter={'name': domain}, + ), + child_classes=['infraRsVlanNs'], + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class=domain_class, + class_config=dict(name=domain), + child_configs=[ + {'infraRsVlanNs': {'attributes': {'tDn': aci_mo}}}, + ] + ) + + aci.get_diff(aci_class=domain_class) + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_encap_pool.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_encap_pool.py new file mode 100644 index 00000000..f35b9342 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_encap_pool.py @@ -0,0 +1,316 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_encap_pool +short_description: Manage encap pools (fvns:VlanInstP, fvns:VxlanInstP, fvns:VsanInstP) +description: +- Manage vlan, vxlan, and vsan pools on Cisco ACI fabrics. +options: + description: + description: + - Description for the C(pool). + type: str + aliases: [ descr ] + pool: + description: + - The name of the pool. + type: str + aliases: [ name, pool_name ] + pool_allocation_mode: + description: + - The method used for allocating encaps to resources. + - Only vlan and vsan support allocation modes. + type: str + choices: [ dynamic, static ] + aliases: [ allocation_mode, mode ] + pool_type: + description: + - The encap type of C(pool). + type: str + required: yes + aliases: [ type ] + choices: [ vlan, vsan, vxlan ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- module: cisco.aci.aci_encap_pool_range +- module: cisco.aci.aci_vlan_pool +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(fvns:VlanInstP), + B(fvns:VxlanInstP) and B(fvns:VsanInstP) + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +EXAMPLES = r''' +- name: Add a new vlan pool + cisco.aci.aci_encap_pool: + host: apic + username: admin + password: SomeSecretPassword + pool: production + pool_type: vlan + description: Production VLANs + state: present + delegate_to: localhost + +- name: Remove a vlan pool + cisco.aci.aci_encap_pool: + host: apic + username: admin + password: SomeSecretPassword + pool: production + pool_type: vlan + state: absent + delegate_to: localhost + +- name: Query a vlan pool + cisco.aci.aci_encap_pool: + host: apic + username: admin + password: SomeSecretPassword + pool: production + pool_type: vlan + state: query + delegate_to: localhost + register: query_result + +- name: Query all vlan pools + cisco.aci.aci_encap_pool: + host: apic + username: admin + password: SomeSecretPassword + pool_type: vlan + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +ACI_POOL_MAPPING = dict( + vlan=dict( + aci_class='fvnsVlanInstP', + aci_mo='infra/vlanns-', + ), + vxlan=dict( + aci_class='fvnsVxlanInstP', + aci_mo='infra/vxlanns-', + ), + vsan=dict( + aci_class='fvnsVsanInstP', + aci_mo='infra/vsanns-', + ), +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + pool_type=dict(type='str', required=True, aliases=['type'], choices=['vlan', 'vsan', 'vxlan']), + description=dict(type='str', aliases=['descr']), + pool=dict(type='str', aliases=['name', 'pool_name']), # Not required for querying all objects + pool_allocation_mode=dict(type='str', aliases=['allocation_mode', 'mode'], choices=['dynamic', 'static']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['pool']], + ['state', 'present', ['pool']], + ], + ) + + description = module.params.get('description') + pool = module.params.get('pool') + pool_type = module.params.get('pool_type') + pool_allocation_mode = module.params.get('pool_allocation_mode') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci_class = ACI_POOL_MAPPING[pool_type]['aci_class'] + aci_mo = ACI_POOL_MAPPING[pool_type]['aci_mo'] + pool_name = pool + + # ACI Pool URL requires the pool_allocation mode for vlan and vsan pools (ex: uni/infra/vlanns-[poolname]-static) + if pool_type != 'vxlan' and pool is not None: + if pool_allocation_mode is not None: + pool_name = '[{0}]-{1}'.format(pool, pool_allocation_mode) + else: + module.fail_json(msg="ACI requires parameter 'pool_allocation_mode' for 'pool_type' of 'vlan' and 'vsan' when parameter 'pool' is provided") + + # Vxlan pools do not support pool allocation modes + if pool_type == 'vxlan' and pool_allocation_mode is not None: + module.fail_json(msg="vxlan pools do not support setting the 'pool_allocation_mode'; please remove this parameter from the task") + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class=aci_class, + aci_rn='{0}{1}'.format(aci_mo, pool_name), + module_object=pool, + target_filter={'name': pool}, + ), + ) + + aci.get_existing() + + if state == 'present': + # Filter out module parameters with null values + aci.payload( + aci_class=aci_class, + class_config=dict( + allocMode=pool_allocation_mode, + descr=description, + name=pool, + nameAlias=name_alias, + ) + ) + + # Generate config diff which will be used as POST request body + aci.get_diff(aci_class=aci_class) + + # Submit changes if module not in check_mode and the proposed is different than existing + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_encap_pool_range.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_encap_pool_range.py new file mode 100644 index 00000000..8e24fadc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_encap_pool_range.py @@ -0,0 +1,449 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_encap_pool_range +short_description: Manage encap ranges assigned to pools (fvns:EncapBlk, fvns:VsanEncapBlk) +description: +- Manage vlan, vxlan, and vsan ranges that are assigned to pools on Cisco ACI fabrics. +options: + allocation_mode: + description: + - The method used for allocating encaps to resources. + - Only vlan and vsan support allocation modes. + type: str + choices: [ dynamic, inherit, static] + aliases: [ mode ] + description: + description: + - Description for the pool range. + type: str + aliases: [ descr ] + pool: + description: + - The name of the pool that the range should be assigned to. + type: str + aliases: [ pool_name ] + pool_allocation_mode: + description: + - The method used for allocating encaps to resources. + - Only vlan and vsan support allocation modes. + type: str + choices: [ dynamic, static] + aliases: [ pool_mode ] + pool_type: + description: + - The encap type of C(pool). + type: str + required: yes + aliases: [ type ] + choices: [ vlan, vxlan, vsan] + range_end: + description: + - The end of encap range. + type: int + aliases: [ end ] + range_name: + description: + - The name to give to the encap range. + type: str + aliases: [ name, range ] + range_start: + description: + - The start of the encap range. + type: int + aliases: [ start ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(pool) must exist in order to add or delete a range. +seealso: +- module: cisco.aci.aci_encap_pool +- module: cisco.aci.aci_vlan_pool_encap_block +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(fvns:EncapBlk) and B(fvns:VsanEncapBlk). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +EXAMPLES = r''' +- name: Add a new VLAN pool range + cisco.aci.aci_encap_pool_range: + host: apic + username: admin + password: SomeSecretPassword + pool: production + pool_type: vlan + pool_allocation_mode: static + range_name: anstest + range_start: 20 + range_end: 40 + allocation_mode: inherit + state: present + delegate_to: localhost + +- name: Remove a VLAN pool range + cisco.aci.aci_encap_pool_range: + host: apic + username: admin + password: SomeSecretPassword + pool: production + pool_type: vlan + pool_allocation_mode: static + range_name: anstest + range_start: 20 + range_end: 40 + state: absent + delegate_to: localhost + +- name: Query a VLAN range + cisco.aci.aci_encap_pool_range: + host: apic + username: admin + password: SomeSecretPassword + pool: production + pool_type: vlan + pool_allocation_mode: static + range_name: anstest + range_start: 20 + range_end: 50 + state: query + delegate_to: localhost + register: query_result + +- name: Query a VLAN pool for ranges by range_name + cisco.aci.aci_encap_pool_range: + host: apic + username: admin + password: SomeSecretPassword + pool_type: vlan + range_name: anstest + state: query + delegate_to: localhost + register: query_result + +- name: Query a VLAN pool for ranges by range_start + cisco.aci.aci_encap_pool_range: + host: apic + username: admin + password: SomeSecretPassword + pool_type: vlan + range_start: 20 + state: query + delegate_to: localhost + register: query_result + +- name: Query a VLAN pool for ranges by range_start and range_end + cisco.aci.aci_encap_pool_range: + host: apic + username: admin + password: SomeSecretPassword + pool_type: vlan + range_start: 20 + range_end: 40 + state: query + delegate_to: localhost + register: query_result + +- name: Query all VLAN pool ranges + cisco.aci.aci_encap_pool_range: + host: apic + username: admin + password: SomeSecretPassword + pool_type: vlan + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +ACI_POOL_MAPPING = dict( + vlan=dict( + aci_class='fvnsVlanInstP', + aci_mo='infra/vlanns-', + ), + vxlan=dict( + aci_class='fvnsVxlanInstP', + aci_mo='infra/vxlanns-', + ), + vsan=dict( + aci_class='fvnsVsanInstP', + aci_mo='infra/vsanns-', + ), +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + pool_type=dict(type='str', required=True, aliases=['type'], choices=['vlan', 'vxlan', 'vsan']), + allocation_mode=dict(type='str', aliases=['mode'], choices=['dynamic', 'inherit', 'static']), + description=dict(type='str', aliases=['descr']), + pool=dict(type='str', aliases=['pool_name']), # Not required for querying all objects + pool_allocation_mode=dict(type='str', aliases=['pool_mode'], choices=['dynamic', 'static']), + range_end=dict(type='int', aliases=['end']), # Not required for querying all objects + range_name=dict(type='str', aliases=["name", "range"]), # Not required for querying all objects + range_start=dict(type='int', aliases=["start"]), # Not required for querying all objects + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['pool', 'range_end', 'range_name', 'range_start']], + ['state', 'present', ['pool', 'range_end', 'range_name', 'range_start']], + ], + ) + + allocation_mode = module.params.get('allocation_mode') + description = module.params.get('description') + pool = module.params.get('pool') + pool_allocation_mode = module.params.get('pool_allocation_mode') + pool_type = module.params.get('pool_type') + range_end = module.params.get('range_end') + range_name = module.params.get('range_name') + range_start = module.params.get('range_start') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + if range_end is not None: + encap_end = '{0}-{1}'.format(pool_type, range_end) + else: + encap_end = None + + if range_start is not None: + encap_start = '{0}-{1}'.format(pool_type, range_start) + else: + encap_start = None + + ACI_RANGE_MAPPING = dict( + vlan=dict( + aci_class='fvnsEncapBlk', + aci_mo='from-[{0}]-to-[{1}]'.format(encap_start, encap_end), + ), + vxlan=dict( + aci_class='fvnsEncapBlk', + aci_mo='from-[{0}]-to-[{1}]'.format(encap_start, encap_end), + ), + vsan=dict( + aci_class='fvnsVsanEncapBlk', + aci_mo='vsanfrom-[{0}]-to-[{1}]'.format(encap_start, encap_end), + ), + ) + + # Collect proper class and mo information based on pool_type + aci_range_class = ACI_RANGE_MAPPING[pool_type]["aci_class"] + aci_range_mo = ACI_RANGE_MAPPING[pool_type]["aci_mo"] + aci_pool_class = ACI_POOL_MAPPING[pool_type]["aci_class"] + aci_pool_mo = ACI_POOL_MAPPING[pool_type]["aci_mo"] + pool_name = pool + + # Validate range_end and range_start are valid for its respective encap type + for encap_id in range_end, range_start: + if encap_id is not None: + if pool_type == 'vlan': + if not 1 <= encap_id <= 4094: + module.fail_json(msg='vlan pools must have "range_start" and "range_end" values between 1 and 4094') + elif pool_type == 'vxlan': + if not 5000 <= encap_id <= 16777215: + module.fail_json(msg='vxlan pools must have "range_start" and "range_end" values between 5000 and 16777215') + elif pool_type == 'vsan': + if not 1 <= encap_id <= 4093: + module.fail_json(msg='vsan pools must have "range_start" and "range_end" values between 1 and 4093') + + if range_end is not None and range_start is not None: + # Validate range_start is less than range_end + if range_start > range_end: + module.fail_json(msg='The "range_start" must be less than or equal to the "range_end"') + + elif range_end is None and range_start is None: + if range_name is None: + # Reset range managed object to None for aci util to properly handle query + aci_range_mo = None + + # Vxlan does not support setting the allocation mode + if pool_type == 'vxlan' and allocation_mode is not None: + module.fail_json(msg='vxlan pools do not support setting the "allocation_mode"; please omit this parameter for vxlan pools') + + # ACI Pool URL requires the allocation mode for vlan and vsan pools (ex: uni/infra/vlanns-[poolname]-static) + if pool_type != 'vxlan' and pool is not None: + if pool_allocation_mode is not None: + pool_name = '[{0}]-{1}'.format(pool, pool_allocation_mode) + else: + module.fail_json(msg='ACI requires the "pool_allocation_mode" for "pool_type" of "vlan" and "vsan" when the "pool" is provided') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class=aci_pool_class, + aci_rn='{0}{1}'.format(aci_pool_mo, pool_name), + module_object=pool, + target_filter={'name': pool}, + ), + subclass_1=dict( + aci_class=aci_range_class, + aci_rn='{0}'.format(aci_range_mo), + module_object=aci_range_mo, + target_filter={'from': encap_start, 'to': encap_end, 'name': range_name}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class=aci_range_class, + class_config={ + "allocMode": allocation_mode, + "descr": description, + "from": encap_start, + "name": range_name, + "to": encap_end, + "nameAlias": name_alias, + }, + ) + + aci.get_diff(aci_class=aci_range_class) + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg.py new file mode 100644 index 00000000..986206a8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg.py @@ -0,0 +1,398 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_epg +short_description: Manage End Point Groups (EPG) objects (fv:AEPg) +description: +- Manage End Point Groups (EPG) on Cisco ACI fabrics. +options: + tenant: + description: + - Name of an existing tenant. + type: str + aliases: [ tenant_name ] + ap: + description: + - Name of an existing application network profile, that will contain the EPGs. + type: str + aliases: [ app_profile, app_profile_name ] + epg: + description: + - Name of the end point group. + type: str + aliases: [ epg_name, name ] + bd: + description: + - Name of the bridge domain being associated with the EPG. + type: str + aliases: [ bd_name, bridge_domain ] + priority: + description: + - The QoS class. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ level1, level2, level3, unspecified ] + intra_epg_isolation: + description: + - The Intra EPG Isolation. + - The APIC defaults to C(unenforced) when unset during creation. + type: str + choices: [ enforced, unenforced ] + description: + description: + - Description for the EPG. + type: str + aliases: [ descr ] + fwd_control: + description: + - The forwarding control used by the EPG. + - The APIC defaults to C(none) when unset during creation. + type: str + choices: [ none, proxy-arp ] + preferred_group: + description: + - Whether ot not the EPG is part of the Preferred Group and can communicate without contracts. + - This is very convenient for migration scenarios, or when ACI is used for network automation but not for policy. + - The APIC defaults to C(no) when unset during creation. + type: bool + monitoring_policy: + description: + - The name of the monitoring policy. + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) and C(app_profile) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_ap) modules can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- module: cisco.aci.aci_ap +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fv:AEPg). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Swetha Chunduri (@schunduri) +- Shreyas Srish (@shrsr) +''' + +EXAMPLES = r''' +- name: Add a new EPG + cisco.aci.aci_epg: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + ap: intranet + epg: web_epg + description: Web Intranet EPG + bd: prod_bd + monitoring_policy: default + preferred_group: yes + state: present + delegate_to: localhost + +- aci_epg: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + ap: ticketing + epg: "{{ item.epg }}" + description: Ticketing EPG + bd: "{{ item.bd }}" + priority: unspecified + intra_epg_isolation: unenforced + state: present + delegate_to: localhost + with_items: + - epg: web + bd: web_bd + - epg: database + bd: database_bd + +- name: Remove an EPG + cisco.aci.aci_epg: + host: apic + username: admin + password: SomeSecretPassword + validate_certs: no + tenant: production + app_profile: intranet + epg: web_epg + monitoring_policy: default + state: absent + delegate_to: localhost + +- name: Query an EPG + cisco.aci.aci_epg: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + ap: ticketing + epg: web_epg + state: query + delegate_to: localhost + register: query_result + +- name: Query all EPGs + cisco.aci.aci_epg: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result + +- name: Query all EPGs with a Specific Name + cisco.aci.aci_epg: + host: apic + username: admin + password: SomeSecretPassword + validate_certs: no + epg: web_epg + state: query + delegate_to: localhost + register: query_result + +- name: Query all EPGs of an App Profile + cisco.aci.aci_epg: + host: apic + username: admin + password: SomeSecretPassword + validate_certs: no + ap: ticketing + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + epg=dict(type='str', aliases=['epg_name', 'name']), # Not required for querying all objects + bd=dict(type='str', aliases=['bd_name', 'bridge_domain']), + ap=dict(type='str', aliases=['app_profile', 'app_profile_name']), # Not required for querying all objects + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + priority=dict(type='str', choices=['level1', 'level2', 'level3', 'unspecified']), + intra_epg_isolation=dict(choices=['enforced', 'unenforced']), + fwd_control=dict(type='str', choices=['none', 'proxy-arp']), + preferred_group=dict(type='bool'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + monitoring_policy=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['ap', 'epg', 'tenant']], + ['state', 'present', ['ap', 'epg', 'tenant']], + ], + ) + + aci = ACIModule(module) + + epg = module.params.get('epg') + bd = module.params.get('bd') + description = module.params.get('description') + priority = module.params.get('priority') + intra_epg_isolation = module.params.get('intra_epg_isolation') + fwd_control = module.params.get('fwd_control') + preferred_group = aci.boolean(module.params.get('preferred_group'), 'include', 'exclude') + state = module.params.get('state') + tenant = module.params.get('tenant') + ap = module.params.get('ap') + name_alias = module.params.get('name_alias') + monitoring_policy = module.params.get('monitoring_policy') + + child_configs = [ + dict(fvRsBd=dict(attributes=dict(tnFvBDName=bd))), + dict(fvRsAEPgMonPol=dict(attributes=dict(tnMonEPGPolName=monitoring_policy))) + ] + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='fvAp', + aci_rn='ap-{0}'.format(ap), + module_object=ap, + target_filter={'name': ap}, + ), + subclass_2=dict( + aci_class='fvAEPg', + aci_rn='epg-{0}'.format(epg), + module_object=epg, + target_filter={'name': epg}, + ), + child_classes=['fvRsBd', 'fvRsAEPgMonPol'], + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fvAEPg', + class_config=dict( + name=epg, + descr=description, + prio=priority, + pcEnfPref=intra_epg_isolation, + fwdCtrl=fwd_control, + prefGrMemb=preferred_group, + nameAlias=name_alias, + ), + child_configs=child_configs, + ) + + aci.get_diff(aci_class='fvAEPg') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg_monitoring_policy.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg_monitoring_policy.py new file mode 100644 index 00000000..aaef8e37 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg_monitoring_policy.py @@ -0,0 +1,246 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_epg_monitoring_policy +short_description: Manage monitoring policies (mon:EPGPol) +description: +- Manage monitoring policies on Cisco ACI fabrics. +options: + monitoring_policy: + description: + - The name of the monitoring policy. + type: str + aliases: [ name ] + description: + description: + - Description for the monitoring policy. + type: str + aliases: [ descr ] + tenant: + description: + - The name of the tenant. + type: str + aliases: [ tenant_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(mon:EPGPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- cisco.aci.aci_epg_monitoring_policy: + host: '{{ hostname }}' + username: '{{ username }}' + password: '{{ password }}' + monitoring_policy: '{{ monitoring_policy }}' + description: '{{ description }}' + tenant: '{{ tenant }}' + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + monitoring_policy=dict(type='str', aliases=['name']), # Not required for querying all objects + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['monitoring_policy', 'tenant']], + ['state', 'present', ['monitoring_policy', 'tenant']], + ], + ) + + monitoring_policy = module.params.get('monitoring_policy') + description = module.params.get('description') + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='monEPGPol', + aci_rn='monepg-{0}'.format(monitoring_policy), + module_object=monitoring_policy, + target_filter={'name': monitoring_policy}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='monEPGPol', + class_config=dict( + name=monitoring_policy, + descr=description, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='monEPGPol') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract.py new file mode 100644 index 00000000..45be2dd1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract.py @@ -0,0 +1,351 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_epg_to_contract +short_description: Bind EPGs to Contracts (fv:RsCons, fv:RsProv) +description: +- Bind EPGs to Contracts on Cisco ACI fabrics. +notes: +- The C(tenant), C(app_profile), C(EPG), and C(Contract) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant), M(cisco.aci.aci_ap), M(cisco.aci.aci_epg), and M(cisco.aci.aci_contract) modules can be used for this. +options: + ap: + description: + - Name of an existing application network profile, that will contain the EPGs. + type: str + aliases: [ app_profile, app_profile_name ] + contract: + description: + - The name of the contract. + type: str + aliases: [ contract_name ] + contract_type: + description: + - Determines if the EPG should Provide or Consume the Contract. + type: str + required: yes + choices: [ consumer, provider ] + epg: + description: + - The name of the end point group. + type: str + aliases: [ epg_name ] + priority: + description: + - QoS class. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ level1, level2, level3, unspecified ] + provider_match: + description: + - The matching algorithm for Provided Contracts. + - The APIC defaults to C(at_least_one) when unset during creation. + type: str + choices: [ all, at_least_one, at_most_one, none ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + tenant: + description: + - Name of an existing tenant. + type: str + aliases: [ tenant_name ] +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- module: cisco.aci.aci_ap +- module: cisco.aci.aci_epg +- module: cisco.aci.aci_contract +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(fv:RsCons) and B(fv:RsProv). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +EXAMPLES = r''' +- name: Add a new contract to EPG binding + cisco.aci.aci_epg_to_contract: + host: apic + username: admin + password: SomeSecretPassword + tenant: anstest + ap: anstest + epg: anstest + contract: anstest_http + contract_type: provider + state: present + delegate_to: localhost + +- name: Remove an existing contract to EPG binding + cisco.aci.aci_epg_to_contract: + host: apic + username: admin + password: SomeSecretPassword + tenant: anstest + ap: anstest + epg: anstest + contract: anstest_http + contract_type: provider + state: absent + delegate_to: localhost + +- name: Query a specific contract to EPG binding + cisco.aci.aci_epg_to_contract: + host: apic + username: admin + password: SomeSecretPassword + tenant: anstest + ap: anstest + epg: anstest + contract: anstest_http + contract_type: provider + state: query + delegate_to: localhost + register: query_result + +- name: Query all provider contract to EPG bindings + cisco.aci.aci_epg_to_contract: + host: apic + username: admin + password: SomeSecretPassword + contract_type: provider + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +ACI_CLASS_MAPPING = dict( + consumer={ + 'class': 'fvRsCons', + 'rn': 'rscons-', + }, + provider={ + 'class': 'fvRsProv', + 'rn': 'rsprov-', + }, +) + +PROVIDER_MATCH_MAPPING = dict( + all='All', + at_least_one='AtleastOne', + at_most_one='AtmostOne', + none='None', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + contract_type=dict(type='str', required=True, choices=['consumer', 'provider']), + ap=dict(type='str', aliases=['app_profile', 'app_profile_name']), # Not required for querying all objects + epg=dict(type='str', aliases=['epg_name']), # Not required for querying all objects + contract=dict(type='str', aliases=['contract_name']), # Not required for querying all objects + priority=dict(type='str', choices=['level1', 'level2', 'level3', 'unspecified']), + provider_match=dict(type='str', choices=['all', 'at_least_one', 'at_most_one', 'none']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['ap', 'contract', 'epg', 'tenant']], + ['state', 'present', ['ap', 'contract', 'epg', 'tenant']], + ], + ) + + ap = module.params.get('ap') + contract = module.params.get('contract') + contract_type = module.params.get('contract_type') + epg = module.params.get('epg') + priority = module.params.get('priority') + provider_match = module.params.get('provider_match') + if provider_match is not None: + provider_match = PROVIDER_MATCH_MAPPING[provider_match] + state = module.params.get('state') + tenant = module.params.get('tenant') + + aci_class = ACI_CLASS_MAPPING[contract_type]["class"] + aci_rn = ACI_CLASS_MAPPING[contract_type]["rn"] + + if contract_type == "consumer" and provider_match is not None: + module.fail_json(msg="the 'provider_match' is only configurable for Provided Contracts") + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='fvAp', + aci_rn='ap-{0}'.format(ap), + module_object=ap, + target_filter={'name': ap}, + ), + subclass_2=dict( + aci_class='fvAEPg', + aci_rn='epg-{0}'.format(epg), + module_object=epg, + target_filter={'name': epg}, + ), + subclass_3=dict( + aci_class=aci_class, + aci_rn='{0}{1}'.format(aci_rn, contract), + module_object=contract, + target_filter={'tnVzBrCPName': contract}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class=aci_class, + class_config=dict( + matchT=provider_match, + prio=priority, + tnVzBrCPName=contract, + ), + ) + + aci.get_diff(aci_class=aci_class) + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract_master.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract_master.py new file mode 100644 index 00000000..d2368f7d --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract_master.py @@ -0,0 +1,302 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_epg_to_contract_master +short_description: Manage End Point Group (EPG) contract master relationships (fv:RsSecInherited) +description: +- Manage End Point Groups (EPG) contract master relationships on Cisco ACI fabrics. +options: + tenant: + description: + - Name of an existing tenant. + type: str + aliases: [ tenant_name ] + required: yes + ap: + description: + - Name of an existing application network profile, that will contain the EPGs. + type: str + required: yes + aliases: [ app_profile, app_profile_name ] + epg: + description: + - Name of the end point group. + type: str + required: yes + aliases: [ epg_name, name ] + contract_master_ap: + description: + - Name of the application profile where the contract master EPG is. + type: str + contract_master_epg: + description: + - Name of the end point group which serves as contract master. + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) and C(app_profile) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_ap) modules can be used for this. +seealso: +- module: cisco.aci.aci_epg +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fv:AEPg). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Cindy Zhao (@cizhao) +''' + +EXAMPLES = r''' +- name: Add contract master + cisco.aci.aci_epg_to_contract_master: + host: apic_host + username: admin + password: SomeSecretPassword + tenant: anstest + ap: apName + epg: epgName + contract_master_ap: ap + contract_master_epg: epg + state: present + delegate_to: localhost + +- name: Remove contract master + cisco.aci.aci_epg_to_contract_master: + host: apic_host + username: admin + password: SomeSecretPassword + tenant: anstest + ap: apName + epg: epgName + contract_master_ap: ap + contract_master_epg: epg + state: absent + delegate_to: localhost + +- name: Query contract master + cisco.aci.aci_epg_to_contract_master: + host: apic_host + username: admin + password: SomeSecretPassword + tenant: anstest + ap: apName + epg: epgName + contract_master_ap: ap + contract_master_epg: epg + state: query + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name'], required=True), + ap=dict(type='str', aliases=['app_profile', 'app_profile_name'], required=True), + epg=dict(type='str', aliases=['epg_name', 'name'], required=True), + contract_master_ap=dict(type='str'), + contract_master_epg=dict(type='str'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['contract_master_ap', 'contract_master_epg']], + ['state', 'present', ['contract_master_ap', 'contract_master_epg']], + ], + ) + + aci = ACIModule(module) + + tenant = module.params.get('tenant') + ap = module.params.get('ap') + epg = module.params.get('epg') + contract_master_ap = module.params.get('contract_master_ap') + contract_master_epg = module.params.get('contract_master_epg') + state = module.params.get('state') + + contract_master = 'uni/tn-{0}/ap-{1}/epg-{2}'.format(tenant, contract_master_ap, contract_master_epg) + + child_configs = [] + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='fvAp', + aci_rn='ap-{0}'.format(ap), + module_object=ap, + target_filter={'name': ap}, + ), + subclass_2=dict( + aci_class='fvAEPg', + aci_rn='epg-{0}'.format(epg), + module_object=epg, + target_filter={'name': epg}, + ), + subclass_3=dict( + aci_class='fvRsSecInherited', + aci_rn='rssecInherited-[{0}]'.format(contract_master), + module_object=contract_master, + target_filter={'tDn': contract_master}, + ), + child_classes=[], + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fvRsSecInherited', + class_config=dict( + tDn=contract_master + ), + child_configs=child_configs + ) + + aci.get_diff(aci_class='fvRsSecInherited') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_domain.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_domain.py new file mode 100644 index 00000000..d4b8ea58 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_domain.py @@ -0,0 +1,441 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Jacob McGill <jmcgill298> +# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_epg_to_domain +short_description: Bind EPGs to Domains (fv:RsDomAtt) +description: +- Bind EPGs to Physical and Virtual Domains on Cisco ACI fabrics. +options: + allow_useg: + description: + - Allows micro-segmentation. + - The APIC defaults to C(encap) when unset during creation. + type: str + choices: [ encap, useg ] + ap: + description: + - Name of an existing application network profile, that will contain the EPGs. + type: str + aliases: [ app_profile, app_profile_name ] + deploy_immediacy: + description: + - Determines when the policy is pushed to hardware Policy CAM. + - The APIC defaults to C(lazy) when unset during creation. + type: str + choices: [ immediate, lazy ] + domain: + description: + - Name of the physical or virtual domain being associated with the EPG. + type: str + aliases: [ domain_name, domain_profile ] + domain_type: + description: + - Specify whether the Domain is a physical (phys), a virtual (vmm) or an L2 external domain association (l2dom). + type: str + choices: [ l2dom, phys, vmm ] + aliases: [ type ] + encap: + description: + - The VLAN encapsulation for the EPG when binding a VMM Domain with static C(encap_mode). + - This acts as the secondary encap when using useg. + - Accepted values range between C(1) and C(4096). + type: int + encap_mode: + description: + - The encapsulation method to be used. + - The APIC defaults to C(auto) when unset during creation. + - If vxlan is selected, switching_mode must be "AVE". + type: str + choices: [ auto, vlan, vxlan ] + switching_mode: + description: + - Switching Mode used by the switch + type: str + choices: [ AVE, native ] + default: native + epg: + description: + - Name of the end point group. + type: str + aliases: [ epg_name, name ] + netflow: + description: + - Determines if netflow should be enabled. + - The APIC defaults to C(no) when unset during creation. + type: bool + primary_encap: + description: + - Determines the primary VLAN ID when using useg. + - Accepted values range between C(1) and C(4096). + type: int + resolution_immediacy: + description: + - Determines when the policies should be resolved and available. + - The APIC defaults to C(lazy) when unset during creation. + type: str + choices: [ immediate, lazy, pre-provision ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + tenant: + description: + - Name of an existing tenant. + type: str + aliases: [ tenant_name ] + promiscuous: + description: + - Allow/Disallow promiscuous mode in vmm domain + type: str + choices: [ accept, reject ] + default: reject + vm_provider: + description: + - The VM platform for VMM Domains. + - Support for Kubernetes was added in ACI v3.0. + - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1. + type: str + choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ] +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant), C(ap), C(epg), and C(domain) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) M(cisco.aci.aci_ap), M(cisco.aci.aci_epg) M(cisco.aci.aci_domain) modules can be used for this. +- OpenStack VMM domains must not be created using this module. The OpenStack VMM domain is created directly + by the Cisco APIC Neutron plugin as part of the installation and configuration. + This module can be used to query status of an OpenStack VMM domain. +seealso: +- module: cisco.aci.aci_ap +- module: cisco.aci.aci_epg +- module: cisco.aci.aci_domain +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fv:RsDomAtt). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +- Shreyas Srish (@shrsr) +''' + +EXAMPLES = r''' +- name: Add a new physical domain to EPG binding + cisco.aci.aci_epg_to_domain: + host: apic + username: admin + password: SomeSecretPassword + tenant: anstest + ap: anstest + epg: anstest + domain: anstest + domain_type: phys + state: present + delegate_to: localhost + +- name: Remove an existing physical domain to EPG binding + cisco.aci.aci_epg_to_domain: + host: apic + username: admin + password: SomeSecretPassword + tenant: anstest + ap: anstest + epg: anstest + domain: anstest + domain_type: phys + state: absent + delegate_to: localhost + +- name: Query a specific physical domain to EPG binding + cisco.aci.aci_epg_to_domain: + host: apic + username: admin + password: SomeSecretPassword + tenant: anstest + ap: anstest + epg: anstest + domain: anstest + domain_type: phys + state: query + delegate_to: localhost + register: query_result + +- name: Query all domain to EPG bindings + cisco.aci.aci_epg_to_domain: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +VM_PROVIDER_MAPPING = dict( + cloudfoundry='CloudFoundry', + kubernetes='Kubernetes', + microsoft='Microsoft', + openshift='OpenShift', + openstack='OpenStack', + redhat='Redhat', + vmware='VMware', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + allow_useg=dict(type='str', choices=['encap', 'useg']), + ap=dict(type='str', aliases=['app_profile', 'app_profile_name']), # Not required for querying all objects + deploy_immediacy=dict(type='str', choices=['immediate', 'lazy']), + domain=dict(type='str', aliases=['domain_name', 'domain_profile']), # Not required for querying all objects + domain_type=dict(type='str', choices=['l2dom', 'phys', 'vmm'], aliases=['type']), # Not required for querying all objects + encap=dict(type='int'), + encap_mode=dict(type='str', choices=['auto', 'vlan', 'vxlan']), + switching_mode=dict(type='str', default='native', choices=['AVE', 'native']), + epg=dict(type='str', aliases=['name', 'epg_name']), # Not required for querying all objects + netflow=dict(type='bool'), + primary_encap=dict(type='int'), + resolution_immediacy=dict(type='str', choices=['immediate', 'lazy', 'pre-provision']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + vm_provider=dict(type='str', choices=['cloudfoundry', 'kubernetes', 'microsoft', 'openshift', 'openstack', 'redhat', 'vmware']), + promiscuous=dict(type='str', default='reject', choices=['accept', 'reject']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['domain_type', 'vmm', ['vm_provider']], + ['state', 'absent', ['ap', 'domain', 'domain_type', 'epg', 'tenant']], + ['state', 'present', ['ap', 'domain', 'domain_type', 'epg', 'tenant']], + ], + ) + + aci = ACIModule(module) + + allow_useg = module.params.get('allow_useg') + ap = module.params.get('ap') + deploy_immediacy = module.params.get('deploy_immediacy') + domain = module.params.get('domain') + domain_type = module.params.get('domain_type') + vm_provider = module.params.get('vm_provider') + promiscuous = module.params.get('promiscuous') + encap = module.params.get('encap') + if encap is not None: + if encap in range(1, 4097): + encap = 'vlan-{0}'.format(encap) + else: + module.fail_json(msg='Valid VLAN assignments are from 1 to 4096') + encap_mode = module.params.get('encap_mode') + switching_mode = module.params.get('switching_mode') + epg = module.params.get('epg') + netflow = aci.boolean(module.params.get('netflow'), 'enabled', 'disabled') + primary_encap = module.params.get('primary_encap') + if primary_encap is not None: + if primary_encap in range(1, 4097): + primary_encap = 'vlan-{0}'.format(primary_encap) + else: + module.fail_json(msg='Valid VLAN assignments are from 1 to 4096') + resolution_immediacy = module.params.get('resolution_immediacy') + state = module.params.get('state') + tenant = module.params.get('tenant') + + if domain_type in ['l2dom', 'phys'] and vm_provider is not None: + module.fail_json(msg="Domain type '%s' cannot have a 'vm_provider'" % domain_type) + + child_classes = None + child_configs = None + + # Compile the full domain for URL building + if domain_type == 'vmm': + epg_domain = 'uni/vmmp-{0}/dom-{1}'.format(VM_PROVIDER_MAPPING[vm_provider], domain) + child_configs = [dict(vmmSecP=dict(attributes=dict(allowPromiscuous=promiscuous)))] + child_classes = ['vmmSecP'] + elif domain_type == 'l2dom': + epg_domain = 'uni/l2dom-{0}'.format(domain) + elif domain_type == 'phys': + epg_domain = 'uni/phys-{0}'.format(domain) + else: + epg_domain = None + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='fvAp', + aci_rn='ap-{0}'.format(ap), + module_object=ap, + target_filter={'name': ap}, + ), + subclass_2=dict( + aci_class='fvAEPg', + aci_rn='epg-{0}'.format(epg), + module_object=epg, + target_filter={'name': epg}, + ), + subclass_3=dict( + aci_class='fvRsDomAtt', + aci_rn='rsdomAtt-[{0}]'.format(epg_domain), + module_object=epg_domain, + target_filter={'tDn': epg_domain}, + ), + child_classes=child_classes, + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fvRsDomAtt', + class_config=dict( + classPref=allow_useg, + encap=encap, + encapMode=encap_mode, + switchingMode=switching_mode, + instrImedcy=deploy_immediacy, + netflowPref=netflow, + primaryEncap=primary_encap, + resImedcy=resolution_immediacy, + ), + child_configs=child_configs, + ) + + aci.get_diff(aci_class='fvRsDomAtt') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_fabric_node.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_fabric_node.py new file mode 100644 index 00000000..b1ea5f91 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_fabric_node.py @@ -0,0 +1,288 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_fabric_node +short_description: Manage Fabric Node Members (fabric:NodeIdentP) +description: +- Manage Fabric Node Members on Cisco ACI fabrics. +options: + pod_id: + description: + - The pod id of the new Fabric Node Member. + type: int + serial: + description: + - Serial Number for the new Fabric Node Member. + type: str + aliases: [ serial_number ] + node_id: + description: + - Node ID Number for the new Fabric Node Member. + type: int + switch: + description: + - Switch Name for the new Fabric Node Member. + type: str + aliases: [ name, switch_name ] + description: + description: + - Description for the new Fabric Node Member. + type: str + aliases: [ descr ] + role: + description: + - Role for the new Fabric Node Member. + type: str + aliases: [ role_name ] + choices: [ leaf, spine, unspecified ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fabric:NodeIdentP). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Bruno Calogero (@brunocalogero) +''' + +EXAMPLES = r''' +- name: Add fabric node + cisco.aci.aci_fabric_node: + host: apic + username: admin + password: SomeSecretPassword + serial: FDO2031124L + node_id: 1011 + switch: fab4-sw1011 + state: present + delegate_to: localhost + +- name: Remove fabric node + cisco.aci.aci_fabric_node: + host: apic + username: admin + password: SomeSecretPassword + serial: FDO2031124L + node_id: 1011 + state: absent + delegate_to: localhost + +- name: Query fabric nodes + cisco.aci.aci_fabric_node: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: '?rsp-prop-include=config-only' +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +# NOTE: (This problem is also present on the APIC GUI) +# NOTE: When specifying a C(role) the new Fabric Node Member will be created but Role on GUI will be "unknown", hence not what seems to be a module problem + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + description=dict(type='str', aliases=['descr']), + node_id=dict(type='int'), # Not required for querying all objects + pod_id=dict(type='int'), + role=dict(type='str', choices=['leaf', 'spine', 'unspecified'], aliases=['role_name']), + serial=dict(type='str', aliases=['serial_number']), # Not required for querying all objects + switch=dict(type='str', aliases=['name', 'switch_name']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['node_id', 'serial']], + ['state', 'present', ['node_id', 'serial']], + ], + ) + + pod_id = module.params.get('pod_id') + serial = module.params.get('serial') + node_id = module.params.get('node_id') + switch = module.params.get('switch') + description = module.params.get('description') + role = module.params.get('role') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fabricNodeIdentP', + aci_rn='controller/nodeidentpol/nodep-{0}'.format(serial), + module_object=serial, + target_filter={'serial': serial}, + ) + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fabricNodeIdentP', + class_config=dict( + descr=description, + name=switch, + nodeId=node_id, + podId=pod_id, + # NOTE: Originally we were sending 'rn', but now we need 'dn' for idempotency + # FIXME: Did this change with ACI version ? + dn='uni/controller/nodeidentpol/nodep-{0}'.format(serial), + # rn='nodep-{0}'.format(serial), + role=role, + serial=serial, + nameAlias=name_alias, + ) + ) + + aci.get_diff(aci_class='fabricNodeIdentP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json(**aci.result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_fabric_scheduler.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_fabric_scheduler.py new file mode 100644 index 00000000..ecc91330 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_fabric_scheduler.py @@ -0,0 +1,331 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +module: aci_fabric_scheduler + +short_description: This modules creates ACI schedulers. + + +description: + - With the module you can create schedule policies that can be a shell, onetime execution or recurring + +options: + name: + description: + - The name of the Scheduler. + type: str + aliases: [ scheduler_name ] + description: + description: + - Description for the Scheduler. + type: str + aliases: [ descr ] + recurring: + description: + - If you want to make the Scheduler a recurring it would be a "True" and for a + oneTime execution it would be "False". For a shell just exclude this option from + the task + type: bool + default: 'no' + windowname: + description: + - This is the name for your what recurring or oneTime execution + type: str + concurCap: + description: + - This is the amount of devices that can be executed on at a time + type: int + maxTime: + description: + - This is the amount MAX amount of time a process can be executed + type: str + date: + description: + - This is the date and time that the scheduler will execute + type: str + hour: + description: + - This set the hour of execution + type: int + minute: + description: + - This sets the minute of execution, used in conjunction with hour + type: int + day: + description: + - This sets the day when execution will take place + type: str + default: "every-day" + choices: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday','Sunday', 'even-day', 'odd-day', 'every-day'] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + default: present + choices: [ absent, present, query ] + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + + +author: + - Steven Gerhart (@sgerhart) +''' + +EXAMPLES = r''' + - name: Simple Scheduler (Empty) + cisco.aci.aci_fabric_scheduler: + host: "{{ inventory_hostname }}" + username: "{{ user }}" + password: "{{ pass }}" + validate_certs: no + name: simpleScheduler + state: present + - name: Remove Simple Scheduler + cisco.aci.aci_fabric_scheduler: + host: "{{ inventory_hostname }}" + username: "{{ user }}" + password: "{{ pass }}" + validate_certs: no + name: simpleScheduler + state: absent + - name: One Time Scheduler + cisco.aci.aci_fabric_scheduler: + host: "{{ inventory_hostname }}" + username: "{{ user }}" + password: "{{ pass }}" + validate_certs: no + name: OneTime + windowname: OneTime + recurring: False + concurCap: 20 + date: "2018-11-20T24:00:00" + state: present + - name: Recurring Scheduler + cisco.aci.aci_fabric_scheduler: + host: "{{ inventory_hostname }}" + username: "{{ user }}" + password: "{{ pass }}" + validate_certs: no + name: Recurring + windowname: Recurring + recurring: True + concurCap: 20 + hour: 13 + minute: 30 + day: Tuesday + state: present +''' + +RETURN = ''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + name=dict(type='str', aliases=['scheduler_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + windowname=dict(type='str'), + recurring=dict(type='bool'), + concurCap=dict(type='int'), # Number of devices it will run against concurrently + maxTime=dict(type='str'), # The amount of minutes a process will be able to run (unlimited or dd:hh:mm:ss) + date=dict(type='str'), # The date the process will run YYYY-MM-DDTHH:MM:SS + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + hour=dict(type='int'), + minute=dict(type='int'), + day=dict(type='str', default='every-day', choices=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', + 'Saturday', 'Sunday', 'every-day', 'even-day', 'odd-day']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['name']], + ['state', 'present', ['name']], + ], + ) + + state = module.params.get('state') + name = module.params.get('name') + windowname = module.params.get('windowname') + recurring = module.params.get('recurring') + date = module.params.get('date') + hour = module.params.get('hour') + minute = module.params.get('minute') + maxTime = module.params.get('maxTime') + concurCap = module.params.get('concurCap') + day = module.params.get('day') + description = module.params.get('description') + name_alias = module.params.get('name_alias') + + if recurring: + child_configs = [dict(trigRecurrWindowP=dict(attributes=dict(name=windowname, hour=hour, minute=minute, + procCa=maxTime, concurCap=concurCap, day=day,)))] + elif recurring is False: + child_configs = [dict(trigAbsWindowP=dict(attributes=dict(name=windowname, procCap=maxTime, + concurCap=concurCap, date=date,)))] + else: + child_configs = [] + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='trigSchedP', + aci_rn='fabric/schedp-{0}'.format(name), + target_filter={'name': name}, + module_object=name, + ), + + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='trigSchedP', + class_config=dict( + name=name, + descr=description, + nameAlias=name_alias, + ), + child_configs=child_configs, + + ) + + aci.get_diff(aci_class='trigSchedP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_filter.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_filter.py new file mode 100644 index 00000000..9b79eae9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_filter.py @@ -0,0 +1,279 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_filter +short_description: Manages top level filter objects (vz:Filter) +description: +- Manages top level filter objects on Cisco ACI fabrics. +- This modules does not manage filter entries, see M(cisco.aci.aci_filter_entry) for this functionality. +options: + filter: + description: + - The name of the filter. + type: str + aliases: [ filter_name, name ] + description: + description: + - Description for the filter. + type: str + aliases: [ descr ] + tenant: + description: + - The name of the tenant. + type: str + aliases: [ tenant_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(vz:Filter). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Add a new filter to a tenant + cisco.aci.aci_filter: + host: apic + username: admin + password: SomeSecretPassword + filter: web_filter + description: Filter for web protocols + tenant: production + state: present + delegate_to: localhost + +- name: Remove a filter for a tenant + cisco.aci.aci_filter: + host: apic + username: admin + password: SomeSecretPassword + filter: web_filter + tenant: production + state: absent + delegate_to: localhost + +- name: Query a filter of a tenant + cisco.aci.aci_filter: + host: apic + username: admin + password: SomeSecretPassword + filter: web_filter + tenant: production + state: query + delegate_to: localhost + register: query_result + +- name: Query all filters for a tenant + cisco.aci.aci_filter: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + filter=dict(type='str', aliases=['name', 'filter_name']), # Not required for querying all objects + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['filter', 'tenant']], + ['state', 'present', ['filter', 'tenant']], + ], + ) + + filter_name = module.params.get('filter') + description = module.params.get('description') + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='vzFilter', + aci_rn='flt-{0}'.format(filter_name), + module_object=filter_name, + target_filter={'name': filter_name}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='vzFilter', + class_config=dict( + name=filter_name, + descr=description, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='vzFilter') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_filter_entry.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_filter_entry.py new file mode 100644 index 00000000..babf6d9d --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_filter_entry.py @@ -0,0 +1,376 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_filter_entry +short_description: Manage filter entries (vz:Entry) +description: +- Manage filter entries for a filter on Cisco ACI fabrics. +options: + arp_flag: + description: + - The arp flag to use when the ether_type is arp. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ arp_reply, arp_request, unspecified ] + description: + description: + - Description for the Filter Entry. + type: str + aliases: [ descr ] + dst_port: + description: + - Used to set both destination start and end ports to the same value when ip_protocol is tcp or udp. + - Accepted values are any valid TCP/UDP port range. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + dst_port_end: + description: + - Used to set the destination end port when ip_protocol is tcp or udp. + - Accepted values are any valid TCP/UDP port range. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + dst_port_start: + description: + - Used to set the destination start port when ip_protocol is tcp or udp. + - Accepted values are any valid TCP/UDP port range. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + entry: + description: + - Then name of the Filter Entry. + type: str + aliases: [ entry_name, filter_entry, name ] + ether_type: + description: + - The Ethernet type. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ arp, fcoe, ip, mac_security, mpls_ucast, trill, unspecified ] + filter: + description: + - The name of Filter that the entry should belong to. + type: str + aliases: [ filter_name ] + icmp_msg_type: + description: + - ICMPv4 message type; used when ip_protocol is icmp. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ dst_unreachable, echo, echo_reply, src_quench, time_exceeded, unspecified ] + icmp6_msg_type: + description: + - ICMPv6 message type; used when ip_protocol is icmpv6. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ dst_unreachable, echo_request, echo_reply, neighbor_advertisement, neighbor_solicitation, redirect, time_exceeded, unspecified ] + ip_protocol: + description: + - The IP Protocol type when ether_type is ip. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ eigrp, egp, icmp, icmpv6, igmp, igp, l2tp, ospfigp, pim, tcp, udp, unspecified ] + state: + description: + - present, absent, query + type: str + default: present + choices: [ absent, present, query ] + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str + stateful: + description: + - Determines the statefulness of the filter entry. + type: bool + tenant: + description: + - The name of the tenant. + type: str + aliases: [ tenant_name ] +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) and C(filter) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_filter) modules can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- module: cisco.aci.aci_filter +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(vz:Entry). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- cisco.aci.aci_filter_entry: + host: "{{ inventory_hostname }}" + username: "{{ user }}" + password: "{{ pass }}" + state: "{{ state }}" + entry: "{{ entry }}" + tenant: "{{ tenant }}" + ether_name: "{{ ether_name }}" + icmp_msg_type: "{{ icmp_msg_type }}" + filter: "{{ filter }}" + descr: "{{ descr }}" + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +VALID_ARP_FLAGS = ['arp_reply', 'arp_request', 'unspecified'] +VALID_ETHER_TYPES = ['arp', 'fcoe', 'ip', 'mac_security', 'mpls_ucast', 'trill', 'unspecified'] +VALID_ICMP_TYPES = ['dst_unreachable', 'echo', 'echo_reply', 'src_quench', 'time_exceeded', 'unspecified'] +VALID_ICMP6_TYPES = ['dst_unreachable', 'echo_request', 'echo_reply', 'neighbor_advertisement', + 'neighbor_solicitation', 'redirect', 'time_exceeded', 'unspecified'] +VALID_IP_PROTOCOLS = ['eigrp', 'egp', 'icmp', 'icmpv6', 'igmp', 'igp', 'l2tp', 'ospfigp', 'pim', 'tcp', 'udp', 'unspecified'] + +# mapping dicts are used to normalize the proposed data to what the APIC expects, which will keep diffs accurate +ARP_FLAG_MAPPING = dict(arp_reply='reply', arp_request='req', unspecified=None) +FILTER_PORT_MAPPING = {'443': 'https', '25': 'smtp', '80': 'http', '20': 'ftpData', '53': 'dns', '110': 'pop3', '554': 'rtsp'} +ICMP_MAPPING = {'dst_unreachable': 'dst-unreach', 'echo': 'echo', 'echo_reply': 'echo-rep', 'src_quench': 'src-quench', + 'time_exceeded': 'time-exceeded', 'unspecified': 'unspecified', 'echo-rep': 'echo-rep', 'dst-unreach': 'dst-unreach'} +ICMP6_MAPPING = dict(dst_unreachable='dst-unreach', echo_request='echo-req', echo_reply='echo-rep', neighbor_advertisement='nbr-advert', + neighbor_solicitation='nbr-solicit', redirect='redirect', time_exceeded='time-exceeded', unspecified='unspecified') + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + arp_flag=dict(type='str', choices=VALID_ARP_FLAGS), + description=dict(type='str', aliases=['descr']), + dst_port=dict(type='str'), + dst_port_end=dict(type='str'), + dst_port_start=dict(type='str'), + entry=dict(type='str', aliases=['entry_name', 'filter_entry', 'name']), # Not required for querying all objects + ether_type=dict(choices=VALID_ETHER_TYPES, type='str'), + filter=dict(type='str', aliases=['filter_name']), # Not required for querying all objects + icmp_msg_type=dict(type='str', choices=VALID_ICMP_TYPES), + icmp6_msg_type=dict(type='str', choices=VALID_ICMP6_TYPES), + ip_protocol=dict(choices=VALID_IP_PROTOCOLS, type='str'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + stateful=dict(type='bool'), + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['entry', 'filter', 'tenant']], + ['state', 'present', ['entry', 'filter', 'tenant']], + ], + ) + + aci = ACIModule(module) + + arp_flag = module.params.get('arp_flag') + if arp_flag is not None: + arp_flag = ARP_FLAG_MAPPING.get(arp_flag) + description = module.params.get('description') + dst_port = module.params.get('dst_port') + if FILTER_PORT_MAPPING.get(dst_port) is not None: + dst_port = FILTER_PORT_MAPPING.get(dst_port) + dst_end = module.params.get('dst_port_end') + if FILTER_PORT_MAPPING.get(dst_end) is not None: + dst_end = FILTER_PORT_MAPPING.get(dst_end) + dst_start = module.params.get('dst_port_start') + if FILTER_PORT_MAPPING.get(dst_start) is not None: + dst_start = FILTER_PORT_MAPPING.get(dst_start) + entry = module.params.get('entry') + ether_type = module.params.get('ether_type') + filter_name = module.params.get('filter') + icmp_msg_type = module.params.get('icmp_msg_type') + if icmp_msg_type is not None: + icmp_msg_type = ICMP_MAPPING.get(icmp_msg_type) + icmp6_msg_type = module.params.get('icmp6_msg_type') + if icmp6_msg_type is not None: + icmp6_msg_type = ICMP6_MAPPING.get(icmp6_msg_type) + ip_protocol = module.params.get('ip_protocol') + state = module.params.get('state') + stateful = aci.boolean(module.params.get('stateful')) + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + # validate that dst_port is not passed with dst_start or dst_end + if dst_port is not None and (dst_end is not None or dst_start is not None): + module.fail_json(msg="Parameter 'dst_port' cannot be used with 'dst_end' and 'dst_start'") + elif dst_port is not None: + dst_end = dst_port + dst_start = dst_port + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='vzFilter', + aci_rn='flt-{0}'.format(filter_name), + module_object=filter_name, + target_filter={'name': filter_name}, + ), + subclass_2=dict( + aci_class='vzEntry', + aci_rn='e-{0}'.format(entry), + module_object=entry, + target_filter={'name': entry}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='vzEntry', + class_config=dict( + arpOpc=arp_flag, + descr=description, + dFromPort=dst_start, + dToPort=dst_end, + etherT=ether_type, + icmpv4T=icmp_msg_type, + icmpv6T=icmp6_msg_type, + name=entry, + prot=ip_protocol, + stateful=stateful, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='vzEntry') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_firmware_group.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_firmware_group.py new file mode 100644 index 00000000..9c25c344 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_firmware_group.py @@ -0,0 +1,238 @@ +#!/usr/bin/python + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +module: aci_firmware_group + +short_description: This module creates a firmware group + + +description: + - This module creates a firmware group, so that you can apply firmware policy to nodes. +options: + group: + description: + - This the name of the firmware group + type: str + firmwarepol: + description: + - This is the name of the firmware policy, which was create by aci_firmware_policy. It is important that + - you use the same name as the policy created with aci_firmware_policy + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + default: present + choices: [ absent, present, query ] + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +author: + - Steven Gerhart (@sgerhart) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' + - name: firmware group + cisco.aci.aci_firmware_group: + host: "{{ inventory_hostname }}" + username: "{{ user }}" + password: "{{ pass }}" + validate_certs: no + group: testingfwgrp1 + firmwarepol: test2FrmPol + state: present +''' +RETURN = ''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + group=dict(type='str'), # Not required for querying all objects + firmwarepol=dict(type='str'), # Not required for querying all objects + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['group']], + ['state', 'present', ['group', 'firmwarepol']], + ], + ) + + state = module.params.get('state') + group = module.params.get('group') + firmwarepol = module.params.get('firmwarepol') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='firmwareFwGrp', + aci_rn='fabric/fwgrp-{0}'.format(group), + target_filter={'name': group}, + module_object=group, + ), + child_classes=['firmwareRsFwgrpp'], + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='firmwareFwGrp', + class_config=dict( + name=group, + nameAlias=name_alias, + ), + child_configs=[ + dict( + firmwareRsFwgrpp=dict( + attributes=dict( + tnFirmwareFwPName=firmwarepol, + ), + ), + ), + ], + + ) + + aci.get_diff(aci_class='firmwareFwGrp') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_firmware_group_node.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_firmware_group_node.py new file mode 100644 index 00000000..79a03fd7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_firmware_group_node.py @@ -0,0 +1,248 @@ +#!/usr/bin/python + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +module: aci_firmware_group_node + +short_description: This modules adds and remove nodes from the firmware group + + +description: + - This module addes/deletes a node to the firmware group. This modules assigns 1 node at a time. + +options: + group: + description: + - This is the name of the firmware group + type: str + node: + description: + - The node to be added to the firmware group - the value equals the NodeID + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + default: present + choices: [ absent, present, query ] + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + + +author: + - Steven Gerhart (@sgerhart) +''' + +EXAMPLES = r''' + - name: add firmware group node + cisco.aci.aci_firmware_group_node: + host: "{{ inventory_hostname }}" + username: "{{ user }}" + password: "{{ pass }}" + validate_certs: no + group: testingfwgrp + node: 1001 + state: present + - name: Remove firmware group node + cisco.aci.aci_firmware_group_node: + host: "{{ inventory_hostname }}" + username: "{{ user }}" + password: "{{ pass }}" + validate_certs: no + group: testingfwgrp + node: 1001 + state: absent +''' + +RETURN = ''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + group=dict(type='str'), # Not required for querying all objects + node=dict(type='str'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['node', 'group']], + ['state', 'present', ['node', 'group']], + ], + ) + + state = module.params.get('state') + group = module.params.get('group') + node = module.params.get('node') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='firmwareFwGrp', + aci_rn='fabric/fwgrp-{0}'.format(group), + target_filter={'name': group}, + module_object=group, + ), + subclass_1=dict( + aci_class='fabricNodeBlk', + aci_rn='nodeblk-blk{0}-{0}'.format(node), + target_filter={'name': node}, + module_object=node, + ), + + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fabricNodeBlk', + class_config=dict( + from_=node, + to_=node, + nameAlias=name_alias, + ), + + + ) + + aci.get_diff(aci_class='fabricNodeBlk') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_firmware_policy.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_firmware_policy.py new file mode 100644 index 00000000..5c466ddc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_firmware_policy.py @@ -0,0 +1,253 @@ +#!/usr/bin/python + + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +module: aci_firmware_policy + +short_description: This creates a firmware policy + + +description: + - This module creates a firmware policy for firmware groups. The firmware policy is create first and then + - referenced by the firmware group. You will assign the firmware and specify if you want to ignore the compatibility + - check +options: + name: + description: + - Name of the firmware policy + type: str + version: + description: + - The version of the firmware associated with this policy. This value is very import as well as constructing + - it correctly. The syntax for this field is n9000-xx.x. If you look at the firmware repository using the UI + - each version will have a "Full Version" column, this is the value you need to use. So, if the Full Version + - is 13.1(1i), the value for this field would be n9000-13.1(1i) + type: str + ignoreCompat: + description: + - Check if compatibility checks should be ignored + type: bool + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [absent, present, query] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + + +author: + - Steven Gerhart (@sgerhart) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' + - name: firmware policy + cisco.aci.aci_firmware_policy: + host: "{{ inventory_hostname }}" + username: "{{ user }}" + password: "{{ pass }}" + validate_certs: no + name: test2FrmPol + version: n9000-13.2(1m) + ignoreCompat: False + state: present + +''' + +RETURN = ''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + name=dict(type='str'), # Not required for querying all objects + version=dict(type='str'), + ignoreCompat=dict(type='bool'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['name']], + ['state', 'present', ['name', 'version']], + ], + ) + + state = module.params.get('state') + name = module.params.get('name') + version = module.params.get('version') + name_alias = module.params.get('name_alias') + + if module.params.get('ignoreCompat'): + ignore = 'yes' + else: + ignore = 'no' + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='firmwareFwP', + aci_rn='fabric/fwpol-{0}'.format(name), + target_filter={'name': name}, + module_object=name, + ), + + + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='firmwareFwP', + class_config=dict( + name=name, + version=version, + ignoreCompat=ignore, + nameAlias=name_alias, + ), + + ) + + aci.get_diff(aci_class='firmwareFwP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_firmware_source.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_firmware_source.py new file mode 100644 index 00000000..52946578 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_firmware_source.py @@ -0,0 +1,303 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com> +# Copyright: (c) 2020, Cindy Zhao (cizhao) <cizhao@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_firmware_source +short_description: Manage firmware image sources (firmware:OSource) +description: +- Manage firmware image sources on Cisco ACI fabrics. +options: + source: + description: + - The identifying name for the outside source of images, such as an HTTP or SCP server. + type: str + aliases: [ name, source_name ] + polling_interval: + description: + - Polling interval in minutes. + type: int + url_protocol: + description: + - The Firmware download protocol. + type: str + choices: [ http, local, scp, usbkey ] + default: scp + aliases: [ url_proto ] + url: + description: + The firmware URL for the image(s) on the source. + type: str + url_password: + description: + The Firmware password or key string. + type: str + url_username: + description: + The username for the source. + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(firmware:OSource). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Add firmware source + cisco.aci.aci_firmware_source: + host: apic + username: admin + password: SomeSecretPassword + source: aci-msft-pkg-3.1.1i.zip + url: foo.bar.cisco.com/download/cisco/aci/aci-msft-pkg-3.1.1i.zip + url_protocol: http + state: present + delegate_to: localhost + +- name: Remove firmware source + cisco.aci.aci_firmware_source: + host: apic + username: admin + password: SomeSecretPassword + source: aci-msft-pkg-3.1.1i.zip + state: absent + delegate_to: localhost + +- name: Query a specific firmware source + cisco.aci.aci_firmware_source: + host: apic + username: admin + password: SomeSecretPassword + source: aci-msft-pkg-3.1.1i.zip + state: query + delegate_to: localhost + register: query_result + +- name: Query all firmware sources + cisco.aci.aci_firmware_source: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + source=dict(type='str', aliases=['name', 'source_name']), # Not required for querying all objects + polling_interval=dict(type='int'), + url=dict(type='str'), + url_username=dict(type='str'), + url_password=dict(type='str', no_log=True), + url_protocol=dict(type='str', default='scp', choices=['http', 'local', 'scp', 'usbkey'], aliases=['url_proto']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['source']], + ['state', 'present', ['url_protocol', 'source', 'url']], + ], + ) + + polling_interval = module.params.get('polling_interval') + url_protocol = module.params.get('url_protocol') + state = module.params.get('state') + source = module.params.get('source') + url = module.params.get('url') + url_password = module.params.get('url_password') + url_username = module.params.get('url_username') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fabricInst', + aci_rn='fabric', + module_object='fabric', + target_filter={'name': 'fabric'}, + ), + subclass_1=dict( + aci_class='firmwareRepoP', + aci_rn='fwrepop', + module_object='fwrepop', + target_filter={'name': 'fwrepop'}, + ), + subclass_2=dict( + aci_class='firmwareOSource', + aci_rn='osrc-{0}'.format(source), + module_object=source, + target_filter={'name': source}, + ), + config_only=False, + ) + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='firmwareOSource', + class_config=dict( + name=source, + url=url, + password=url_password, + pollingInterval=polling_interval, + proto=url_protocol, + user=url_username, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='firmwareOSource') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_cdp.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_cdp.py new file mode 100644 index 00000000..635c1799 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_cdp.py @@ -0,0 +1,266 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Tim Knipper <tim.knipper@gmail.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = r''' +--- +module: aci_interface_policy_cdp +short_description: Manage CDP interface policies (cdp:IfPol) +description: +- Manage CDP interface policies on Cisco ACI fabrics. +options: + cdp_policy: + description: + - The CDP interface policy name. + type: str + aliases: [ cdp_interface, name ] + description: + description: + - The description for the CDP interface policy name. + type: str + aliases: [ descr ] + admin_state: + description: + - Enable or Disable CDP state. + - The APIC defaults to C(yes) when unset during creation. + type: bool + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(cdp:IfPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Tim Knipper (@tknipper11) +''' + +EXAMPLES = r''' +- name: Create CDP Interface Policy to enable CDP + cisco.aci.aci_interface_policy_cdp: + name: Ansible_CDP_Interface_Policy + host: apic.example.com + username: admin + password: adminpass + admin_state: true + state: present + +- name: Create CDP Interface Policy to disable CDP + cisco.aci.aci_interface_policy_cdp: + name: Ansible_CDP_Interface_Policy + host: apic.example.com + username: admin + password: adminpass + admin_state: false + state: present + +- name: Remove CDP Interface Policy + cisco.aci.aci_interface_policy_cdp: + name: Ansible_CDP_Interface_Policy + host: apic.example.com + username: admin + password: adminpass + output_level: debug + state: absent + +- name: Query CDP Policy + cisco.aci.aci_interface_policy_cdp: + host: apic.example.com + username: admin + password: adminpass + state: query +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "cdpIfPol": { + "attributes": { + "adminSt": "disabled", + "annotation": "", + "descr": "Ansible Created CDP Test Policy", + "dn": "uni/infra/cdpIfP-Ansible_CDP_Test_Policy", + "name": "Ansible_CDP_Test_Policy", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + cdp_policy=dict(type='str', required=False, aliases=['cdp_interface', 'name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + admin_state=dict(type='bool'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['cdp_policy']], + ['state', 'present', ['cdp_policy']], + ], + ) + + aci = ACIModule(module) + + cdp_policy = module.params.get('cdp_policy') + description = module.params.get('description') + admin_state = aci.boolean(module.params.get('admin_state'), 'enabled', 'disabled') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci.construct_url( + root_class=dict( + aci_class='cdpIfPol', + aci_rn='infra/cdpIfP-{0}'.format(cdp_policy), + module_object=cdp_policy, + target_filter={'name': cdp_policy}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='cdpIfPol', + class_config=dict( + name=cdp_policy, + descr=description, + adminSt=admin_state, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='cdpIfPol') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_fc.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_fc.py new file mode 100644 index 00000000..d2eb1595 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_fc.py @@ -0,0 +1,240 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_interface_policy_fc +short_description: Manage Fibre Channel interface policies (fc:IfPol) +description: +- Manage ACI Fiber Channel interface policies on Cisco ACI fabrics. +options: + fc_policy: + description: + - The name of the Fiber Channel interface policy. + type: str + aliases: [ name ] + description: + description: + - The description of the Fiber Channel interface policy. + type: str + aliases: [ descr ] + port_mode: + description: + - The Port Mode to use. + - The APIC defaults to C(f) when unset during creation. + type: str + choices: [ f, np ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fc:IfPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- name: Add a Fibre Channel interface policy + cisco.aci.aci_interface_policy_fc: + host: '{{ hostname }}' + username: '{{ username }}' + password: '{{ password }}' + fc_policy: '{{ fc_policy }}' + port_mode: '{{ port_mode }}' + description: '{{ description }}' + state: present + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + fc_policy=dict(type='str', aliases=['name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + port_mode=dict(type='str', choices=['f', 'np']), # No default provided on purpose + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['fc_policy']], + ['state', 'present', ['fc_policy']], + ], + ) + + fc_policy = module.params.get('fc_policy') + port_mode = module.params.get('port_mode') + description = module.params.get('description') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fcIfPol', + aci_rn='infra/fcIfPol-{0}'.format(fc_policy), + module_object=fc_policy, + target_filter={'name': fc_policy}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fcIfPol', + class_config=dict( + name=fc_policy, + descr=description, + portMode=port_mode, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='fcIfPol') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_l2.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_l2.py new file mode 100644 index 00000000..bd74c922 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_l2.py @@ -0,0 +1,265 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_interface_policy_l2 +short_description: Manage Layer 2 interface policies (l2:IfPol) +description: +- Manage Layer 2 interface policies on Cisco ACI fabrics. +options: + l2_policy: + description: + - The name of the Layer 2 interface policy. + type: str + aliases: [ name ] + description: + description: + - The description of the Layer 2 interface policy. + type: str + aliases: [ descr ] + qinq: + description: + - Determines if QinQ is disabled or if the port should be considered a core or edge port. + - The APIC defaults to C(disabled) when unset during creation. + type: str + choices: [ core, disabled, edge ] + vepa: + description: + - Determines if Virtual Ethernet Port Aggregator is disabled or enabled. + - The APIC defaults to C(no) when unset during creation. + type: bool + vlan_scope: + description: + - The scope of the VLAN. + - The APIC defaults to C(global) when unset during creation. + type: str + choices: [ global, portlocal ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(l2:IfPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- name: Add a Layer 2 interface policy + cisco.aci.aci_interface_policy_l2: + host: '{{ hostname }}' + username: '{{ username }}' + password: '{{ password }}' + l2_policy: '{{ l2_policy }}' + vlan_scope: '{{ vlan_policy }}' + description: '{{ description }}' + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +# Mapping dicts are used to normalize the proposed data to what the APIC expects, which will keep diffs accurate +QINQ_MAPPING = dict( + core='corePort', + disabled='disabled', + edge='edgePort', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + l2_policy=dict(type='str', aliases=['name']), # Not required for querying all policies + description=dict(type='str', aliases=['descr']), + vlan_scope=dict(type='str', choices=['global', 'portlocal']), # No default provided on purpose + qinq=dict(type='str', choices=['core', 'disabled', 'edge']), + vepa=dict(type='bool'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['l2_policy']], + ['state', 'present', ['l2_policy']], + ], + ) + + aci = ACIModule(module) + + l2_policy = module.params.get('l2_policy') + vlan_scope = module.params.get('vlan_scope') + qinq = module.params.get('qinq') + if qinq is not None: + qinq = QINQ_MAPPING.get(qinq) + vepa = aci.boolean(module.params.get('vepa'), 'enabled', 'disabled') + description = module.params.get('description') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci.construct_url( + root_class=dict( + aci_class='l2IfPol', + aci_rn='infra/l2IfP-{0}'.format(l2_policy), + module_object=l2_policy, + target_filter={'name': l2_policy}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='l2IfPol', + class_config=dict( + name=l2_policy, + descr=description, + vlanScope=vlan_scope, + qinq=qinq, vepa=vepa, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='l2IfPol') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_breakout_port_group.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_breakout_port_group.py new file mode 100644 index 00000000..2646198b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_breakout_port_group.py @@ -0,0 +1,259 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_interface_policy_leaf_breakout_port_group +short_description: Manage fabric interface policy leaf breakout port group (infra:BrkoutPortGrp) +description: +- Manage fabric interface policy leaf breakout port group on Cisco ACI fabrics. +options: + breakout_port_group: + description: + - Name of the leaf breakout port group to be added/deleted. + type: str + aliases: [ name ] + description: + description: + - Description for the leaf breakout port group to be created. + type: str + aliases: [ descr ] + breakout_map: + description: + - The mapping of breakout port. + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(infra:BrkoutPortGrp). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Cindy Zhao (@cizhao) +''' + +EXAMPLES = r''' +- name: Create a Leaf Breakout Port Group + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + host: apic + username: admin + password: SomeSecretPassword + breakout_port_group: BreakoutPortName + breakout_map: 10g-4x + state: present + delegate_to: localhost + +- name: Query all Leaf Breakout Port Groups of type link + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result + +- name: Query a specific Leaf Breakout Port Group + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + host: apic + username: admin + password: SomeSecretPassword + breakout_port_group: BreakoutPortName + state: query + delegate_to: localhost + register: query_result + +- name: Delete an Leaf Breakout Port Group + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + host: apic + username: admin + password: SomeSecretPassword + breakout_port_group: BreakoutPortName + state: absent + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + breakout_port_group=dict(type='str', aliases=['name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + breakout_map=dict(type='str'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['breakout_port_group']], + ['state', 'present', ['breakout_port_group']], + ], + ) + + breakout_port_group = module.params.get('breakout_port_group') + description = module.params.get('description') + breakout_map = module.params.get('breakout_map') + state = module.params.get('state') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='infraBrkoutPortGrp', + aci_rn='infra/funcprof/brkoutportgrp-{0}'.format(breakout_port_group), + module_object=breakout_port_group, + target_filter={'name': breakout_port_group}, + ), + child_classes=[], + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='infraBrkoutPortGrp', + class_config=dict( + name=breakout_port_group, + descr=description, + brkoutMap=breakout_map, + ), + ) + + aci.get_diff(aci_class='infraBrkoutPortGrp') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_policy_group.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_policy_group.py new file mode 100644 index 00000000..001e6e97 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_policy_group.py @@ -0,0 +1,580 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_interface_policy_leaf_policy_group +short_description: Manage fabric interface policy leaf policy groups (infra:AccBndlGrp, infra:AccPortGrp) +description: +- Manage fabric interface policy leaf policy groups on Cisco ACI fabrics. +options: + policy_group: + description: + - Name of the leaf policy group to be added/deleted. + type: str + aliases: [ name, policy_group_name ] + description: + description: + - Description for the leaf policy group to be created. + type: str + aliases: [ descr ] + lag_type: + description: + - Selector for the type of leaf policy group we want to create. + - C(leaf) for Leaf Access Port Policy Group + - C(link) for Port Channel (PC) + - C(node) for Virtual Port Channel (VPC) + type: str + required: yes + choices: [ leaf, link, node ] + aliases: [ lag_type_name ] + link_level_policy: + description: + - Choice of link_level_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ link_level_policy_name ] + cdp_policy: + description: + - Choice of cdp_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ cdp_policy_name ] + mcp_policy: + description: + - Choice of mcp_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ mcp_policy_name ] + lldp_policy: + description: + - Choice of lldp_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ lldp_policy_name ] + stp_interface_policy: + description: + - Choice of stp_interface_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ stp_interface_policy_name ] + egress_data_plane_policing_policy: + description: + - Choice of egress_data_plane_policing_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ egress_data_plane_policing_policy_name ] + ingress_data_plane_policing_policy: + description: + - Choice of ingress_data_plane_policing_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ ingress_data_plane_policing_policy_name ] + priority_flow_control_policy: + description: + - Choice of priority_flow_control_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ priority_flow_control_policy_name ] + fibre_channel_interface_policy: + description: + - Choice of fibre_channel_interface_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ fibre_channel_interface_policy_name ] + slow_drain_policy: + description: + - Choice of slow_drain_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ slow_drain_policy_name ] + port_channel_policy: + description: + - Choice of port_channel_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ port_channel_policy_name ] + monitoring_policy: + description: + - Choice of monitoring_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ monitoring_policy_name ] + storm_control_interface_policy: + description: + - Choice of storm_control_interface_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ storm_control_interface_policy_name ] + l2_interface_policy: + description: + - Choice of l2_interface_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ l2_interface_policy_name ] + port_security_policy: + description: + - Choice of port_security_policy to be used as part of the leaf policy group to be created. + type: str + aliases: [ port_security_policy_name ] + aep: + description: + - Choice of attached_entity_profile (AEP) to be used as part of the leaf policy group to be created. + type: str + aliases: [ aep_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- When using the module please select the appropriate link_aggregation_type (lag_type). + C(link) for Port Channel(PC), C(node) for Virtual Port Channel(VPC) and C(leaf) for Leaf Access Port Policy Group. +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(infra:AccBndlGrp) and B(infra:AccPortGrp). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Bruno Calogero (@brunocalogero) +''' + +EXAMPLES = r''' +- name: Create a Port Channel (PC) Interface Policy Group + cisco.aci.aci_interface_policy_leaf_policy_group: + host: apic + username: admin + password: SomeSecretPassword + lag_type: link + policy_group: policygroupname + description: policygroupname description + link_level_policy: whateverlinklevelpolicy + cdp_policy: whatevercdppolicy + lldp_policy: whateverlldppolicy + port_channel_policy: whateverlacppolicy + state: present + delegate_to: localhost + +- name: Create a Virtual Port Channel (VPC) Interface Policy Group + cisco.aci.aci_interface_policy_leaf_policy_group: + host: apic + username: admin + password: SomeSecretPassword + lag_type: node + policy_group: policygroupname + link_level_policy: whateverlinklevelpolicy + cdp_policy: whatevercdppolicy + lldp_policy: whateverlldppolicy + port_channel_policy: whateverlacppolicy + state: present + delegate_to: localhost + +- name: Create a Leaf Access Port Policy Group + cisco.aci.aci_interface_policy_leaf_policy_group: + host: apic + username: admin + password: SomeSecretPassword + lag_type: leaf + policy_group: policygroupname + link_level_policy: whateverlinklevelpolicy + cdp_policy: whatevercdppolicy + lldp_policy: whateverlldppolicy + state: present + delegate_to: localhost + +- name: Query all Leaf Access Port Policy Groups of type link + cisco.aci.aci_interface_policy_leaf_policy_group: + host: apic + username: admin + password: SomeSecretPassword + lag_type: link + state: query + delegate_to: localhost + register: query_result + +- name: Query a specific Lead Access Port Policy Group + cisco.aci.aci_interface_policy_leaf_policy_group: + host: apic + username: admin + password: SomeSecretPassword + lag_type: leaf + policy_group: policygroupname + state: query + delegate_to: localhost + register: query_result + +- name: Delete an Interface policy Leaf Policy Group + cisco.aci.aci_interface_policy_leaf_policy_group: + host: apic + username: admin + password: SomeSecretPassword + lag_type: leaf + policy_group: policygroupname + state: absent + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + # NOTE: Since this module needs to include both infra:AccBndlGrp (for PC and VPC) and infra:AccPortGrp (for leaf access port policy group): + # NOTE: I'll allow the user to make the choice here (link(PC), node(VPC), leaf(leaf-access port policy group)) + lag_type=dict(type='str', required=True, aliases=['lag_type_name'], choices=['leaf', 'link', 'node']), + policy_group=dict(type='str', aliases=['name', 'policy_group_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + link_level_policy=dict(type='str', aliases=['link_level_policy_name']), + cdp_policy=dict(type='str', aliases=['cdp_policy_name']), + mcp_policy=dict(type='str', aliases=['mcp_policy_name']), + lldp_policy=dict(type='str', aliases=['lldp_policy_name']), + stp_interface_policy=dict(type='str', aliases=['stp_interface_policy_name']), + egress_data_plane_policing_policy=dict(type='str', aliases=['egress_data_plane_policing_policy_name']), + ingress_data_plane_policing_policy=dict(type='str', aliases=['ingress_data_plane_policing_policy_name']), + priority_flow_control_policy=dict(type='str', aliases=['priority_flow_control_policy_name']), + fibre_channel_interface_policy=dict(type='str', aliases=['fibre_channel_interface_policy_name']), + slow_drain_policy=dict(type='str', aliases=['slow_drain_policy_name']), + port_channel_policy=dict(type='str', aliases=['port_channel_policy_name']), + monitoring_policy=dict(type='str', aliases=['monitoring_policy_name']), + storm_control_interface_policy=dict(type='str', aliases=['storm_control_interface_policy_name']), + l2_interface_policy=dict(type='str', aliases=['l2_interface_policy_name']), + port_security_policy=dict(type='str', aliases=['port_security_policy_name']), + aep=dict(type='str', aliases=['aep_name']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['policy_group']], + ['state', 'present', ['policy_group']], + ], + ) + + policy_group = module.params.get('policy_group') + description = module.params.get('description') + lag_type = module.params.get('lag_type') + link_level_policy = module.params.get('link_level_policy') + cdp_policy = module.params.get('cdp_policy') + mcp_policy = module.params.get('mcp_policy') + lldp_policy = module.params.get('lldp_policy') + stp_interface_policy = module.params.get('stp_interface_policy') + egress_data_plane_policing_policy = module.params.get('egress_data_plane_policing_policy') + ingress_data_plane_policing_policy = module.params.get('ingress_data_plane_policing_policy') + priority_flow_control_policy = module.params.get('priority_flow_control_policy') + fibre_channel_interface_policy = module.params.get('fibre_channel_interface_policy') + slow_drain_policy = module.params.get('slow_drain_policy') + port_channel_policy = module.params.get('port_channel_policy') + monitoring_policy = module.params.get('monitoring_policy') + storm_control_interface_policy = module.params.get('storm_control_interface_policy') + l2_interface_policy = module.params.get('l2_interface_policy') + port_security_policy = module.params.get('port_security_policy') + aep = module.params.get('aep') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + if lag_type == 'leaf' and port_channel_policy is not None: + aci.fail_json('port_channel_policy is not a valid parameter for leaf\ + (leaf access port policy group), if used\ + assign null to it (port_channel_policy: null).') + + if lag_type == 'leaf': + aci_class_name = 'infraAccPortGrp' + dn_name = 'accportgrp' + class_config_dict = dict( + name=policy_group, + descr=description, + nameAlias=name_alias, + ) + # Reset for target_filter + lag_type = None + elif lag_type in ('link', 'node'): + aci_class_name = 'infraAccBndlGrp' + dn_name = 'accbundle' + class_config_dict = dict( + name=policy_group, + descr=description, + lagT=lag_type, + nameAlias=name_alias, + ) + + child_configs = [ + dict( + infraRsCdpIfPol=dict( + attributes=dict( + tnCdpIfPolName=cdp_policy, + ), + ), + ), + dict( + infraRsFcIfPol=dict( + attributes=dict( + tnFcIfPolName=fibre_channel_interface_policy, + ), + ), + ), + dict( + infraRsHIfPol=dict( + attributes=dict( + tnFabricHIfPolName=link_level_policy, + ), + ), + ), + dict( + infraRsL2IfPol=dict( + attributes=dict( + tnL2IfPolName=l2_interface_policy, + ), + ), + ), + dict( + infraRsL2PortSecurityPol=dict( + attributes=dict( + tnL2PortSecurityPolName=port_security_policy, + ), + ), + ), + dict( + infraRsLacpPol=dict( + attributes=dict( + tnLacpLagPolName=port_channel_policy, + ), + ), + ), + dict( + infraRsLldpIfPol=dict( + attributes=dict( + tnLldpIfPolName=lldp_policy, + ), + ), + ), + dict( + infraRsMcpIfPol=dict( + attributes=dict( + tnMcpIfPolName=mcp_policy, + ), + ), + ), + dict( + infraRsMonIfInfraPol=dict( + attributes=dict( + tnMonInfraPolName=monitoring_policy, + ), + ), + ), + dict( + infraRsQosEgressDppIfPol=dict( + attributes=dict( + tnQosDppPolName=egress_data_plane_policing_policy, + ), + ), + ), + dict( + infraRsQosIngressDppIfPol=dict( + attributes=dict( + tnQosDppPolName=ingress_data_plane_policing_policy, + ), + ), + ), + dict( + infraRsQosPfcIfPol=dict( + attributes=dict( + tnQosPfcIfPolName=priority_flow_control_policy, + ), + ), + ), + dict( + infraRsQosSdIfPol=dict( + attributes=dict( + tnQosSdIfPolName=slow_drain_policy, + ), + ), + ), + dict( + infraRsStormctrlIfPol=dict( + attributes=dict( + tnStormctrlIfPolName=storm_control_interface_policy, + ), + ), + ), + dict( + infraRsStpIfPol=dict( + attributes=dict( + tnStpIfPolName=stp_interface_policy, + ), + ), + ), + ] + + # Add infraRsattEntP binding only when aep was defined + if aep is not None: + child_configs.append(dict( + infraRsAttEntP=dict( + attributes=dict( + tDn='uni/infra/attentp-{0}'.format(aep), + ), + ), + )) + + aci.construct_url( + root_class=dict( + aci_class=aci_class_name, + aci_rn='infra/funcprof/{0}-{1}'.format(dn_name, policy_group), + module_object=policy_group, + target_filter={'name': policy_group, 'lagT': lag_type}, + ), + child_classes=[ + 'infraRsAttEntP', + 'infraRsCdpIfPol', + 'infraRsFcIfPol', + 'infraRsHIfPol', + 'infraRsL2IfPol', + 'infraRsL2PortSecurityPol', + 'infraRsLacpPol', + 'infraRsLldpIfPol', + 'infraRsMcpIfPol', + 'infraRsMonIfInfraPol', + 'infraRsQosEgressDppIfPol', + 'infraRsQosIngressDppIfPol', + 'infraRsQosPfcIfPol', + 'infraRsQosSdIfPol', + 'infraRsStormctrlIfPol', + 'infraRsStpIfPol', + ], + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class=aci_class_name, + class_config=class_config_dict, + child_configs=child_configs, + ) + + aci.get_diff(aci_class=aci_class_name) + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_profile.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_profile.py new file mode 100644 index 00000000..468ccbc8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_profile.py @@ -0,0 +1,313 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> +# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_interface_policy_leaf_profile +short_description: Manage fabric interface policy leaf profiles (infra:AccPortP) +description: +- Manage fabric interface policy leaf profiles on Cisco ACI fabrics. +options: + interface_profile: + description: + - The name of the Fabric access policy leaf interface profile. + type: str + aliases: [ name, leaf_interface_profile_name, leaf_interface_profile, interface_profile_name ] + description: + description: + - Description for the Fabric access policy leaf interface profile. + type: str + aliases: [ descr ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str + type: + description: + - The type of profile to be created. + type: str + choices: [ fex, leaf ] + default: leaf +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(infra:AccPortP). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Bruno Calogero (@brunocalogero) +- Shreyas Srish (@shrsr) +''' + +EXAMPLES = r''' +- name: Add a new leaf_interface_profile + cisco.aci.aci_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname + description: leafintprfname description + state: present + delegate_to: localhost + +- name: Add a new leaf_interface_profile of type fex + cisco.aci.aci_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname_fex + type: fex + description: leafintprfname description + state: present + delegate_to: localhost + +- name: Remove a leaf_interface_profile + cisco.aci.aci_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname + state: absent + delegate_to: localhost + +- name: Remove a leaf_interface_profile of type fex + cisco.aci.aci_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname_fex + type: fex + state: absent + delegate_to: localhost + +- name: Query a leaf_interface_profile + cisco.aci.aci_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname + state: query + delegate_to: localhost + register: query_result + +- name: Query a leaf_interface_profile of type fex + cisco.aci.aci_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + interface_profile: leafintprfname_fex + type: fex + state: query + delegate_to: localhost + register: query_result + +- name: Query all leaf_interface_profiles + cisco.aci.aci_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + +- name: Query all leaf_interface_profiles of type fex + cisco.aci.aci_interface_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + type: fex + state: query + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + interface_profile=dict(type='str', aliases=['name', 'leaf_interface_profile_name', 'leaf_interface_profile', 'interface_profile_name']), + description=dict(type='str', aliases=['descr']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + type=dict(type='str', default='leaf', choices=['fex', 'leaf']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['interface_profile']], + ['state', 'present', ['interface_profile']], + ], + ) + + interface_profile = module.params.get('interface_profile') + description = module.params.get('description') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + type_profile = module.params.get('type') + + aci = ACIModule(module) + aci_class = 'infraAccPortP' + aci_rn = 'accportprof' + if type_profile == 'fex': + aci_class = 'infraFexP' + aci_rn = 'fexprof' + aci.construct_url( + root_class=dict( + aci_class=aci_class, + aci_rn='infra/' + aci_rn + '-{0}'.format(interface_profile), + module_object=interface_profile, + target_filter={'name': interface_profile}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class=aci_class, + class_config=dict( + name=interface_profile, + descr=description, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class=aci_class) + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_link_level.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_link_level.py new file mode 100644 index 00000000..c6e792f7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_link_level.py @@ -0,0 +1,292 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2019, Vasily Prokopov (@vasilyprokopov) +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_interface_policy_link_level +short_description: Manage Link Level interface policies (fabric:HIfPol) +description: +- The link level interface policy specifies the layer 1 parameters of switch interfaces. +options: + link_level_policy: + description: + - The name of the Link Level interface policy. + type: str + aliases: [ name ] + description: + description: + - The description of the Link Level interface policy. + type: str + aliases: [ descr ] + auto_negotiation: + description: + - Auto-negotiation enables devices to automatically exchange information over a link about speed and duplex abilities. + - The APIC defaults to C(on) when unset during creation. + type: bool + default: true + speed: + description: + - Determines the interface policy administrative port speed. + - The APIC defaults to C(inherit) when unset during creation. + type: str + choices: [ 100M, 1G, 10G, 25G, 40G, 50G, 100G, 200G, 400G, inherit ] + default: inherit + link_debounce_interval: + description: + - Enables the debounce timer for physical interface ports and sets it for a specified amount of time in milliseconds. + - The APIC defaults to C(100) when unset during creation. + type: int + default: 100 + forwarding_error_correction: + description: + - Determines the forwarding error correction (FEC) mode. + - The APIC defaults to C(inherit) when unset during creation. + type: str + choices: [ inherit, kp-fec, cl91-rs-fec, cl74-fc-fec, disable-fec, ieee-rs-fec, cons16-rs-fec ] + default: inherit + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: cisco.aci.aci +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fabric:HIfPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Vasily Prokopov (@vasilyprokopov) +''' + +EXAMPLES = r''' +- name: Add a Link Level Policy + aci_interface_policy_link_level: + host: apic + username: admin + password: SomeSecretPassword + link_level_policy: link_level_policy_test + description: via Ansible + auto_negotiation: on + speed: 100M + link_debounce_interval: 100 + forwarding_error_correction: cl91-rs-fec + state: present + delegate_to: localhost + +- name: Remove a Link Level Policy + aci_interface_policy_link_level: + host: apic + username: admin + password: SomeSecretPassword + link_level_policy: ansible_test + state: absent + +- name: Query a Link Level Policy + aci_interface_policy_link_level: + host: apic + username: admin + password: SomeSecretPassword + link_level_policy: link_level_policy_test + state: query + delegate_to: localhost + +- name: Query all Link Level Policies + aci_interface_policy_link_level: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + link_level_policy=dict(type='str', aliases=['name']), + description=dict(type='str', aliases=['descr']), + auto_negotiation=dict(type='bool', default='true'), + speed=dict(type='str', default='inherit', choices=['100M', '1G', '10G', '25G', '40G', '50G', '100G', '200G', '400G', 'inherit']), + link_debounce_interval=dict(type='int', default='100'), + forwarding_error_correction=dict(type='str', default='inherit', + choices=['inherit', 'kp-fec', 'cl91-rs-fec', 'cl74-fc-fec', 'disable-fec', 'ieee-rs-fec', 'cons16-rs-fec']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['link_level_policy']], + ['state', 'present', ['link_level_policy']], + ], + ) + + aci = ACIModule(module) + + link_level_policy = module.params['link_level_policy'] + description = module.params['description'] + auto_negotiation = aci.boolean(module.params['auto_negotiation'], 'on', 'off') + speed = module.params['speed'] + link_debounce_interval = module.params['link_debounce_interval'] + if link_debounce_interval is not None and link_debounce_interval not in range(0, 5001): + module.fail_json(msg='The "link_debounce_interval" must be a value between 0 and 5000') + forwarding_error_correction = module.params['forwarding_error_correction'] + state = module.params['state'] + + aci.construct_url( + root_class=dict( + aci_class='fabricHIfPol', + aci_rn='infra/hintfpol-{0}'.format(link_level_policy), + module_object=link_level_policy, + target_filter={'name': link_level_policy}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fabricHIfPol', + class_config=dict( + name=link_level_policy, + descr=description, + autoNeg=auto_negotiation, + speed=speed, + linkDebounce=link_debounce_interval, + fecMode=forwarding_error_correction, + ), + ) + + aci.get_diff(aci_class='fabricHIfPol') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_lldp.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_lldp.py new file mode 100644 index 00000000..a1964733 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_lldp.py @@ -0,0 +1,248 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_interface_policy_lldp +short_description: Manage LLDP interface policies (lldp:IfPol) +description: +- Manage LLDP interface policies on Cisco ACI fabrics. +options: + lldp_policy: + description: + - The LLDP interface policy name. + type: str + aliases: [ name ] + description: + description: + - The description for the LLDP interface policy name. + type: str + aliases: [ descr ] + receive_state: + description: + - Enable or disable Receive state. + - The APIC defaults to C(yes) when unset during creation. + type: bool + transmit_state: + description: + - Enable or Disable Transmit state. + - The APIC defaults to C(yes) when unset during creation. + type: bool + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(lldp:IfPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- name: Add a LLDP interface policy + cisco.aci.aci_interface_policy_lldp: + host: '{{ hostname }}' + username: '{{ username }}' + password: '{{ password }}' + lldp_policy: '{{ lldp_policy }}' + description: '{{ description }}' + receive_state: '{{ receive_state }}' + transmit_state: '{{ transmit_state }}' + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + lldp_policy=dict(type='str', aliases=['name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + receive_state=dict(type='bool'), + transmit_state=dict(type='bool'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['lldp_policy']], + ['state', 'present', ['lldp_policy']], + ], + ) + + aci = ACIModule(module) + + lldp_policy = module.params.get('lldp_policy') + description = module.params.get('description') + receive_state = aci.boolean(module.params.get('receive_state'), 'enabled', 'disabled') + transmit_state = aci.boolean(module.params.get('transmit_state'), 'enabled', 'disabled') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci.construct_url( + root_class=dict( + aci_class='lldpIfPol', + aci_rn='infra/lldpIfP-{0}'.format(lldp_policy), + module_object=lldp_policy, + target_filter={'name': lldp_policy}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='lldpIfPol', + class_config=dict( + name=lldp_policy, + descr=description, + adminRxSt=receive_state, + adminTxSt=transmit_state, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='lldpIfPol') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_mcp.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_mcp.py new file mode 100644 index 00000000..97a1360a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_mcp.py @@ -0,0 +1,239 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_interface_policy_mcp +short_description: Manage MCP interface policies (mcp:IfPol) +description: +- Manage MCP interface policies on Cisco ACI fabrics. +options: + mcp: + description: + - The name of the MCP interface. + type: str + aliases: [ mcp_interface, name ] + description: + description: + - The description for the MCP interface. + type: str + aliases: [ descr ] + admin_state: + description: + - Enable or disable admin state. + - The APIC defaults to C(yes) when unset during creation. + type: bool + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(mcp:IfPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- name: Add a MCP interface policy + cisco.aci.aci_interface_policy_mcp: + host: '{{ hostname }}' + username: '{{ username }}' + password: '{{ password }}' + mcp: '{{ mcp }}' + description: '{{ descr }}' + admin_state: '{{ admin_state }}' + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + mcp=dict(type='str', aliases=['mcp_interface', 'name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + admin_state=dict(type='bool'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['mcp']], + ['state', 'present', ['mcp']], + ], + ) + + aci = ACIModule(module) + + mcp = module.params.get('mcp') + description = module.params.get('description') + admin_state = aci.boolean(module.params.get('admin_state'), 'enabled', 'disabled') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci.construct_url( + root_class=dict( + aci_class='mcpIfPol', + aci_rn='infra/mcpIfP-{0}'.format(mcp), + module_object=mcp, + target_filter={'name': mcp}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='mcpIfPol', + class_config=dict( + name=mcp, + descr=description, + adminSt=admin_state, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='mcpIfPol') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_ospf.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_ospf.py new file mode 100644 index 00000000..ff08e7a1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_ospf.py @@ -0,0 +1,405 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_interface_policy_ospf +short_description: Manage OSPF interface policies (ospf:IfPol) +description: +- Manage OSPF interface policies on Cisco ACI fabrics. +options: + tenant: + description: + - The name of the Tenant the OSPF interface policy should belong to. + type: str + aliases: [ tenant_name ] + ospf: + description: + - The OSPF interface policy name. + - This name can be between 1 and 64 alphanumeric characters. + - Note that you cannot change this name after the object has been saved. + type: str + aliases: [ ospf_interface, name ] + description: + description: + - The description for the OSPF interface. + type: str + aliases: [ descr ] + network_type: + description: + - The OSPF interface policy network type. + - OSPF supports broadcast and point-to-point. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ bcast, p2p ] + cost: + description: + - The OSPF cost of the interface. + - The cost (also called metric) of an interface in OSPF is an indication of + the overhead required to send packets across a certain interface. The + cost of an interface is inversely proportional to the bandwidth of that + interface. A higher bandwidth indicates a lower cost. There is more + overhead (higher cost) and time delays involved in crossing a 56k serial + line than crossing a 10M ethernet line. The formula used to calculate the + cost is C(cost= 10000 0000/bandwith in bps) For example, it will cost + 10 EXP8/10 EXP7 = 10 to cross a 10M Ethernet line and will cost + 10 EXP8/1544000 = 64 to cross a T1 line. + - By default, the cost of an interface is calculated based on the bandwidth; + you can force the cost of an interface with the ip ospf cost value + interface subconfiguration mode command. + - Accepted values range between C(1) and C(450). + - The APIC defaults to C(0) when unset during creation. + type: int + controls: + description: + - The interface policy controls. + - 'This is a list of one or more of the following controls:' + - C(advert-subnet) -- Advertise IP subnet instead of a host mask in the router LSA. + - C(bfd) -- Bidirectional Forwarding Detection + - C(mtu-ignore) -- Disables MTU mismatch detection on an interface. + - C(passive) -- The interface does not participate in the OSPF protocol and + will not establish adjacencies or send routing updates. However the + interface is announced as part of the routing network. + type: list + elements: str + choices: [ advert-subnet, bfd, mtu-ignore, passive ] + dead_interval: + description: + - The interval between hello packets from a neighbor before the router + declares the neighbor as down. + - This value must be the same for all networking devices on a specific network. + - Specifying a smaller dead interval (seconds) will give faster detection + of a neighbor being down and improve convergence, but might cause more + routing instability. + - Accepted values range between C(1) and C(65535). + - The APIC defaults to C(40) when unset during creation. + type: int + hello_interval: + description: + - The interval between hello packets that OSPF sends on the interface. + - Note that the smaller the hello interval, the faster topological changes will be detected, but more routing traffic will ensue. + - This value must be the same for all routers and access servers on a specific network. + - Accepted values range between C(1) and C(65535). + - The APIC defaults to C(10) when unset during creation. + type: int + prefix_suppression: + description: + - Whether prefix suppressions is enabled or disabled. + - The APIC defaults to C(inherit) when unset during creation. + type: bool + priority: + description: + - The priority for the OSPF interface profile. + - Accepted values ranges between C(0) and C(255). + - The APIC defaults to C(1) when unset during creation. + type: int + retransmit_interval: + description: + - The interval between LSA retransmissions. + - The retransmit interval occurs while the router is waiting for an acknowledgement from the neighbor router that it received the LSA. + - If no acknowledgment is received at the end of the interval, then the LSA is resent. + - Accepted values range between C(1) and C(65535). + - The APIC defaults to C(5) when unset during creation. + type: int + transmit_delay: + description: + - The delay time needed to send an LSA update packet. + - OSPF increments the LSA age time by the transmit delay amount before transmitting the LSA update. + - You should take into account the transmission and propagation delays for the interface when you set this value. + - Accepted values range between C(1) and C(450). + - The APIC defaults to C(1) when unset during creation. + type: int + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(ospf:IfPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Ensure ospf interface policy exists + cisco.aci.aci_interface_policy_ospf: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + ospf: ospf1 + state: present + delegate_to: localhost + +- name: Ensure ospf interface policy does not exist + cisco.aci.aci_interface_policy_ospf: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + ospf: ospf1 + state: present + delegate_to: localhost + +- name: Query an ospf interface policy + cisco.aci.aci_interface_policy_ospf: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + ospf: ospf1 + state: query + delegate_to: localhost + register: query_result + +- name: Query all ospf interface policies in tenant production + cisco.aci.aci_interface_policy_ospf: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + ospf=dict(type='str', aliases=['ospf_interface', 'name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + network_type=dict(type='str', choices=['bcast', 'p2p']), + cost=dict(type='int'), + controls=dict(type='list', elements='str', choices=['advert-subnet', 'bfd', 'mtu-ignore', 'passive']), + dead_interval=dict(type='int'), + hello_interval=dict(type='int'), + prefix_suppression=dict(type='bool'), + priority=dict(type='int'), + retransmit_interval=dict(type='int'), + transmit_delay=dict(type='int'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['ospf', 'tenant']], + ['state', 'present', ['ospf', 'tenant']], + ], + ) + + aci = ACIModule(module) + + tenant = module.params.get('tenant') + ospf = module.params.get('ospf') + description = module.params.get('description') + name_alias = module.params.get('name_alias') + + if module.params.get('controls') is None: + controls = None + else: + controls = ','.join(module.params.get('controls')) + + cost = module.params.get('cost') + if cost is not None and cost not in range(1, 451): + module.fail_json(msg="Parameter 'cost' is only valid in range between 1 and 450.") + + dead_interval = module.params.get('dead_interval') + if dead_interval is not None and dead_interval not in range(1, 65536): + module.fail_json(msg="Parameter 'dead_interval' is only valid in range between 1 and 65536.") + + hello_interval = module.params.get('hello_interval') + if hello_interval is not None and hello_interval not in range(1, 65536): + module.fail_json(msg="Parameter 'hello_interval' is only valid in range between 1 and 65536.") + + network_type = module.params.get('network_type') + prefix_suppression = aci.boolean(module.params.get('prefix_suppression'), 'enabled', 'disabled') + priority = module.params.get('priority') + if priority is not None and priority not in range(0, 256): + module.fail_json(msg="Parameter 'priority' is only valid in range between 1 and 255.") + + retransmit_interval = module.params.get('retransmit_interval') + if retransmit_interval is not None and retransmit_interval not in range(1, 65536): + module.fail_json(msg="Parameter 'retransmit_interval' is only valid in range between 1 and 65536.") + + transmit_delay = module.params.get('transmit_delay') + if transmit_delay is not None and transmit_delay not in range(1, 451): + module.fail_json(msg="Parameter 'transmit_delay' is only valid in range between 1 and 450.") + + state = module.params.get('state') + + aci.construct_url( + root_class=dict( + aci_class='ospfIfPol', + aci_rn='tn-{0}/ospfIfPol-{1}'.format(tenant, ospf), + module_object=ospf, + target_filter={'name': ospf}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='ospfIfPol', + class_config=dict( + name=ospf, + descr=description, + cost=cost, + ctrl=controls, + deadIntvl=dead_interval, + helloIntvl=hello_interval, + nwT=network_type, + pfxSuppress=prefix_suppression, + prio=priority, + rexmitIntvl=retransmit_interval, + xmitDelay=transmit_delay, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='ospfIfPol') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_channel.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_channel.py new file mode 100644 index 00000000..94e2fd74 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_channel.py @@ -0,0 +1,322 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_interface_policy_port_channel +short_description: Manage port channel interface policies (lacp:LagPol) +description: +- Manage port channel interface policies on Cisco ACI fabrics. +options: + port_channel: + description: + - Name of the port channel. + type: str + aliases: [ name ] + description: + description: + - The description for the port channel. + type: str + aliases: [ descr ] + max_links: + description: + - Maximum links. + - Accepted values range between 1 and 16. + - The APIC defaults to C(16) when unset during creation. + type: int + min_links: + description: + - Minimum links. + - Accepted values range between 1 and 16. + - The APIC defaults to C(1) when unset during creation. + type: int + mode: + description: + - Port channel interface policy mode. + - Determines the LACP method to use for forming port-channels. + - The APIC defaults to C(off) when unset during creation. + type: str + choices: [ active, mac-pin, mac-pin-nicload, 'off', passive ] + fast_select: + description: + - Determines if Fast Select is enabled for Hot Standby Ports. + - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties + left undefined or set to false will not exist after the task is ran. + - The APIC defaults to C(yes) when unset during creation. + type: bool + graceful_convergence: + description: + - Determines if Graceful Convergence is enabled. + - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties + left undefined or set to false will not exist after the task is ran. + - The APIC defaults to C(yes) when unset during creation. + type: bool + load_defer: + description: + - Determines if Load Defer is enabled. + - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties + left undefined or set to false will not exist after the task is ran. + - The APIC defaults to C(no) when unset during creation. + type: bool + suspend_individual: + description: + - Determines if Suspend Individual is enabled. + - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties + left undefined or set to false will not exist after the task is ran. + - The APIC defaults to C(yes) when unset during creation. + type: bool + symmetric_hash: + description: + - Determines if Symmetric Hashing is enabled. + - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties + left undefined or set to false will not exist after the task is ran. + - The APIC defaults to C(no) when unset during creation. + type: bool + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(lacp:LagPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- name: Add a port channel interface policy + cisco.aci.aci_interface_policy_port_channel: + host: '{{ inventory_hostname }}' + username: '{{ username }}' + password: '{{ password }}' + port_channel: '{{ port_channel }}' + description: '{{ description }}' + min_links: '{{ min_links }}' + max_links: '{{ max_links }}' + mode: '{{ mode }}' + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + port_channel=dict(type='str', aliases=['name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + min_links=dict(type='int'), + max_links=dict(type='int'), + mode=dict(type='str', choices=['active', 'mac-pin', 'mac-pin-nicload', 'off', 'passive']), + fast_select=dict(type='bool'), + graceful_convergence=dict(type='bool'), + load_defer=dict(type='bool'), + suspend_individual=dict(type='bool'), + symmetric_hash=dict(type='bool'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['port_channel']], + ['state', 'present', ['port_channel']], + ], + ) + + port_channel = module.params.get('port_channel') + description = module.params.get('description') + min_links = module.params.get('min_links') + if min_links is not None and min_links not in range(1, 17): + module.fail_json(msg='The "min_links" must be a value between 1 and 16') + max_links = module.params.get('max_links') + if max_links is not None and max_links not in range(1, 17): + module.fail_json(msg='The "max_links" must be a value between 1 and 16') + mode = module.params.get('mode') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + # Build ctrl value for request + ctrl = [] + if module.params.get('fast_select') is True: + ctrl.append('fast-sel-hot-stdby') + if module.params.get('graceful_convergence') is True: + ctrl.append('graceful-conv') + if module.params.get('load_defer') is True: + ctrl.append('load-defer') + if module.params.get('suspend_individual') is True: + ctrl.append('susp-individual') + if module.params.get('symmetric_hash') is True: + ctrl.append('symmetric-hash') + if not ctrl: + ctrl = None + else: + ctrl = ",".join(ctrl) + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='lacpLagPol', + aci_rn='infra/lacplagp-{0}'.format(port_channel), + module_object=port_channel, + target_filter={'name': port_channel}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='lacpLagPol', + class_config=dict( + name=port_channel, + ctrl=ctrl, + descr=description, + minLinks=min_links, + maxLinks=max_links, + mode=mode, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='lacpLagPol') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_security.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_security.py new file mode 100644 index 00000000..557fd2f4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_security.py @@ -0,0 +1,253 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_interface_policy_port_security +short_description: Manage port security (l2:PortSecurityPol) +description: +- Manage port security on Cisco ACI fabrics. +options: + port_security: + description: + - The name of the port security. + type: str + aliases: [ name ] + description: + description: + - The description for the contract. + type: str + aliases: [ descr ] + max_end_points: + description: + - Maximum number of end points. + - Accepted values range between C(0) and C(12000). + - The APIC defaults to C(0) when unset during creation. + type: int + port_security_timeout: + description: + - The delay time in seconds before MAC learning is re-enabled + - Accepted values range between C(60) and C(3600) + - The APIC defaults to C(60) when unset during creation + type: int + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(l2:PortSecurityPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- name: Add a port security interface policy + cisco.aci.aci_interface_policy_port_security: + host: '{{ inventory_hostname }}' + username: '{{ username }}' + password: '{{ password }}' + port_security: '{{ port_security }}' + description: '{{ descr }}' + max_end_points: '{{ max_end_points }}' + port_security_timeout: '{{ port_security_timeout }}' + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + port_security=dict(type='str', aliases=['name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + max_end_points=dict(type='int'), + port_security_timeout=dict(type='int'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['port_security']], + ['state', 'present', ['port_security']], + ], + ) + + port_security = module.params.get('port_security') + description = module.params.get('description') + max_end_points = module.params.get('max_end_points') + port_security_timeout = module.params.get('port_security_timeout') + name_alias = module.params.get('name_alias') + if max_end_points is not None and max_end_points not in range(12001): + module.fail_json(msg='The max_end_points must be between 0 and 12000') + if port_security_timeout is not None and port_security_timeout not in range(60, 3601): + module.fail_json(msg='The port_security_timeout must be between 60 and 3600') + state = module.params.get('state') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='l2PortSecurityPol', + aci_rn='infra/portsecurityP-{0}'.format(port_security), + module_object=port_security, + target_filter={'name': port_security}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='l2PortSecurityPol', + class_config=dict( + name=port_security, + descr=description, + maximum=max_end_points, + nameAlias=name_alias, + timeout=port_security_timeout, + ), + ) + + aci.get_diff(aci_class='l2PortSecurityPol') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_selector_to_switch_policy_leaf_profile.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_selector_to_switch_policy_leaf_profile.py new file mode 100644 index 00000000..6921e015 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_interface_selector_to_switch_policy_leaf_profile.py @@ -0,0 +1,254 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_interface_selector_to_switch_policy_leaf_profile +short_description: Bind interface selector profiles to switch policy leaf profiles (infra:RsAccPortP) +description: +- Bind interface selector profiles to switch policy leaf profiles on Cisco ACI fabrics. +options: + leaf_profile: + description: + - Name of the Leaf Profile to which we add a Selector. + type: str + aliases: [ leaf_profile_name ] + interface_selector: + description: + - Name of Interface Profile Selector to be added and associated with the Leaf Profile. + type: str + aliases: [ name, interface_selector_name, interface_profile_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- This module requires an existing leaf profile, the module M(cisco.aci.aci_switch_policy_leaf_profile) can be used for this. +seealso: +- module: cisco.aci.aci_switch_policy_leaf_profile +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(infra:RsAccPortP). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Bruno Calogero (@brunocalogero) +''' + +EXAMPLES = r''' +- name: Associating an interface selector profile to a switch policy leaf profile + cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + leaf_profile: sw_name + interface_selector: interface_profile_name + state: present + delegate_to: localhost + +- name: Remove an interface selector profile associated with a switch policy leaf profile + cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + leaf_profile: sw_name + interface_selector: interface_profile_name + state: absent + delegate_to: localhost + +- name: Query an interface selector profile associated with a switch policy leaf profile + cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + leaf_profile: sw_name + interface_selector: interface_profile_name + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + leaf_profile=dict(type='str', aliases=['leaf_profile_name']), # Not required for querying all objects + interface_selector=dict(type='str', aliases=['interface_profile_name', 'interface_selector_name', 'name']), # Not required for querying all objects + state=dict(type='str', default='present', choices=['absent', 'present', 'query']) + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['leaf_profile', 'interface_selector']], + ['state', 'present', ['leaf_profile', 'interface_selector']] + ], + ) + + leaf_profile = module.params.get('leaf_profile') + # WARNING: interface_selector accepts non existing interface_profile names and they appear on APIC gui with a state of "missing-target" + interface_selector = module.params.get('interface_selector') + state = module.params.get('state') + + # Defining the interface profile tDn for clarity + interface_selector_tDn = 'uni/infra/accportprof-{0}'.format(interface_selector) + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='infraNodeP', + aci_rn='infra/nprof-{0}'.format(leaf_profile), + module_object=leaf_profile, + target_filter={'name': leaf_profile}, + ), + subclass_1=dict( + aci_class='infraRsAccPortP', + aci_rn='rsaccPortP-[{0}]'.format(interface_selector_tDn), + module_object=interface_selector, + target_filter={'name': interface_selector}, + ) + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='infraRsAccPortP', + class_config=dict(tDn=interface_selector_tDn), + ) + + aci.get_diff(aci_class='infraRsAccPortP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l2out.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l2out.py new file mode 100644 index 00000000..5550c715 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l2out.py @@ -0,0 +1,320 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Sudhakar Shet Kudtarkar (@kudtarkar1) +# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: aci_l2out +short_description: Manage Layer2 Out (L2Out) objects. +description: +- Manage Layer2 Out configuration on Cisco ACI fabrics. +options: + tenant: + description: + - Name of an existing tenant. + type: str + l2out: + description: + - The name of outer layer2. + type: str + aliases: [ 'name' ] + description: + description: + - Description for the L2Out. + type: str + bd: + description: + - Name of the Bridge domain which is associted with the L2Out. + type: str + domain: + description: + - Name of the external L2 Domain that is being associated with L2Out. + type: str + vlan: + description: + - The VLAN which is being associated with the L2Out. + type: int + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) modules can be used for this. +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fvTenant). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Sudhakar Shet Kudtarkar (@kudtarkar1) +- Shreyas Srish (@shrsr) +''' + +EXAMPLES = r''' +- name: Add a new L2Out + cisco.aci.aci_l2out: + host: apic + username: admin + password: SomeSecretePassword + tenant: Auto-Demo + l2out: l2out + description: via Ansible + bd: bd1 + domain: l2Dom + vlan: 3200 + state: present + delegate_to: localhost + +- name: Remove an L2Out + cisco.aci.aci_l2out: + host: apic + username: admin + password: SomeSecretePassword + tenant: Auto-Demo + l2out: l2out + state: absent + delegate_to: localhost + +- name: Query an L2Out + cisco.aci.aci_l2out: + host: apic + username: admin + password: SomeSecretePassword + tenant: Auto-Demo + l2out: l2out + state: query + delegate_to: localhost + register: query_result + +- name: Query all L2Outs in a specific tenant + cisco.aci.aci_l2out: + host: apic + username: admin + password: SomeSecretePassword + tenant: Auto-Demo + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' + current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] + error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } + raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></imdata>' + sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } + previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] + proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } + filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only + method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST + response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) + status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 + url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json + ''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + bd=dict(type='str'), + l2out=dict(type='str', aliases=['name']), + domain=dict(type='str'), + vlan=dict(type='int'), + description=dict(type='str'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + tenant=dict(type='str'), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['l2out', 'tenant']], + ['state', 'present', ['bd', 'l2out', 'tenant', 'domain', 'vlan']], + ], + ) + + bd = module.params.get('bd') + l2out = module.params.get('l2out') + description = module.params.get('description') + domain = module.params.get('domain') + vlan = module.params.get('vlan') + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + child_classes = ['l2extRsEBd', 'l2extRsL2DomAtt', 'l2extLNodeP'] + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='l2extOut', + aci_rn='l2out-{0}'.format(l2out), + module_object=l2out, + target_filter={'name': l2out}, + ), + child_classes=child_classes, + ) + + aci.get_existing() + + if state == 'present': + child_configs = [ + dict( + l2extRsL2DomAtt=dict( + attributes=dict( + tDn='uni/l2dom-{0}'.format(domain) + ) + ) + ), + dict( + l2extRsEBd=dict( + attributes=dict( + tnFvBDName=bd, encap='vlan-{0}'.format(vlan) + ) + ) + ) + ] + + aci.payload( + aci_class='l2extOut', + class_config=dict( + name=l2out, + descr=description, + dn='uni/tn-{0}/l2out-{1}'.format(tenant, l2out), + nameAlias=name_alias + ), + child_configs=child_configs, + ) + + aci.get_diff(aci_class='l2extOut') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l2out_extepg.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l2out_extepg.py new file mode 100644 index 00000000..8111e2b2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l2out_extepg.py @@ -0,0 +1,310 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Sudhakar Shet Kudtarkar (@kudtarkar1) +# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = r''' +--- +module: aci_l2out_extepg +short_description: Manage External Network Instance (L2Out External EPG) objects (l2extInstP). +description: +- Manage External Network Instance (L2Out External EPG) objects (l2extInstP) on ACI fabrics. +options: + tenant: + description: + - Name of existing tenant. + type: str + l2out: + description: + - Name of the l2out. + type: str + extepg: + description: + - Name of the external end point group. + type: str + aliases: [ external_epg, extepg_name, name ] + description: + description: + - Description for the l2out. + type: str + preferred_group: + description: + - This depicts whether this External EPG is part of the Preferred Group and can communicate without contracts. + - This is convenient for migration scenarios, or when ACI is used for network automation but not for policy. + - The APIC defaults to C(no) when unset during creation. + type: bool + qos_class: + description: + - The bandwidth level for Quality of service. + type: str + choices: [ level1, level2, level3, level4, level5, level6, Unspecified ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) and C(l2out) must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_l2out) modules can be used for this. +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fvtenant) and B(l2extOut). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Sudhakar Shet Kudtarkar (@kudtarkar1) +- Shreyas Srish (@shrsr) +''' + +EXAMPLES = r''' +- name: Add a new L2 external end point group + cisco.aci.aci_l2out_extepg: + host: apic + username: admin + password: SomeSecretePassword + tenant: Auto-Demo + l2out: l2out + extepg: NewExt + description: external epg + preferred_group: False + state: present + delegate_to: localhost + +- name: Remove an L2 external end point group + cisco.aci.aci_l2out_extepg: + host: apic + username: admin + password: SomeSecretePassword + tenant: Auto-Demo + l2out: l2out + extepg: NewExt + state: absent + delegate_to: localhost + +- name: Query the L2 external end point group + cisco.aci.aci_l2out_extepg: + host: apic + username: admin + password: SomeSecretePassword + tenant: Auto-Demo + l2out: l2out + extepg: NewExt + state: query + delegate_to: localhost + register: query_result + +- name: Query all L2 external end point groups in a tenant + cisco.aci.aci_l2out_extepg: + host: apic + username: admin + password: SomeSecretePassword + tenant: Auto-Demo + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' + current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] + error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } + raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></imdata>' + sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } + previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] + proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } + filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only + method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST + response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) + status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 + url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json + ''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + l2out=dict(type='str'), + description=dict(type='str'), + extepg=dict(type='str', aliases=['external_epg', 'extepg_name', 'name']), + preferred_group=dict(type='bool'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + tenant=dict(type='str'), + qos_class=dict(type='str', choices=['level1', 'level2', 'level3', 'level4', 'level5', 'level6', 'Unspecified']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['l2out', 'tenant', 'extepg']], + ['state', 'present', ['l2out', 'tenant', 'extepg']], + ], + ) + + aci = ACIModule(module) + + l2out = module.params.get('l2out') + description = module.params.get('description') + preferred_group = aci.boolean(module.params.get('preferred_group'), 'include', 'exclude') + state = module.params.get('state') + tenant = module.params.get('tenant') + extepg = module.params.get('extepg') + qos_class = module.params.get('qos_class') + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='l2extOut', + aci_rn='l2out-{0}'.format(l2out), + module_object=l2out, + target_filter={'name': l2out}, + ), + subclass_2=dict( + aci_class='l2extInstP', + aci_rn='instP-{0}'.format(extepg), + module_object=extepg, + target_filter={'name': extepg}, + + ) + ) + + aci.get_existing() + + if state == 'present': + config = dict( + name=extepg, + descr=description, + dn='uni/tn-{0}/l2out-{1}/instP-{2}'.format(tenant, l2out, extepg), + prefGrMemb=preferred_group + ) + if qos_class: + config.update(prio=qos_class) + aci.payload( + class_config=config, + aci_class='l2extInstP', + ) + + aci.get_diff(aci_class='l2extInstP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out.py new file mode 100644 index 00000000..50fee411 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out.py @@ -0,0 +1,367 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_l3out +short_description: Manage Layer 3 Outside (L3Out) objects (l3ext:Out) +description: +- Manage Layer 3 Outside (L3Out) on Cisco ACI fabrics. +options: + tenant: + description: + - Name of an existing tenant. + type: str + aliases: [ tenant_name ] + l3out: + description: + - Name of L3Out being created. + type: str + aliases: [ l3out_name, name ] + vrf: + description: + - Name of the VRF being associated with the L3Out. + type: str + aliases: [ vrf_name ] + domain: + description: + - Name of the external L3 domain being associated with the L3Out. + type: str + aliases: [ ext_routed_domain_name, routed_domain ] + dscp: + description: + - The target Differentiated Service (DSCP) value. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ AF11, AF12, AF13, AF21, AF22, AF23, AF31, AF32, AF33, AF41, AF42, AF43, CS0, CS1, CS2, CS3, CS4, CS5, CS6, CS7, EF, VA, unspecified ] + aliases: [ target ] + route_control: + description: + - Route Control enforcement direction. The only allowed values are export or import,export. + type: list + elements: str + choices: [ export, import ] + aliases: [ route_control_enforcement ] + l3protocol: + description: + - Routing protocol for the L3Out + type: list + elements: str + choices: [ bgp, eigrp, ospf, pim, static ] + asn: + description: + - The AS number for the L3Out. + - Only applicable when using 'eigrp' as the l3protocol + type: int + aliases: [ as_number ] + description: + description: + - Description for the L3Out. + type: str + aliases: [ descr ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) and C(domain) and C(vrf) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_domain) and M(cisco.aci.aci_vrf) modules can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- module: cisco.aci.aci_domain +- module: cisco.aci.aci_vrf +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(l3ext:Out). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Rostyslav Davydenko (@rost-d) +''' + +EXAMPLES = r''' +- name: Add a new L3Out + cisco.aci.aci_l3out: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + name: prod_l3out + description: L3Out for Production tenant + domain: l3dom_prod + vrf: prod + l3protocol: ospf + state: present + delegate_to: localhost + +- name: Delete L3Out + cisco.aci.aci_l3out: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + name: prod_l3out + state: absent + delegate_to: localhost + +- name: Query L3Out information + cisco.aci.aci_l3out: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + name: prod_l3out + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + l3out=dict(type='str', aliases=['l3out_name', 'name']), # Not required for querying all objects + domain=dict(type='str', aliases=['ext_routed_domain_name', 'routed_domain']), + vrf=dict(type='str', aliases=['vrf_name']), + description=dict(type='str', aliases=['descr']), + route_control=dict(type='list', elements='str', choices=['export', 'import'], aliases=['route_control_enforcement']), + dscp=dict(type='str', + choices=['AF11', 'AF12', 'AF13', 'AF21', 'AF22', 'AF23', 'AF31', 'AF32', 'AF33', 'AF41', 'AF42', + 'AF43', 'CS0', 'CS1', 'CS2', 'CS3', 'CS4', 'CS5', 'CS6', 'CS7', 'EF', 'VA', 'unspecified'], + aliases=['target']), + l3protocol=dict(type='list', elements='str', choices=['bgp', 'eigrp', 'ospf', 'pim', 'static']), + asn=dict(type='int', aliases=['as_number']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['l3out', 'tenant']], + ['state', 'present', ['l3out', 'tenant', 'domain', 'vrf']], + ], + ) + + aci = ACIModule(module) + + l3out = module.params.get('l3out') + domain = module.params.get('domain') + dscp = module.params.get('dscp') + description = module.params.get('description') + enforceRtctrl = module.params.get('route_control') + vrf = module.params.get('vrf') + l3protocol = module.params.get('l3protocol') + asn = module.params.get('asn') + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + if l3protocol: + if 'eigrp' in l3protocol and asn is None: + module.fail_json(msg="Parameter 'asn' is required when l3protocol is 'eigrp'") + if 'eigrp' not in l3protocol and asn is not None: + module.warn("Parameter 'asn' is only applicable when l3protocol is 'eigrp'. The ASN will be ignored") + + enforce_ctrl = '' + if enforceRtctrl is not None: + if len(enforceRtctrl) == 1 and enforceRtctrl[0] == 'import': + aci.fail_json( + "The route_control parameter is invalid: allowed options are export or import,export only") + elif len(enforceRtctrl) == 1 and enforceRtctrl[0] == 'export': + enforce_ctrl = 'export' + else: + enforce_ctrl = 'export,import' + child_classes = ['l3extRsL3DomAtt', 'l3extRsEctx', 'bgpExtP', 'ospfExtP', 'eigrpExtP', 'pimExtP'] + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='l3extOut', + aci_rn='out-{0}'.format(l3out), + module_object=l3out, + target_filter={'name': l3out}, + ), + child_classes=child_classes, + ) + + aci.get_existing() + + child_configs = [ + dict(l3extRsL3DomAtt=dict(attributes=dict( + tDn='uni/l3dom-{0}'.format(domain)))), + dict(l3extRsEctx=dict(attributes=dict(tnFvCtxName=vrf))), + ] + if l3protocol is not None: + for protocol in l3protocol: + if protocol == 'bgp': + child_configs.append( + dict(bgpExtP=dict(attributes=dict(descr='', nameAlias='')))) + elif protocol == 'eigrp': + child_configs.append( + dict(eigrpExtP=dict(attributes=dict(descr='', nameAlias='', asn=asn)))) + elif protocol == 'ospf': + child_configs.append( + dict(ospfExtP=dict(attributes=dict(descr='', nameAlias='')))) + elif protocol == 'pim': + child_configs.append( + dict(pimExtP=dict(attributes=dict(descr='', nameAlias='')))) + + if state == 'present': + aci.payload( + aci_class='l3extOut', + class_config=dict( + name=l3out, + descr=description, + dn='uni/tn-{0}/out-{1}'.format(tenant, l3out), + enforceRtctrl=enforce_ctrl, + targetDscp=dscp, + nameAlias=name_alias, + ), + child_configs=child_configs, + ) + + aci.get_diff(aci_class='l3extOut') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg.py new file mode 100644 index 00000000..bbe4dfa6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg.py @@ -0,0 +1,309 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_l3out_extepg +short_description: Manage External Network Instance Profile (ExtEpg) objects (l3extInstP:instP) +description: +- Manage External Network Instance Profile (ExtEpg) objects (l3extInstP:instP) +options: + tenant: + description: + - Name of an existing tenant. + type: str + aliases: [ tenant_name ] + l3out: + description: + - Name of an existing L3Out. + type: str + aliases: [ l3out_name ] + extepg: + description: + - Name of ExtEpg being created. + type: str + aliases: [ extepg_name, name ] + description: + description: + - Description for the ExtEpg. + type: str + aliases: [ descr ] + preferred_group: + description: + - Whether ot not the EPG is part of the Preferred Group and can communicate without contracts. + - This is very convenient for migration scenarios, or when ACI is used for network automation but not for policy. + - The APIC defaults to C(no) when unset during creation. + type: bool + dscp: + description: + - The target Differentiated Service (DSCP) value. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ AF11, AF12, AF13, AF21, AF22, AF23, AF31, AF32, AF33, AF41, AF42, AF43, CS0, CS1, CS2, CS3, CS4, CS5, CS6, CS7, EF, VA, unspecified ] + aliases: [ target ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) and C(domain) and C(vrf) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_domain) and M(cisco.aci.aci_vrf) modules can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- module: cisco.aci.aci_domain +- module: cisco.aci.aci_vrf +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(l3ext:Out). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Rostyslav Davydenko (@rost-d) +''' + +EXAMPLES = r''' +- name: Add a new ExtEpg + cisco.aci.aci_l3out_extepg: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + l3out: prod_l3out + name: prod_extepg + description: ExtEpg for Production L3Out + state: present + delegate_to: localhost + +- name: Delete ExtEpg + cisco.aci.aci_extepg: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + l3out: prod_l3out + name: prod_extepg + state: absent + delegate_to: localhost + +- name: Query ExtEpg information + cisco.aci.aci_l3out_extepg: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + l3out: prod_l3out + name: prod_extepg + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + l3out=dict(type='str', aliases=['l3out_name']), # Not required for querying all objects + extepg=dict(type='str', aliases=['extepg_name', 'name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + preferred_group=dict(type='bool'), + dscp=dict(type='str', + choices=['AF11', 'AF12', 'AF13', 'AF21', 'AF22', 'AF23', 'AF31', 'AF32', 'AF33', 'AF41', 'AF42', + 'AF43', 'CS0', 'CS1', 'CS2', 'CS3', 'CS4', 'CS5', 'CS6', 'CS7', 'EF', 'VA', 'unspecified'], + aliases=['target']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['extepg', 'l3out', 'tenant']], + ['state', 'absent', ['extepg', 'l3out', 'tenant']], + ], + ) + + aci = ACIModule(module) + + tenant = module.params.get('tenant') + l3out = module.params.get('l3out') + extepg = module.params.get('extepg') + description = module.params.get('description') + preferred_group = aci.boolean(module.params.get('preferred_group'), 'include', 'exclude') + dscp = module.params.get('dscp') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='l3extOut', + aci_rn='out-{0}'.format(l3out), + module_object=l3out, + target_filter={'name': l3out}, + ), + subclass_2=dict( + aci_class='l3extInstP', + aci_rn='instP-{0}'.format(extepg), + module_object=extepg, + target_filter={'name': extepg}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='l3extInstP', + class_config=dict( + name=extepg, + descr=description, + prefGrMemb=preferred_group, + targetDscp=dscp, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='l3extInstP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg_to_contract.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg_to_contract.py new file mode 100644 index 00000000..9bc94fe7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg_to_contract.py @@ -0,0 +1,346 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Sudhakar Shet Kudtarkar (@kudtarkar1) +# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: aci_l3out_extepg_to_contract +short_description: Bind Contracts to External End Point Groups (EPGs) +description: +- Bind Contracts to External End Point Groups (EPGs) on ACI fabrics. +options: + tenant: + description: + - Name of existing tenant. + type: str + l3out: + description: + - Name of the l3out. + type: str + aliases: ['l3out_name'] + extepg: + description: + - Name of the external end point group. + type: str + aliases: ['extepg_name', 'external_epg'] + contract: + description: + - Name of the contract. + type: str + contract_type: + description: + - The type of contract. + type: str + required: yes + choices: ['consumer', 'provider'] + priority: + description: + - This has four levels of priority. + type: str + choices: ['level1', 'level2', 'level3', 'unspecified'] + provider_match: + description: + - This is configurable for provided contracts. + type: str + choices: ['all', 'at_least_one', 'at_most_one', 'none'] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant), C(l3out) and C(extepg) must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant), M(cisco.aci.aci_l3out) and M(cisco.aci.aci_l3out_extepg) modules can be used for this. +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fvtenant), B(l3extInstP) and B(l3extOut). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Sudhakar Shet Kudtarkar (@kudtarkar1) +- Shreyas Srish (@shrsr) +''' + +EXAMPLES = r''' +- name: Bind a contract to an external EPG + cisco.aci.aci_l3out_epg_to_contract: + host: apic + username: admin + password: SomeSecretePassword + tenant: Auto-Demo + l3out: l3out + extepg : testEpg + contract: contract1 + contract_type: provider + state: present + delegate_to: localhost + +- name: Remove existing contract from an external EPG + cisco.aco.aci_l3out_epg_to_contract: + host: apic + username: admin + password: SomeSecretePassword + tenant: Auto-Demo + l3out: l3out + extepg : testEpg + contract: contract1 + contract_type: provider + state: absent + delegate_to: localhost + +- name: Query a contract bound to an external EPG + cisco.aci.aci_l3out_epg_to_contract: + host: apic + username: admin + password: SomeSecretePassword + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + contract: ansible_contract + contract_type: provider + state: query + delegate_to: localhost + register: query_result + +- name: Query all contracts relationships + cisco.aci.aci_l3out_epg_to_contract: + host: apic + username: admin + password: SomeSecretePassword + contract_type: provider + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' + current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] + error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } + raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></imdata>' + sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } + previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] + proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } + filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only + method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST + response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) + status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 + url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json + ''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +ACI_CLASS_MAPPING = dict( + consumer={ + 'class': 'fvRsCons', + 'rn': 'rscons-', + }, + provider={ + 'class': 'fvRsProv', + 'rn': 'rsprov-', + }, +) + +PROVIDER_MATCH_MAPPING = dict( + all='All', + at_least_one='AtleastOne', + at_most_one='tmostOne', + none='None', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + contract_type=dict(type='str', required=True, choices=['consumer', 'provider']), + l3out=dict(type='str', aliases=['l3out_name']), + contract=dict(type='str'), + priority=dict(type='str', choices=['level1', 'level2', 'level3', 'unspecified']), + provider_match=dict(type='str', choices=['all', 'at_least_one', 'at_most_one', 'none']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + tenant=dict(type='str'), + extepg=dict(type='str', aliases=['extepg_name', 'external_epg']), + ) + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['extepg', 'contract', 'l3out', 'tenant']], + ['state', 'present', ['extepg', 'contract', 'l3out', 'tenant']], + ], + ) + + l3out = module.params.get('l3out') + contract = module.params.get('contract') + contract_type = module.params.get('contract_type') + extepg = module.params.get('extepg') + priority = module.params.get('priority') + provider_match = module.params.get('provider_match') + if provider_match is not None: + provider_match = PROVIDER_MATCH_MAPPING.get(provider_match) + state = module.params.get('state') + tenant = module.params.get('tenant') + + aci_class = ACI_CLASS_MAPPING.get(contract_type)["class"] + aci_rn = ACI_CLASS_MAPPING.get(contract_type)["rn"] + + if contract_type == "consumer" and provider_match is not None: + module.fail_json(msg="the 'provider_match' is only configurable for Provided Contracts") + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='l3extOut', + aci_rn='out-{0}'.format(l3out), + module_object=l3out, + target_filter={'name': l3out}, + ), + subclass_2=dict( + aci_class='l3extInstP', + aci_rn='instP-{0}'.format(extepg), + module_object=extepg, + target_filter={'name': extepg}, + ), + subclass_3=dict( + aci_class=aci_class, + aci_rn='{0}{1}'.format(aci_rn, contract), + module_object=contract, + target_filter={'tnVzBrCPName': contract}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class=aci_class, + class_config=dict( + matchT=provider_match, + prio=priority, + tnVzBrCPName=contract, + ), + ) + + aci.get_diff(aci_class=aci_class) + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extsubnet.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extsubnet.py new file mode 100644 index 00000000..cddd76a6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extsubnet.py @@ -0,0 +1,332 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_l3out_extsubnet +short_description: Manage External Subnet objects (l3extSubnet:extsubnet) +description: +- Manage External Subnet objects (l3extSubnet:extsubnet) +options: + tenant: + description: + - Name of an existing tenant. + type: str + aliases: [ tenant_name ] + required: yes + l3out: + description: + - Name of an existing L3Out. + type: str + aliases: [ l3out_name ] + required: yes + extepg: + description: + - Name of an existing ExtEpg. + type: str + aliases: [ extepg_name ] + required: yes + network: + description: + - The network address for the Subnet. + type: str + aliases: [ address, ip ] + subnet_name: + description: + - Name of External Subnet being created. + type: str + aliases: [ name ] + description: + description: + - Description for the External Subnet. + type: str + aliases: [ descr ] + scope: + description: + - Determines the scope of the Subnet. + - The C(export-rtctrl) option controls which external networks are advertised out of the fabric using route-maps and IP prefix-lists. + - The C(import-security) option classifies for the external EPG. + The rules and contracts defined in this external EPG apply to networks matching this subnet. + - The C(shared-rtctrl) option controls which external prefixes are advertised to other tenants for shared services. + - The C(shared-security) option configures the classifier for the subnets in the VRF where the routes are leaked. + - The APIC defaults to C(import-security) when unset during creation. + default: [ import-security ] + type: list + elements: str + choices: [ export-rtctrl, import-security, shared-rtctrl, shared-security ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) and C(domain) and C(vrf) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_domain) and M(cisco.aci.aci_vrf) modules can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- module: cisco.aci.aci_domain +- module: cisco.aci.aci_vrf +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(l3ext:Out). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Rostyslav Davydenko (@rost-d) +- Cindy Zhao (@cizhao) +''' + +EXAMPLES = r''' +- name: Add a new External Subnet + cisco.aci.aci_l3out_extsubnet: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + l3out: prod_l3out + extepg: prod_extepg + description: External Subnet for Production ExtEpg + network: 192.0.2.0/24 + scope: export-rtctrl + state: present + delegate_to: localhost + +- name: Delete External Subnet + cisco.aci.aci_l3out_extsubnet: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + l3out: prod_l3out + extepg: prod_extepg + network: 192.0.2.0/24 + state: absent + delegate_to: localhost + +- name: Query ExtEpg Subnet information + cisco.aci.aci_l3out_extsubnet: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + l3out: prod_l3out + extepg: prod_extepg + network: 192.0.2.0/24 + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', required=True, aliases=['tenant_name']), + l3out=dict(type='str', required=True, aliases=['l3out_name']), + extepg=dict(type='str', required=True, aliases=['extepg_name', 'name']), + network=dict(type='str', aliases=['address', 'ip']), + description=dict(type='str', aliases=['descr']), + subnet_name=dict(type='str', aliases=['name']), + scope=dict(type='list', elements='str', default=['import-security'], choices=['export-rtctrl', 'import-security', 'shared-rtctrl', 'shared-security']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['network']], + ['state', 'absent', ['network']], + ], + ) + + aci = ACIModule(module) + + tenant = module.params.get('tenant') + l3out = module.params.get('l3out') + extepg = module.params.get('extepg') + network = module.params.get('network') + description = module.params.get('description') + subnet_name = module.params.get('subnet_name') + scope = ','.join(sorted(module.params.get('scope'))) + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='l3extOut', + aci_rn='out-{0}'.format(l3out), + module_object=l3out, + target_filter={'name': l3out}, + ), + subclass_2=dict( + aci_class='l3extInstP', + aci_rn='instP-{0}'.format(extepg), + module_object=extepg, + target_filter={'name': extepg}, + ), + subclass_3=dict( + aci_class='l3extSubnet', + aci_rn='extsubnet-[{0}]'.format(network), + module_object=network, + target_filter={'name': network}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='l3extSubnet', + class_config=dict( + ip=network, + descr=description, + name=subnet_name, + scope=scope, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='l3extSubnet') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_vpc_member.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_vpc_member.py new file mode 100644 index 00000000..61110acb --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_vpc_member.py @@ -0,0 +1,340 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Anvitha Jain(@anvitha-jain) <anvjain@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_l3out_logical_interface_vpc_member +short_description: Manage Member Node objects (l3extMember:Member) +description: +- Manage Member Node objects (l3extMember:Member) +options: + description: + description: + - The description for the logical interface VPC member. + type: str + aliases: [ descr ] + tenant: + description: + - Name of an existing tenant. + type: str + aliases: [ tenant_name ] + l3out: + description: + - Name of an existing L3Out. + type: str + aliases: [ l3out_name ] + logical_node: + description: + - Name of an existing logical node profile. + type: str + logical_interface: + description: + - Name of an existing logical interface. + type: str + path_dn: + description: + - DN of existing path endpoints for VPC policy group used to reach external L3 network. + type: str + side: + description: + - Provides the side of member. + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant), C(l3out), C(logical_node), C(logical_interface), C(path_dn) and C(member) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_l3out) modules can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- module: cisco.aci.aci_l3out +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(l3ext:Out). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Anvitha Jain(@anvitha-jain) +''' + +EXAMPLES = r''' +- name: Create a VPC member + cisco.aci.aci_l3out_logical_interface_vpc_member: + host: apic + username: admin + password: SomeSecretPassword + tenant: tenantName + l3out: l3out + logical_node: nodeName + logical_interface: interfaceName + path_dn: topology/pod-1/protpaths-101-102/pathep-[policy_group_name] + side: A + state: present + delegate_to: localhost + +- name: Delete a VPC member + cisco.aci.aci_l3out_logical_interface_vpc_member: + host: apic + username: admin + password: SomeSecretPassword + tenant: tenantName + l3out: l3out + logical_node: nodeName + logical_interface: interfaceName + path_dn: topology/pod-1/protpaths-101-102/pathep-[policy_group_name] + side: A + state: absent + delegate_to: localhost + +- name: Query all VPC members + cisco.aci.aci_l3out_logical_interface_vpc_member: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + state: query + delegate_to: localhost + register: query_result + +- name: Query a specific VPC member under l3out + cisco.aci.aci_l3out_logical_interface_vpc_member: + host: apic + username: admin + password: SomeSecretPassword + tenant: tenantName + l3out: l3out + logical_node: nodeName + logical_interface: interfaceName + path_dn: topology/pod-1/protpaths-101-102/pathep-[policy_group_name] + side: A + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + l3out=dict(type='str', aliases=['l3out_name']), # Not required for querying all objects + logical_node=dict(type='str'), # Not required for querying all objects + logical_interface=dict(type='str'), + path_dn=dict(type='str'), + side=dict(type='str'), + description=dict(type='str', aliases=['descr']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['side', 'path_dn', 'logical_interface', 'logical_node', 'l3out', 'tenant']], + ['state', 'absent', ['side', 'path_dn', 'logical_interface', 'logical_node', 'l3out', 'tenant']], + ], + ) + + aci = ACIModule(module) + + tenant = module.params.get('tenant') + l3out = module.params.get('l3out') + logical_node = module.params.get('logical_node') + logical_interface = module.params.get('logical_interface') + path_dn = module.params.get('path_dn') + side = module.params.get('side') + description = module.params.get('description') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='l3extOut', + aci_rn='out-{0}'.format(l3out), + module_object=l3out, + target_filter={'name': l3out}, + ), + subclass_2=dict( + aci_class='l3extLNodeP', + aci_rn='lnodep-{0}'.format(logical_node), + module_object=logical_node, + target_filter={'name': logical_node}, + ), + subclass_3=dict( + aci_class='l3extLIfP', + aci_rn='/lifp-{0}'.format(logical_interface), + module_object=logical_interface, + target_filter={'name': logical_interface}, + ), + subclass_4=dict( + aci_class='l3extRsPathL3OutAtt', + aci_rn='/rspathL3OutAtt-[{0}]'.format(path_dn), + module_object=path_dn, + target_filter={'name': path_dn}, + ), + subclass_5=dict( + aci_class='l3extMember', + aci_rn='/mem-{0}'.format(side), + module_object=side, + target_filter={'name': side}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='l3extMember', + class_config=dict( + name=side, + descr=description, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='l3extMember') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_route_tag_policy.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_route_tag_policy.py new file mode 100644 index 00000000..412a492d --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_route_tag_policy.py @@ -0,0 +1,255 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_l3out_route_tag_policy +short_description: Manage route tag policies (l3ext:RouteTagPol) +description: +- Manage route tag policies on Cisco ACI fabrics. +options: + rtp: + description: + - The name of the route tag policy. + type: str + aliases: [ name, rtp_name ] + description: + description: + - The description for the route tag policy. + type: str + aliases: [ descr ] + tenant: + description: + - The name of the tenant. + type: str + aliases: [ tenant_name ] + tag: + description: + - The value of the route tag. + - Accepted values range between C(0) and C(4294967295). + - The APIC defaults to C(4294967295) when unset during creation. + type: int + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(l3ext:RouteTagPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- cisco.aci.aci_l3out_route_tag_policy: + host: apic + username: admin + password: SomeSecretPassword + rtp: '{{ rtp_name }}' + tenant: production + tag: '{{ tag }}' + description: '{{ description }}' + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + rtp=dict(type='str', aliases=['name', 'rtp_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + tag=dict(type='int'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['rtp', 'tenant']], + ['state', 'present', ['rtp', 'tenant']], + ], + ) + + rtp = module.params.get('rtp') + description = module.params.get('description') + tag = module.params.get('tag') + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='l3extRouteTagPol', + aci_rn='rttag-{0}'.format(rtp), + module_object=rtp, + target_filter={'name': rtp}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='l3extRouteTagPol', + class_config=dict( + name=rtp, + descr=description, tag=tag, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='l3extRouteTagPol') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_static_routes.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_static_routes.py new file mode 100644 index 00000000..2faf6e3b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_l3out_static_routes.py @@ -0,0 +1,368 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Anvitha Jain(@anvitha-jain) <anvjain@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_l3out_static_routes +short_description: Manage Static routes object (l3ext:ipRouteP) +description: +- Manage External Subnet objects (l3ext:ipRouteP) +options: + description: + description: + - The description for the static routes. + type: str + aliases: [ descr ] + tenant: + description: + - Name of an existing tenant. + type: str + aliases: [ tenant_name ] + l3out: + description: + - Name of an existing L3Out. + type: str + aliases: [ l3out_name ] + logical_node: + description: + - Name of an existing logical node profile. + type: str + pod_id: + description: + - Existing podId. + type: int + node_id: + description: + - Existing nodeId. + type: int + prefix: + description: + - Configure IP and next hop IP for the routed outside network. + type: str + aliases: [ route ] + track_policy: + description: + - Relation definition for static route to TrackList. + type: str + preference: + description: + - Administrative preference value for the route. + type: int + bfd: + description: + - Determines if bfd is required for route control. + - The APIC defaults to C(null) when unset during creation. + type: str + choices: [ bfd, null ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant), C(l3out), C(logical_node), C(fabric_node) and C(prefix) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_l3out) modules can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- module: cisco.aci.aci_l3out +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(l3ext:Out). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Anvitha Jain(@anvitha-jain) +''' + +EXAMPLES = r''' +- name: Create static routes + cisco.aci.aci_l3out_static_routes: + host: apic + username: admin + password: SomeSecretPassword + tenant: tenantName + l3out: l3out + logical_node: nodeName + node_id: 101 + pod_id: 1 + prefix: 10.10.0.0/16 + delegate_to: localhost + +- name: Delete static routes + cisco.aci.aci_l3out_static_routes: + host: apic + username: admin + password: SomeSecretPassword + tenant: tenantName + l3out: l3out + logical_node: nodeName + node_id: 101 + pod_id: 1 + prefix: 10.10.0.0/16 + delegate_to: localhost + +- name: Query for a specific MO under l3out + cisco.aci.aci_l3out_static_routes: + host: apic + username: admin + password: SomeSecretPassword + tenant: tenantName + l3out: l3out + logical_node: nodeName + node_id: 101 + pod_id: 1 + prefix: 10.10.0.0/16 + delegate_to: localhost + +- name: Query for all static routes + cisco.aci.aci_l3out_static_routes: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + l3out=dict(type='str', aliases=['l3out_name']), # Not required for querying all objects + logical_node=dict(type='str'), # Not required for querying all objects + pod_id=dict(type='int'), + node_id=dict(type='int'), + prefix=dict(type='str', aliases=['route']), + track_policy=dict(type='str'), + preference=dict(type='int'), + bfd=dict(type='str', choices=['bfd', None]), + description=dict(type='str', aliases=['descr']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['prefix', 'node_id', 'pod_id', 'logical_node', 'l3out', 'tenant']], + ['state', 'absent', ['prefix', 'node_id', 'pod_id', 'logical_node', 'l3out', 'tenant']], + ], + ) + + aci = ACIModule(module) + + tenant = module.params.get('tenant') + l3out = module.params.get('l3out') + logical_node = module.params.get('logical_node') + node_id = module.params.get('node_id') + pod_id = module.params.get('pod_id') + prefix = module.params.get('prefix') + track_policy = module.params.get('track_policy') + preference = module.params.get('preference') + bfd = module.params.get('bfd') + description = module.params.get('description') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + fabric_node = 'topology/pod-{0}/node-{1}'.format(pod_id, node_id) + child_classes = ['ipNexthopP'] + if track_policy is not None: + child_classes.append('ipRsRouteTrack') + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='l3extOut', + aci_rn='out-{0}'.format(l3out), + module_object=l3out, + target_filter={'name': l3out}, + ), + subclass_2=dict( + aci_class='l3extLNodeP', + aci_rn='lnodep-{0}'.format(logical_node), + module_object=logical_node, + target_filter={'name': logical_node}, + ), + subclass_3=dict( + aci_class='l3extRsNodeL3OutAtt', + aci_rn='/rsnodeL3OutAtt-[{0}]'.format(fabric_node), + module_object=fabric_node, + target_filter={'name': fabric_node}, + ), + subclass_4=dict( + aci_class='ipRouteP', + aci_rn='/rt-[{0}]'.format(prefix), + module_object=prefix, + target_filter={'name': prefix}, + ), + child_classes=child_classes + ) + + aci.get_existing() + + if state == 'present': + child_configs = [] + class_config = dict( + descr=description, + ip=prefix, + pref=preference, + nameAlias=name_alias, + ) + if bfd is not None: + class_config['rtCtrl'] = bfd + + if track_policy is not None: + tDn = 'uni/tn-{0}/tracklist-{1}'.format(tenant, track_policy) + child_configs.append({'ipRsRouteTrack': {'attributes': {'tDn': tDn}}}) + + aci.payload( + aci_class='ipRouteP', + class_config=class_config, + child_configs=child_configs + ), + + aci.get_diff(aci_class='ipRouteP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group.py new file mode 100644 index 00000000..2df19b7d --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group.py @@ -0,0 +1,236 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +module: aci_maintenance_group +short_description: This creates an ACI maintenance group +notes: + - a maintenance policy (aci_maintenance_policy must be created prior to creating an aci maintenance group +description: + - This modules creates an ACI maintenance group +options: + group: + description: + - This is the name of the group + type: str + policy: + description: + - This is the name of the policy that was created using aci_maintenance_policy + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [absent, present, query] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +author: + - Steven Gerhart (@sgerhart) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- name: maintenance group + cisco.aci.aci_maintenance_group: + host: "{{ inventory_hostname }}" + username: "{{ user }}" + password: "{{ pass }}" + validate_certs: no + group: maintenancegrp1 + policy: maintenancePol1 + state: present +''' + +RETURN = ''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + group=dict(type='str'), # Not required for querying all objects + policy=dict(type='str'), # Not required for querying all objects + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['group']], + ['state', 'present', ['group']], + ], + ) + + state = module.params.get('state') + group = module.params.get('group') + policy = module.params.get('policy') + name_alias = module.params.get('name_alias') + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='maintMaintGrp', + aci_rn='fabric/maintgrp-{0}'.format(group), + target_filter={'name': group}, + module_object=group, + ), + child_classes=['maintRsMgrpp'], + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='maintMaintGrp', + class_config=dict( + name=group, + nameAlias=name_alias, + ), + child_configs=[ + dict( + maintRsMgrpp=dict( + attributes=dict( + tnMaintMaintPName=policy, + ), + ), + ), + ], + + ) + + aci.get_diff(aci_class='maintMaintGrp') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group_node.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group_node.py new file mode 100644 index 00000000..3043c466 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group_node.py @@ -0,0 +1,242 @@ +#!/usr/bin/python + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = r''' +--- +module: aci_maintenance_group_node +short_description: Manage maintenance group nodes +description: +- Manage maintenance group nodes +options: + group: + description: + - The maintenance group name that you want to add the node to. + type: str + node: + description: + - The node to be added to the maintenance group. + - The value equals the nodeid. + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +author: +- Steven Gerhart (@sgerhart) +''' + +EXAMPLES = r''' +- name: maintenance group + cisco.aci.aci_maintenance_group_node: + host: "{{ inventory_hostname }}" + username: "{{ user }}" + password: "{{ pass }}" + validate_certs: no + group: maintenancegrp1 + node: 1001 + state: present + +- name: maintenance group + cisco.aci.aci_maintenance_group_node: + host: "{{ inventory_hostname }}" + username: "{{ user }}" + password: "{{ pass }}" + validate_certs: no + group: maintenancegrp1 + node: 1002 + state: absent +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json + +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + group=dict(type='str'), # Not required for querying all objects + node=dict(type='str'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['node', 'group']], + ['state', 'present', ['node', 'group']], + ], + ) + + state = module.params.get('state') + group = module.params.get('group') + node = module.params.get('node') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='maintMaintGrp', + aci_rn='fabric/maintgrp-{0}'.format(group), + target_filter={'name': group}, + module_object=group, + ), + subclass_1=dict( + aci_class='fabricNodeBlk', + aci_rn='nodeblk-blk{0}-{0}'.format(node), + target_filter={'name': 'blk{0}-{0}'.format(node)}, + module_object=node, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fabricNodeBlk', + class_config=dict( + from_=node, + to_=node, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='fabricNodeBlk') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_policy.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_policy.py new file mode 100644 index 00000000..b067e1d5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_policy.py @@ -0,0 +1,277 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = r''' +--- +module: aci_maintenance_policy +short_description: Manage firmware maintenance policies +description: +- Manage maintenance policies that defines behavior during an ACI upgrade. +options: + name: + description: + - The name for the maintenance policy. + type: str + aliases: [ maintenance_policy ] + runmode: + description: + - Whether the system pauses on error or just continues through it. + type: str + choices: [ pauseOnlyOnFailures, pauseNever ] + default: pauseOnlyOnFailures + graceful: + description: + - Whether the system will bring down the nodes gracefully during an upgrade, which reduces traffic lost. + - The APIC defaults to C(no) when unset during creation. + type: bool + scheduler: + description: + - The name of scheduler that is applied to the policy. + type: str + adminst: + description: + - Will trigger an immediate upgrade for nodes if adminst is set to triggered. + type: str + choices: [ triggered, untriggered ] + default: untriggered + ignoreCompat: + description: + - To check whether compatibility checks should be ignored + - The APIC defaults to C(no) when unset during creation. + type: bool + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- A scheduler is required for this module, which could have been created using the M(cisco.aci.aci_fabric_scheduler) module or via the UI. +author: +- Steven Gerhart (@sgerhart) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- name: Ensure maintenance policy is present + cisco.aci.aci_maintenance_policy: + host: '{{ inventory_hostname }}' + username: '{{ user }}' + password: '{{ pass }}' + validate_certs: no + name: maintenancePol1 + scheduler: simpleScheduler + runmode: False + state: present +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + name=dict(type='str', aliases=['maintenance_policy']), # Not required for querying all objects + runmode=dict(type='str', default='pauseOnlyOnFailures', choices=['pauseOnlyOnFailures', 'pauseNever']), + graceful=dict(type='bool'), + scheduler=dict(type='str'), + ignoreCompat=dict(type='bool'), + adminst=dict(type='str', default='untriggered', choices=['triggered', 'untriggered']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['name']], + ['state', 'present', ['name', 'scheduler']], + ], + ) + + aci = ACIModule(module) + + state = module.params.get('state') + name = module.params.get('name') + runmode = module.params.get('runmode') + scheduler = module.params.get('scheduler') + adminst = module.params.get('adminst') + graceful = aci.boolean(module.params.get('graceful')) + ignoreCompat = aci.boolean(module.params.get('ignoreCompat')) + name_alias = module.params.get('name_alias') + + aci.construct_url( + root_class=dict( + aci_class='maintMaintP', + aci_rn='fabric/maintpol-{0}'.format(name), + target_filter={'name': name}, + module_object=name, + ), + child_classes=['maintRsPolScheduler'] + + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='maintMaintP', + class_config=dict( + name=name, + runMode=runmode, + graceful=graceful, + adminSt=adminst, + ignoreCompat=ignoreCompat, + nameAlias=name_alias, + ), + child_configs=[ + dict( + maintRsPolScheduler=dict( + attributes=dict( + tnTrigSchedPName=scheduler, + ), + ), + ), + ], + + ) + + aci.get_diff(aci_class='maintMaintP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_rest.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_rest.py new file mode 100644 index 00000000..2e18c548 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_rest.py @@ -0,0 +1,451 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> +# Copyright: (c) 2020, Cindy Zhao (@cizhao) <cizhao@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_rest +short_description: Direct access to the Cisco APIC REST API +description: +- Enables the management of the Cisco ACI fabric through direct access to the Cisco APIC REST API. +- Thanks to the idempotent nature of the APIC, this module is idempotent and reports changes. +requirements: +- lxml (when using XML payload) +- xmljson >= 0.1.8 (when using XML payload) +- python 2.7+ (when using xmljson) +options: + method: + description: + - The HTTP method of the request. + - Using C(delete) is typically used for deleting objects. + - Using C(get) is typically used for querying objects. + - Using C(post) is typically used for modifying objects. + type: str + choices: [ delete, get, post ] + default: get + aliases: [ action ] + path: + description: + - URI being used to execute API calls. + - Must end in C(.xml) or C(.json). + type: str + required: yes + aliases: [ uri ] + content: + description: + - When used instead of C(src), sets the payload of the API request directly. + - This may be convenient to template simple requests. + - For anything complex use the C(template) lookup plugin (see examples) + or the M(template) module with parameter C(src). + type: raw + src: + description: + - Name of the absolute path of the filename that includes the body + of the HTTP request being sent to the ACI fabric. + - If you require a templated payload, use the C(content) parameter + together with the C(template) lookup plugin, or use M(template). + type: path + aliases: [ config_file ] +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- Certain payloads are known not to be idempotent, so be careful when constructing payloads, + e.g. using C(status="created") will cause idempotency issues, use C(status="modified") instead. + More information in :ref:`the ACI documentation <aci_guide_known_issues>`. +- Certain payloads (and used paths) are known to report no changes happened when changes did happen. + This is a known APIC problem and has been reported to the vendor. A workaround for this issue exists. + More information in :ref:`the ACI documentation <aci_guide_known_issues>`. +- XML payloads require the C(lxml) and C(xmljson) python libraries. For JSON payloads nothing special is needed. +- If you do not have any attributes, it may be necessary to add the "attributes" key with an empty dictionnary "{}" for value + as the APIC does expect the entry to precede any children. +seealso: +- module: cisco.aci.aci_tenant +- name: Cisco APIC REST API Configuration Guide + description: More information about the APIC REST API. + link: http://www.cisco.com/c/en/us/td/docs/switches/datacenter/aci/apic/sw/2-x/rest_cfg/2_1_x/b_Cisco_APIC_REST_API_Configuration_Guide.html +author: +- Dag Wieers (@dagwieers) +- Cindy Zhao (@cizhao) +''' + +EXAMPLES = r''' +- name: Add a tenant using certificate authentication + cisco.aci.aci_rest: + host: apic + username: admin + private_key: pki/admin.key + method: post + path: /api/mo/uni.xml + src: /home/cisco/ansible/aci/configs/aci_config.xml + delegate_to: localhost + +- name: Add a tenant from a templated payload file from templates/ + cisco.aci.aci_rest: + host: apic + username: admin + private_key: pki/admin.key + method: post + path: /api/mo/uni.xml + content: "{{ lookup('template', 'aci/tenant.xml.j2') }}" + delegate_to: localhost + +- name: Add a tenant using inline YAML + cisco.aci.aci_rest: + host: apic + username: admin + private_key: pki/admin.key + validate_certs: no + path: /api/mo/uni.json + method: post + content: + fvTenant: + attributes: + name: Sales + descr: Sales department + delegate_to: localhost + +- name: Add a tenant using a JSON string + cisco.aci.aci_rest: + host: apic + username: admin + private_key: pki/admin.key + validate_certs: no + path: /api/mo/uni.json + method: post + content: + { + "fvTenant": { + "attributes": { + "name": "Sales", + "descr": "Sales department" + } + } + } + delegate_to: localhost + +- name: Add a tenant using an XML string + cisco.aci.aci_rest: + host: apic + username: admin + private_key: pki/{{ aci_username }}.key + validate_certs: no + path: /api/mo/uni.xml + method: post + content: '<fvTenant name="Sales" descr="Sales departement"/>' + delegate_to: localhost + +- name: Get tenants using password authentication + cisco.aci.aci_rest: + host: apic + username: admin + password: SomeSecretPassword + method: get + path: /api/node/class/fvTenant.json + delegate_to: localhost + register: query_result + +- name: Configure contracts + cisco.aci.aci_rest: + host: apic + username: admin + private_key: pki/admin.key + method: post + path: /api/mo/uni.xml + src: /home/cisco/ansible/aci/configs/contract_config.xml + delegate_to: localhost + +- name: Register leaves and spines + cisco.aci.aci_rest: + host: apic + username: admin + private_key: pki/admin.key + validate_certs: no + method: post + path: /api/mo/uni/controller/nodeidentpol.xml + content: + <fabricNodeIdentPol> + <fabricNodeIdentP name="{{ item.name }}" nodeId="{{ item.nodeid }}" status="{{ item.status }}" serial="{{ item.serial }}"/> + </fabricNodeIdentPol> + with_items: + - '{{ apic_leavesspines }}' + delegate_to: localhost + +- name: Wait for all controllers to become ready + cisco.aci.aci_rest: + host: apic + username: admin + private_key: pki/admin.key + validate_certs: no + path: /api/node/class/topSystem.json?query-target-filter=eq(topSystem.role,"controller") + register: apics + until: "'totalCount' in apics and apics.totalCount|int >= groups['apic']|count" + retries: 120 + delay: 30 + delegate_to: localhost + run_once: yes +''' + +RETURN = r''' +error_code: + description: The REST ACI return code, useful for troubleshooting on failure + returned: always + type: int + sample: 122 +error_text: + description: The REST ACI descriptive text, useful for troubleshooting on failure + returned: always + type: str + sample: unknown managed object class foo +imdata: + description: Converted output returned by the APIC REST (register this for post-processing) + returned: always + type: str + sample: [{"error": {"attributes": {"code": "122", "text": "unknown managed object class foo"}}}] +payload: + description: The (templated) payload send to the APIC REST API (xml or json) + returned: always + type: str + sample: '<foo bar="boo"/>' +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +response: + description: HTTP response string + returned: always + type: str + sample: 'HTTP Error 400: Bad Request' +status: + description: HTTP status code + returned: always + type: int + sample: 400 +totalCount: + description: Number of items in the imdata array + returned: always + type: str + sample: '0' +url: + description: URL used for APIC REST call + returned: success + type: str + sample: https://1.2.3.4/api/mo/uni/tn-[Dag].json?rsp-subtree=modified +''' + +import json +import os + +try: + from ansible.module_utils.six.moves.urllib.parse import parse_qsl, urlencode, urlparse, urlunparse + HAS_URLPARSE = True +except Exception: + HAS_URLPARSE = False + +# Optional, only used for XML payload +try: + from lxml import etree # noqa + HAS_LXML_ETREE = True +except ImportError: + HAS_LXML_ETREE = False + +# Optional, only used for XML payload +try: + from xmljson import cobra # noqa + HAS_XMLJSON_COBRA = True +except ImportError: + HAS_XMLJSON_COBRA = False + +# Optional, only used for YAML validation +try: + import yaml + HAS_YAML = True +except Exception: + HAS_YAML = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.urls import fetch_url +from ansible.module_utils._text import to_text + + +def update_qsl(url, params): + ''' Add or update a URL query string ''' + + if HAS_URLPARSE: + url_parts = list(urlparse(url)) + query = dict(parse_qsl(url_parts[4])) + query.update(params) + url_parts[4] = urlencode(query) + return urlunparse(url_parts) + elif '?' in url: + return url + '&' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()]) + else: + return url + '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()]) + + +class ACIRESTModule(ACIModule): + + def changed(self, d): + ''' Check ACI response for changes ''' + + if isinstance(d, dict): + for k, v in d.items(): + if k == 'status' and v in ('created', 'modified', 'deleted'): + return True + elif self.changed(v) is True: + return True + elif isinstance(d, list): + for i in d: + if self.changed(i) is True: + return True + + return False + + def response_type(self, rawoutput, rest_type='xml'): + ''' Handle APIC response output ''' + + if rest_type == 'json': + self.response_json(rawoutput) + else: + self.response_xml(rawoutput) + + # Use APICs built-in idempotency + if HAS_URLPARSE: + self.result['changed'] = self.changed(self.imdata) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + path=dict(type='str', required=True, aliases=['uri']), + method=dict(type='str', default='get', choices=['delete', 'get', 'post'], aliases=['action']), + src=dict(type='path', aliases=['config_file']), + content=dict(type='raw'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=[['content', 'src']], + ) + + content = module.params.get('content') + path = module.params.get('path') + src = module.params.get('src') + + # Report missing file + file_exists = False + if src: + if os.path.isfile(src): + file_exists = True + else: + module.fail_json(msg="Cannot find/access src '%s'" % src) + + # Find request type + if path.find('.xml') != -1: + rest_type = 'xml' + if not HAS_LXML_ETREE: + module.fail_json(msg='The lxml python library is missing, or lacks etree support.') + if not HAS_XMLJSON_COBRA: + module.fail_json(msg='The xmljson python library is missing, or lacks cobra support.') + elif path.find('.json') != -1: + rest_type = 'json' + else: + module.fail_json(msg='Failed to find REST API payload type (neither .xml nor .json).') + + aci = ACIRESTModule(module) + aci.result['status'] = -1 # Ensure we always return a status + + # We include the payload as it may be templated + payload = content + if file_exists: + with open(src, 'r') as config_object: + # TODO: Would be nice to template this, requires action-plugin + payload = config_object.read() + payload_output_file = json.loads(payload) + + # Validate payload + if rest_type == 'json': + if content and isinstance(content, dict): + # Validate inline YAML/JSON + payload = json.dumps(payload) + elif payload and isinstance(payload, str) and HAS_YAML: + try: + # Validate YAML/JSON string + payload = json.dumps(yaml.safe_load(payload)) + except Exception as e: + module.fail_json(msg='Failed to parse provided JSON/YAML payload: %s' % to_text(e), exception=to_text(e), payload=payload) + elif rest_type == 'xml' and HAS_LXML_ETREE: + if content and isinstance(content, dict) and HAS_XMLJSON_COBRA: + # Validate inline YAML/JSON + payload = etree.tostring(cobra.etree(payload)[0]) + elif payload and isinstance(payload, str): + try: + # Validate XML string + payload = etree.tostring(etree.fromstring(payload)) + except Exception as e: + module.fail_json(msg='Failed to parse provided XML payload: %s' % to_text(e), payload=payload) + + # Perform actual request using auth cookie (Same as aci.request(), but also supports XML) + if 'port' in aci.params and aci.params.get('port') is not None: + aci.url = '%(protocol)s://%(host)s:%(port)s/' % aci.params + path.lstrip('/') + else: + aci.url = '%(protocol)s://%(host)s/' % aci.params + path.lstrip('/') + if aci.params.get('method') != 'get': + path += '?rsp-subtree=modified' + aci.url = update_qsl(aci.url, {'rsp-subtree': 'modified'}) + + # Sign and encode request as to APIC's wishes + if aci.params.get('private_key') is not None: + aci.cert_auth(path=path, payload=payload) + + aci.method = aci.params.get('method').upper() + + # Perform request + resp, info = fetch_url(module, aci.url, + data=payload, + headers=aci.headers, + method=aci.method, + timeout=aci.params.get('timeout'), + use_proxy=aci.params.get('use_proxy')) + + aci.response = info.get('msg') + aci.status = info.get('status') + + # Report failure + if info.get('status') != 200: + try: + # APIC error + aci.response_type(info.get('body'), rest_type) + aci.fail_json(msg='APIC Error %(code)s: %(text)s' % aci.error) + except KeyError: + # Connection error + aci.fail_json(msg='Connection failed for %(url)s. %(msg)s' % info) + + aci.response_type(resp.read(), rest_type) + + aci.result['imdata'] = aci.imdata + aci.result['totalCount'] = aci.totalCount + + output_path = aci.params.get('output_path') + if(output_path is not None): + with open(output_path, "a") as output_file: + json.dump([payload_output_file], output_file) + + # Report success + aci.exit_json(**aci.result) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_static_binding_to_epg.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_static_binding_to_epg.py new file mode 100644 index 00000000..15677831 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_static_binding_to_epg.py @@ -0,0 +1,441 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_static_binding_to_epg +short_description: Bind static paths to EPGs (fv:RsPathAtt) +description: +- Bind static paths to EPGs on Cisco ACI fabrics. +options: + tenant: + description: + - Name of an existing tenant. + type: str + aliases: [ tenant_name ] + ap: + description: + - Name of an existing application network profile, that will contain the EPGs. + type: str + aliases: [ app_profile, app_profile_name ] + epg: + description: + - The name of the end point group. + type: str + aliases: [ epg_name ] + description: + description: + - Description for the static path to EPG binding. + type: str + aliases: [ descr ] + encap_id: + description: + - The encapsulation ID associating the C(epg) with the interface path. + - This acts as the secondary C(encap_id) when using micro-segmentation. + - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096). + type: int + aliases: [ vlan, vlan_id ] + primary_encap_id: + description: + - Determines the primary encapsulation ID associating the C(epg) + with the interface path when using micro-segmentation. + - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096). + type: int + aliases: [ primary_vlan, primary_vlan_id ] + deploy_immediacy: + description: + - The Deployment Immediacy of Static EPG on PC, VPC or Interface. + - The APIC defaults to C(lazy) when unset during creation. + type: str + choices: [ immediate, lazy ] + interface_mode: + description: + - Determines how layer 2 tags will be read from and added to frames. + - Values C(802.1p) and C(native) are identical. + - Values C(access) and C(untagged) are identical. + - Values C(regular), C(tagged) and C(trunk) are identical. + - The APIC defaults to C(trunk) when unset during creation. + type: str + choices: [ 802.1p, access, native, regular, tagged, trunk, untagged ] + aliases: [ interface_mode_name, mode ] + interface_type: + description: + - The type of interface for the static EPG deployment. + type: str + choices: [ fex, port_channel, switch_port, vpc ] + default: switch_port + pod_id: + description: + - The pod number part of the tDn. + - C(pod_id) is usually an integer below C(10). + type: int + aliases: [ pod, pod_number ] + leafs: + description: + - The switch ID(s) that the C(interface) belongs to. + - When C(interface_type) is C(switch_port), C(port_channel), or C(fex), then C(leafs) is a string of the leaf ID. + - When C(interface_type) is C(vpc), then C(leafs) is a list with both leaf IDs. + - The C(leafs) value is usually something like '101' or '101-102' depending on C(connection_type). + type: list + elements: str + aliases: [ leaves, nodes, paths, switches ] + interface: + description: + - The C(interface) string value part of the tDn. + - Usually a policy group like C(test-IntPolGrp) or an interface of the following format C(1/7) depending on C(interface_type). + type: str + extpaths: + description: + - The C(extpaths) integer value part of the tDn. + - C(extpaths) is only used if C(interface_type) is C(fex). + - Usually something like C(1011). + type: int + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant), C(ap), C(epg) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant), M(cisco.aci.aci_ap), M(cisco.aci.aci_epg) modules can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- module: cisco.aci.aci_ap +- module: cisco.aci.aci_epg +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fv:RsPathAtt). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Bruno Calogero (@brunocalogero) +''' + +EXAMPLES = r''' +- name: Deploy Static Path binding for given EPG + cisco.aci.aci_static_binding_to_epg: + host: apic + username: admin + password: SomeSecretPassword + tenant: accessport-code-cert + ap: accessport_code_app + epg: accessport_epg1 + encap_id: 222 + deploy_immediacy: lazy + interface_mode: untagged + interface_type: switch_port + pod_id: 1 + leafs: 101 + interface: '1/7' + state: present + delegate_to: localhost + +- name: Remove Static Path binding for given EPG + cisco.aci.aci_static_binding_to_epg: + host: apic + username: admin + password: SomeSecretPassword + tenant: accessport-code-cert + ap: accessport_code_app + epg: accessport_epg1 + interface_type: switch_port + pod: 1 + leafs: 101 + interface: '1/7' + state: absent + delegate_to: localhost + +- name: Get specific Static Path binding for given EPG + cisco.aci.aci_static_binding_to_epg: + host: apic + username: admin + password: SomeSecretPassword + tenant: accessport-code-cert + ap: accessport_code_app + epg: accessport_epg1 + interface_type: switch_port + pod: 1 + leafs: 101 + interface: '1/7' + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +INTERFACE_MODE_MAPPING = { + '802.1p': 'native', + 'access': 'untagged', + 'native': 'native', + 'regular': 'regular', + 'tagged': 'regular', + 'trunk': 'regular', + 'untagged': 'untagged', +} + +INTERFACE_TYPE_MAPPING = dict( + fex='topology/pod-{pod_id}/paths-{leafs}/extpaths-{extpaths}/pathep-[eth{interface}]', + port_channel='topology/pod-{pod_id}/paths-{leafs}/pathep-[{interface}]', + switch_port='topology/pod-{pod_id}/paths-{leafs}/pathep-[eth{interface}]', + vpc='topology/pod-{pod_id}/protpaths-{leafs}/pathep-[{interface}]', +) + +# TODO: change 'deploy_immediacy' to 'resolution_immediacy' (as seen in aci_epg_to_domain)? + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + ap=dict(type='str', aliases=['app_profile', 'app_profile_name']), # Not required for querying all objects + epg=dict(type='str', aliases=['epg_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + encap_id=dict(type='int', aliases=['vlan', 'vlan_id']), + primary_encap_id=dict(type='int', aliases=['primary_vlan', 'primary_vlan_id']), + deploy_immediacy=dict(type='str', choices=['immediate', 'lazy']), + interface_mode=dict(type='str', choices=['802.1p', 'access', 'native', 'regular', 'tagged', 'trunk', 'untagged'], + aliases=['interface_mode_name', 'mode']), + interface_type=dict(type='str', default='switch_port', choices=['fex', 'port_channel', 'switch_port', 'vpc']), + pod_id=dict(type='int', aliases=['pod', 'pod_number']), # Not required for querying all objects + leafs=dict(type='list', elements='str', aliases=['leaves', 'nodes', 'paths', 'switches']), # Not required for querying all objects + interface=dict(type='str'), # Not required for querying all objects + extpaths=dict(type='int'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['interface_type', 'fex', ['extpaths']], + ['state', 'absent', ['ap', 'epg', 'interface', 'leafs', 'pod_id', 'tenant']], + ['state', 'present', ['ap', 'encap_id', 'epg', 'interface', 'leafs', 'pod_id', 'tenant']], + ], + ) + + tenant = module.params.get('tenant') + ap = module.params.get('ap') + epg = module.params.get('epg') + description = module.params.get('description') + encap_id = module.params.get('encap_id') + primary_encap_id = module.params.get('primary_encap_id') + deploy_immediacy = module.params.get('deploy_immediacy') + interface_mode = module.params.get('interface_mode') + interface_type = module.params.get('interface_type') + pod_id = module.params.get('pod_id') + leafs = module.params.get('leafs') + if leafs is not None: + # Process leafs, and support dash-delimited leafs + leafs = [] + for leaf in module.params.get('leafs'): + # Users are likely to use integers for leaf IDs, which would raise an exception when using the join method + leafs.extend(str(leaf).split('-')) + if len(leafs) == 1: + if interface_type == 'vpc': + module.fail_json(msg='A interface_type of "vpc" requires 2 leafs') + leafs = leafs[0] + elif len(leafs) == 2: + if interface_type != 'vpc': + module.fail_json(msg='The interface_types "switch_port", "port_channel", and "fex" \ + do not support using multiple leafs for a single binding') + leafs = "-".join(leafs) + else: + module.fail_json(msg='The "leafs" parameter must not have more than 2 entries') + interface = module.params.get('interface') + extpaths = module.params.get('extpaths') + state = module.params.get('state') + + if encap_id is not None: + if encap_id not in range(1, 4097): + module.fail_json(msg='Valid VLAN assigments are from 1 to 4096') + encap_id = 'vlan-{0}'.format(encap_id) + + if primary_encap_id is not None: + if primary_encap_id not in range(1, 4097): + module.fail_json(msg='Valid VLAN assigments are from 1 to 4096') + primary_encap_id = 'vlan-{0}'.format(primary_encap_id) + + static_path = INTERFACE_TYPE_MAPPING[interface_type].format(pod_id=pod_id, leafs=leafs, extpaths=extpaths, interface=interface) + + path_target_filter = {} + if pod_id is not None and leafs is not None and interface is not None and (interface_type != 'fex' or extpaths is not None): + path_target_filter = {'tDn': static_path} + + if interface_mode is not None: + interface_mode = INTERFACE_MODE_MAPPING[interface_mode] + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='fvAp', + aci_rn='ap-{0}'.format(ap), + module_object=ap, + target_filter={'name': ap}, + ), + subclass_2=dict( + aci_class='fvAEPg', + aci_rn='epg-{0}'.format(epg), + module_object=epg, + target_filter={'name': epg}, + ), + subclass_3=dict( + aci_class='fvRsPathAtt', + aci_rn='rspathAtt-[{0}]'.format(static_path), + module_object=static_path, + target_filter=path_target_filter, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fvRsPathAtt', + class_config=dict( + descr=description, + encap=encap_id, + primaryEncap=primary_encap_id, + instrImedcy=deploy_immediacy, + mode=interface_mode, + tDn=static_path, + ), + ) + + aci.get_diff(aci_class='fvRsPathAtt') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_switch_leaf_selector.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_switch_leaf_selector.py new file mode 100644 index 00000000..0334fd84 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_switch_leaf_selector.py @@ -0,0 +1,349 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_switch_leaf_selector +short_description: Bind leaf selectors to switch policy leaf profiles (infra:LeafS, infra:NodeBlk, infra:RsAccNodePGrep) +description: +- Bind leaf selectors (with node block range and policy group) to switch policy leaf profiles on Cisco ACI fabrics. +options: + description: + description: + - The description to assign to the C(leaf). + type: str + leaf_profile: + description: + - Name of the Leaf Profile to which we add a Selector. + type: str + aliases: [ leaf_profile_name ] + leaf: + description: + - Name of Leaf Selector. + type: str + aliases: [ name, leaf_name, leaf_profile_leaf_name, leaf_selector_name ] + leaf_node_blk: + description: + - Name of Node Block range to be added to Leaf Selector of given Leaf Profile. + type: str + aliases: [ leaf_node_blk_name, node_blk_name ] + leaf_node_blk_description: + description: + - The description to assign to the C(leaf_node_blk) + type: str + from: + description: + - Start of Node Block range. + type: int + aliases: [ node_blk_range_from, from_range, range_from ] + to: + description: + - Start of Node Block range. + type: int + aliases: [ node_blk_range_to, to_range, range_to ] + policy_group: + description: + - Name of the Policy Group to be added to Leaf Selector of given Leaf Profile. + type: str + aliases: [ name, policy_group_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- This module is to be used with M(cisco.aci.aci_switch_policy_leaf_profile). + One first creates a leaf profile (infra:NodeP) and then creates an associated selector (infra:LeafS), +seealso: +- module: cisco.aci.aci_switch_policy_leaf_profile +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(infra:LeafS), + B(infra:NodeBlk) and B(infra:RsAccNodePGrp). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Bruno Calogero (@brunocalogero) +''' + +EXAMPLES = r''' +- name: adding a switch policy leaf profile selector associated Node Block range (w/ policy group) + cisco.aci.aci_switch_leaf_selector: + host: apic + username: admin + password: SomeSecretPassword + leaf_profile: sw_name + leaf: leaf_selector_name + leaf_node_blk: node_blk_name + from: 1011 + to: 1011 + policy_group: somepolicygroupname + state: present + delegate_to: localhost + +- name: adding a switch policy leaf profile selector associated Node Block range (w/o policy group) + cisco.aci.aci_switch_leaf_selector: + host: apic + username: admin + password: SomeSecretPassword + leaf_profile: sw_name + leaf: leaf_selector_name + leaf_node_blk: node_blk_name + from: 1011 + to: 1011 + state: present + delegate_to: localhost + +- name: Removing a switch policy leaf profile selector + cisco.aci.aci_switch_leaf_selector: + host: apic + username: admin + password: SomeSecretPassword + leaf_profile: sw_name + leaf: leaf_selector_name + state: absent + delegate_to: localhost + +- name: Querying a switch policy leaf profile selector + cisco.aci.aci_switch_leaf_selector: + host: apic + username: admin + password: SomeSecretPassword + leaf_profile: sw_name + leaf: leaf_selector_name + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update({ + 'description': dict(type='str'), + 'leaf_profile': dict(type='str', aliases=['leaf_profile_name']), # Not required for querying all objects + 'leaf': dict(type='str', aliases=['name', 'leaf_name', 'leaf_profile_leaf_name', 'leaf_selector_name']), # Not required for querying all objects + 'leaf_node_blk': dict(type='str', aliases=['leaf_node_blk_name', 'node_blk_name']), + 'leaf_node_blk_description': dict(type='str'), + # NOTE: Keyword 'from' is a reserved word in python, so we need it as a string + 'from': dict(type='int', aliases=['node_blk_range_from', 'from_range', 'range_from']), + 'to': dict(type='int', aliases=['node_blk_range_to', 'to_range', 'range_to']), + 'policy_group': dict(type='str', aliases=['policy_group_name']), + 'state': dict(type='str', default='present', choices=['absent', 'present', 'query']), + 'name_alias': dict(type='str'), + }) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['leaf_profile', 'leaf']], + ['state', 'present', ['leaf_profile', 'leaf', 'leaf_node_blk', 'from', 'to']] + ] + ) + + description = module.params.get('description') + leaf_profile = module.params.get('leaf_profile') + leaf = module.params.get('leaf') + leaf_node_blk = module.params.get('leaf_node_blk') + leaf_node_blk_description = module.params.get('leaf_node_blk_description') + from_ = module.params.get('from') + to_ = module.params.get('to') + policy_group = module.params.get('policy_group') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + # Build child_configs dynamically + child_configs = [ + dict( + infraNodeBlk=dict( + attributes=dict( + descr=leaf_node_blk_description, + name=leaf_node_blk, + from_=from_, + to_=to_, + ), + ), + ), + ] + + # Add infraRsAccNodePGrp only when policy_group was defined + if policy_group is not None: + child_configs.append(dict( + infraRsAccNodePGrp=dict( + attributes=dict( + tDn='uni/infra/funcprof/accnodepgrp-{0}'.format(policy_group), + ), + ), + )) + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='infraNodeP', + aci_rn='infra/nprof-{0}'.format(leaf_profile), + module_object=leaf_profile, + target_filter={'name': leaf_profile}, + ), + subclass_1=dict( + aci_class='infraLeafS', + # NOTE: normal rn: leaves-{name}-typ-{type}, hence here hardcoded to range for purposes of module + aci_rn='leaves-{0}-typ-range'.format(leaf), + module_object=leaf, + target_filter={'name': leaf}, + ), + # NOTE: infraNodeBlk is not made into a subclass because there is a 1-1 mapping between node block and leaf selector name + child_classes=['infraNodeBlk', 'infraRsAccNodePGrp'], + + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='infraLeafS', + class_config=dict( + descr=description, + name=leaf, + nameAlias=name_alias, + ), + child_configs=child_configs, + ) + + aci.get_diff(aci_class='infraLeafS') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_leaf_profile.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_leaf_profile.py new file mode 100644 index 00000000..932a0d8b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_leaf_profile.py @@ -0,0 +1,250 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_switch_policy_leaf_profile +short_description: Manage switch policy leaf profiles (infra:NodeP) +description: +- Manage switch policy leaf profiles on Cisco ACI fabrics. +options: + leaf_profile: + description: + - The name of the Leaf Profile. + type: str + aliases: [ leaf_profile_name, name ] + description: + description: + - Description for the Leaf Profile. + type: str + aliases: [ descr ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- module: cisco.aci.aci_switch_policy_leaf_profile +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(infra:NodeP). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Bruno Calogero (@brunocalogero) +''' + +EXAMPLES = r''' +- name: creating a Leaf Profile with description + cisco.aci.aci_switch_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + leaf_profile: sw_name + description: sw_description + state: present + delegate_to: localhost + +- name: Deleting a Leaf Profile + cisco.aci.aci_switch_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + leaf_profile: sw_name + state: absent + delegate_to: localhost + +- name: Query a Leaf Profile + cisco.aci.aci_switch_policy_leaf_profile: + host: apic + username: admin + password: SomeSecretPassword + leaf_profile: sw_name + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + leaf_profile=dict(type='str', aliases=['name', 'leaf_profile_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['leaf_profile']], + ['state', 'present', ['leaf_profile']], + ], + ) + + leaf_profile = module.params.get('leaf_profile') + description = module.params.get('description') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='infraNodeP', + aci_rn='infra/nprof-{0}'.format(leaf_profile), + module_object=leaf_profile, + target_filter={'name': leaf_profile}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='infraNodeP', + class_config=dict( + name=leaf_profile, + descr=description, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='infraNodeP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_vpc_protection_group.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_vpc_protection_group.py new file mode 100644 index 00000000..184f20dd --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_vpc_protection_group.py @@ -0,0 +1,304 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_switch_policy_vpc_protection_group +short_description: Manage switch policy explicit vPC protection groups (fabric:ExplicitGEp, fabric:NodePEp). +description: +- Manage switch policy explicit vPC protection groups on Cisco ACI fabrics. +options: + protection_group: + description: + - The name of the Explicit vPC Protection Group. + type: str + aliases: [ name, protection_group_name ] + protection_group_id: + description: + - The Explicit vPC Protection Group ID. + type: int + aliases: [ id ] + vpc_domain_policy: + description: + - The vPC domain policy to be associated with the Explicit vPC Protection Group. + type: str + aliases: [ vpc_domain_policy_name ] + switch_1_id: + description: + - The ID of the first Leaf Switch for the Explicit vPC Protection Group. + type: int + switch_2_id: + description: + - The ID of the Second Leaf Switch for the Explicit vPC Protection Group. + type: int + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- module: cisco.aci.aci_switch_policy_leaf_profile +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(fabric:ExplicitGEp) and B(fabric:NodePEp). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Bruno Calogero (@brunocalogero) +''' + +EXAMPLES = r''' +- name: Add vPC Protection Group + cisco.aci.aci_switch_policy_vpc_protection_group: + host: apic + username: admin + password: SomeSecretPassword + protection_group: leafPair101-vpcGrp + protection_group_id: 6 + switch_1_id: 1011 + switch_2_id: 1012 + state: present + delegate_to: localhost + +- name: Remove Explicit vPC Protection Group + cisco.aci.aci_switch_policy_vpc_protection_group: + host: apic + username: admin + password: SomeSecretPassword + protection_group: leafPair101-vpcGrp + state: absent + delegate_to: localhost + +- name: Query vPC Protection Groups + cisco.aci.aci_switch_policy_vpc_protection_group: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result + +- name: Query our vPC Protection Group + cisco.aci.aci_switch_policy_vpc_protection_group: + host: apic + username: admin + password: SomeSecretPassword + protection_group: leafPair101-vpcGrp + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + protection_group=dict(type='str', aliases=['name', 'protection_group_name']), # Not required for querying all objects + protection_group_id=dict(type='int', aliases=['id']), + vpc_domain_policy=dict(type='str', aliases=['vpc_domain_policy_name']), + switch_1_id=dict(type='int'), + switch_2_id=dict(type='int'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['protection_group']], + ['state', 'present', ['protection_group', 'protection_group_id', 'switch_1_id', 'switch_2_id']], + ], + ) + + protection_group = module.params.get('protection_group') + protection_group_id = module.params.get('protection_group_id') + vpc_domain_policy = module.params.get('vpc_domain_policy') + switch_1_id = module.params.get('switch_1_id') + switch_2_id = module.params.get('switch_2_id') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fabricExplicitGEp', + aci_rn='fabric/protpol/expgep-{0}'.format(protection_group), + module_object=protection_group, + target_filter={'name': protection_group}, + ), + child_classes=['fabricNodePEp', 'fabricNodePEp', 'fabricRsVpcInstPol'], + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fabricExplicitGEp', + class_config=dict( + name=protection_group, + id=protection_group_id, + nameAlias=name_alias, + ), + child_configs=[ + dict( + fabricNodePEp=dict( + attributes=dict( + id='{0}'.format(switch_1_id), + ), + ), + ), + dict( + fabricNodePEp=dict( + attributes=dict( + id='{0}'.format(switch_2_id), + ), + ), + ), + dict( + fabricRsVpcInstPol=dict( + attributes=dict( + tnVpcInstPolName=vpc_domain_policy, + ), + ), + ), + ], + ) + + aci.get_diff(aci_class='fabricExplicitGEp') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_system.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_system.py new file mode 100644 index 00000000..b4789adc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_system.py @@ -0,0 +1,197 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Lionel Hercot (@lhercot) <lhercot@cisco.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: aci_system +short_description: Query the ACI system information (top:System) +description: +- Query the ACI system information (top:System) on Cisco ACI. +author: +- Lionel Hercot (@lhercot) +options: + id: + description: + - The controller node ID + aliases: [ controller, node ] + type: int + state: + description: + - Use C(query) for listing an object or multiple objects. + choices: [ query ] + default: query + type: str +notes: +- More information about the internal APIC class B(top:System) from + L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/). +- This module is used to query system information for both cloud and on-premises controllers. + +extends_documentation_fragment: +- cisco.aci.aci +''' + +EXAMPLES = r''' +- name: Query all controllers system information + cisco.aci.aci_system: + host: apic + username: userName + password: somePassword + validate_certs: no + state: query + delegate_to: localhost + +- name: Query controller 1 specific system information + cisco.aci.aci_system: + host: apic + username: userName + password: somePassword + validate_certs: no + id: 1 + state: query + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + id=dict(type='int', aliases=['controller', 'node']), + state=dict(type='str', default='query', choices=['query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + id = module.params.get('id') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='topSystem', + target_filter={'id': id} + ) + ) + + aci.get_existing() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_taboo_contract.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_taboo_contract.py new file mode 100644 index 00000000..b730328c --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_taboo_contract.py @@ -0,0 +1,286 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_taboo_contract +short_description: Manage taboo contracts (vz:BrCP) +description: +- Manage taboo contracts on Cisco ACI fabrics. +options: + taboo_contract: + description: + - The name of the Taboo Contract. + type: str + aliases: [ name ] + description: + description: + - The description for the Taboo Contract. + type: str + aliases: [ descr ] + tenant: + description: + - The name of the tenant. + type: str + aliases: [ tenant_name ] + scope: + description: + - The scope of a service contract. + - The APIC defaults to C(context) when unset during creation. + type: str + choices: [ application-profile, context, global, tenant ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(vz:BrCP). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Add taboo contract + cisco.aci.aci_taboo_contract: + host: apic + username: admin + password: SomeSecretPassword + tenant: ansible_test + taboo_contract: taboo_contract_test + state: present + delegate_to: localhost + +- name: Remove taboo contract + cisco.aci.aci_taboo_contract: + host: apic + username: admin + password: SomeSecretPassword + tenant: ansible_test + taboo_contract: taboo_contract_test + state: absent + delegate_to: localhost + +- name: Query all taboo contracts + cisco.aci.aci_taboo_contract: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result + +- name: Query a specific taboo contract + cisco.aci.aci_taboo_contract: + host: apic + username: admin + password: SomeSecretPassword + tenant: ansible_test + taboo_contract: taboo_contract_test + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + taboo_contract=dict(type='str', aliases=['name']), # Not required for querying all contracts + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all contracts + scope=dict(type='str', choices=['application-profile', 'context', 'global', 'tenant']), + description=dict(type='str', aliases=['descr']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['tenant', 'taboo_contract']], + ['state', 'present', ['tenant', 'taboo_contract']], + ], + ) + + taboo_contract = module.params.get('taboo_contract') + description = module.params.get('description') + scope = module.params.get('scope') + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='vzTaboo', + aci_rn='taboo-{0}'.format(taboo_contract), + module_object=taboo_contract, + target_filter={'name': taboo_contract}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='vzTaboo', + class_config=dict( + name=taboo_contract, + descr=description, + scope=scope, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='vzTaboo') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant.py new file mode 100644 index 00000000..26ee14cf --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant.py @@ -0,0 +1,261 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_tenant +short_description: Manage tenants (fv:Tenant) +description: +- Manage tenants on Cisco ACI fabrics. +options: + tenant: + description: + - The name of the tenant. + type: str + aliases: [ name, tenant_name ] + description: + description: + - Description for the tenant. + type: str + aliases: [ descr ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- module: cisco.aci.aci_ap +- module: cisco.aci.aci_bd +- module: cisco.aci.aci_contract +- module: cisco.aci.aci_filter +- module: cisco.aci.aci_vrf +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fv:Tenant). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +EXAMPLES = r''' +- name: Add a new tenant + cisco.aci.aci_tenant: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + description: Production tenant + state: present + delegate_to: localhost + +- name: Remove a tenant + cisco.aci.aci_tenant: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + state: absent + delegate_to: localhost + +- name: Query a tenant + cisco.aci.aci_tenant: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + state: query + delegate_to: localhost + register: query_result + +- name: Query all tenants + cisco.aci.aci_tenant: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['name', 'tenant_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['tenant']], + ['state', 'present', ['tenant']], + ], + ) + + description = module.params.get('description') + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + ) + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fvTenant', + class_config=dict( + name=tenant, + descr=description, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='fvTenant') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_action_rule_profile.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_action_rule_profile.py new file mode 100644 index 00000000..ca8393d2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_action_rule_profile.py @@ -0,0 +1,246 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_tenant_action_rule_profile +short_description: Manage action rule profiles (rtctrl:AttrP) +description: +- Manage action rule profiles on Cisco ACI fabrics. +options: + action_rule: + description: + - The name of the action rule profile. + type: str + aliases: [ action_rule_name, name ] + description: + description: + - The description for the action rule profile. + type: str + aliases: [ descr ] + tenant: + description: + - The name of the tenant. + type: str + aliases: [ tenant_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(rtctrl:AttrP). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- cisco.aci.aci_tenant_action_rule_profile: + host: apic + username: admin + password: SomeSecretPassword + action_rule: '{{ action_rule }}' + description: '{{ descr }}' + tenant: '{{ tenant }}' + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + action_rule=dict(type='str', aliases=['action_rule_name', 'name']), # Not required for querying all objects + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['action_rule', 'tenant']], + ['state', 'present', ['action_rule', 'tenant']], + ], + ) + + action_rule = module.params.get('action_rule') + description = module.params.get('description') + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='rtctrlAttrP', + aci_rn='attr-{0}'.format(action_rule), + module_object=action_rule, + target_filter={'name': action_rule}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='rtctrlAttrP', + class_config=dict( + name=action_rule, + descr=description, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='rtctrlAttrP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_ep_retention_policy.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_ep_retention_policy.py new file mode 100644 index 00000000..5b0e0e70 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_ep_retention_policy.py @@ -0,0 +1,361 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_tenant_ep_retention_policy +short_description: Manage End Point (EP) retention protocol policies (fv:EpRetPol) +description: +- Manage End Point (EP) retention protocol policies on Cisco ACI fabrics. +options: + tenant: + description: + - The name of an existing tenant. + type: str + aliases: [ tenant_name ] + epr_policy: + description: + - The name of the end point retention policy. + type: str + aliases: [ epr_name, name ] + bounce_age: + description: + - Bounce entry aging interval in seconds. + - Accepted values range between C(150) and C(65535); 0 is used for infinite. + - The APIC defaults to C(630) when unset during creation. + type: int + bounce_trigger: + description: + - Determines if the bounce entries are installed by RARP Flood or COOP Protocol. + - The APIC defaults to C(coop) when unset during creation. + type: str + choices: [ coop, flood ] + hold_interval: + description: + - Hold interval in seconds. + - Accepted values range between C(5) and C(65535). + - The APIC defaults to C(300) when unset during creation. + type: int + local_ep_interval: + description: + - Local end point aging interval in seconds. + - Accepted values range between C(120) and C(65535); 0 is used for infinite. + - The APIC defaults to C(900) when unset during creation. + type: int + remote_ep_interval: + description: + - Remote end point aging interval in seconds. + - Accepted values range between C(120) and C(65535); 0 is used for infinite. + - The APIC defaults to C(300) when unset during creation. + type: int + move_frequency: + description: + - Move frequency per second. + - Accepted values range between C(0) and C(65535); 0 is used for none. + - The APIC defaults to C(256) when unset during creation. + type: int + description: + description: + - Description for the End point retention policy. + type: str + aliases: [ descr ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fv:EpRetPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Swetha Chunduri (@schunduri) +''' + +EXAMPLES = r''' +- name: Add a new EPR policy + cisco.aci.aci_tenant_ep_retention_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + epr_policy: EPRPol1 + bounce_age: 630 + hold_interval: 300 + local_ep_interval: 900 + remote_ep_interval: 300 + move_frequency: 256 + description: test + state: present + delegate_to: localhost + +- name: Remove an EPR policy + cisco.aci.aci_tenant_ep_retention_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + epr_policy: EPRPol1 + state: absent + delegate_to: localhost + +- name: Query an EPR policy + cisco.aci.aci_tenant_ep_retention_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + epr_policy: EPRPol1 + state: query + delegate_to: localhost + register: query_result + +- name: Query all EPR policies + cisco.aci.aci_tenant_ep_retention_policy: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +BOUNCE_TRIG_MAPPING = dict( + coop='protocol', + rarp='rarp-flood', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + epr_policy=dict(type='str', aliases=['epr_name', 'name']), # Not required for querying all objects + bounce_age=dict(type='int'), + bounce_trigger=dict(type='str', choices=['coop', 'flood']), + hold_interval=dict(type='int'), + local_ep_interval=dict(type='int'), + remote_ep_interval=dict(type='int'), + description=dict(type='str', aliases=['descr']), + move_frequency=dict(type='int'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['epr_policy', 'tenant']], + ['state', 'present', ['epr_policy', 'tenant']], + ], + ) + + epr_policy = module.params.get('epr_policy') + bounce_age = module.params.get('bounce_age') + if bounce_age is not None and bounce_age != 0 and bounce_age not in range(150, 65536): + module.fail_json(msg="The bounce_age must be a value of 0 or between 150 and 65535") + if bounce_age == 0: + bounce_age = 'infinite' + bounce_trigger = module.params.get('bounce_trigger') + if bounce_trigger is not None: + bounce_trigger = BOUNCE_TRIG_MAPPING[bounce_trigger] + description = module.params.get('description') + hold_interval = module.params.get('hold_interval') + if hold_interval is not None and hold_interval not in range(5, 65536): + module.fail_json(msg="The hold_interval must be a value between 5 and 65535") + local_ep_interval = module.params.get('local_ep_interval') + if local_ep_interval is not None and local_ep_interval != 0 and local_ep_interval not in range(120, 65536): + module.fail_json(msg="The local_ep_interval must be a value of 0 or between 120 and 65535") + if local_ep_interval == 0: + local_ep_interval = "infinite" + move_frequency = module.params.get('move_frequency') + if move_frequency is not None and move_frequency not in range(65536): + module.fail_json(msg="The move_frequency must be a value between 0 and 65535") + if move_frequency == 0: + move_frequency = "none" + remote_ep_interval = module.params.get('remote_ep_interval') + if remote_ep_interval is not None and remote_ep_interval not in range(120, 65536): + module.fail_json(msg="The remote_ep_interval must be a value of 0 or between 120 and 65535") + if remote_ep_interval == 0: + remote_ep_interval = "infinite" + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='fvEpRetPol', + aci_rn='epRPol-{0}'.format(epr_policy), + module_object=epr_policy, + target_filter={'name': epr_policy}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fvEpRetPol', + class_config=dict( + name=epr_policy, + descr=description, + bounceAgeIntvl=bounce_age, + bounceTrig=bounce_trigger, + holdIntvl=hold_interval, + localEpAgeIntvl=local_ep_interval, + remoteEpAgeIntvl=remote_ep_interval, + moveFreq=move_frequency, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='fvEpRetPol') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_dst_group.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_dst_group.py new file mode 100644 index 00000000..9c6c2664 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_dst_group.py @@ -0,0 +1,246 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_tenant_span_dst_group +short_description: Manage SPAN destination groups (span:DestGrp) +description: +- Manage SPAN destination groups on Cisco ACI fabrics. +options: + dst_group: + description: + - The name of the SPAN destination group. + type: str + aliases: [ name ] + description: + description: + - The description of the SPAN destination group. + type: str + aliases: [ descr ] + tenant: + description: + - The name of the tenant. + type: str + aliases: [ tenant_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(span:DestGrp). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Dag Wieers (@dagwieers) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- cisco.aci.aci_tenant_span_dst_group: + host: apic + username: admin + password: SomeSecretPassword + dst_group: '{{ dst_group }}' + description: '{{ descr }}' + tenant: '{{ tenant }}' + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + dst_group=dict(type='str', aliases=['name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['dst_group', 'tenant']], + ['state', 'present', ['dst_group', 'tenant']], + ], + ) + + dst_group = module.params.get('dst_group') + description = module.params.get('description') + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='spanDestGrp', + aci_rn='destgrp-{0}'.format(dst_group), + module_object=dst_group, + target_filter={'name': dst_group}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='spanDestGrp', + class_config=dict( + name=dst_group, + descr=description, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='spanDestGrp') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group.py new file mode 100644 index 00000000..0e8cbb1e --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group.py @@ -0,0 +1,265 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_tenant_span_src_group +short_description: Manage SPAN source groups (span:SrcGrp) +description: +- Manage SPAN source groups on Cisco ACI fabrics. +options: + admin_state: + description: + - Enable or disable the span sources. + - The APIC defaults to C(yes) when unset during creation. + type: bool + description: + description: + - The description for Span source group. + type: str + aliases: [ descr ] + dst_group: + description: + - The Span destination group to associate with the source group. + type: str + src_group: + description: + - The name of the Span source group. + type: str + aliases: [ name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str + tenant: + description: + - The name of the Tenant. + type: str + aliases: [ tenant_name ] +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(span:SrcGrp). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- cisco.aci.aci_tenant_span_src_group: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + src_group: "{{ src_group }}" + dst_group: "{{ dst_group }}" + admin_state: "{{ admin_state }}" + description: "{{ description }}" + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + src_group=dict(type='str', aliases=['name']), # Not required for querying all objects + admin_state=dict(type='bool'), + description=dict(type='str', aliases=['descr']), + dst_group=dict(type='str'), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['src_group', 'tenant']], + ['state', 'present', ['src_group', 'tenant']], + ], + ) + + aci = ACIModule(module) + + admin_state = aci.boolean(module.params.get('admin_state'), 'enabled', 'disabled') + description = module.params.get('description') + dst_group = module.params.get('dst_group') + src_group = module.params.get('src_group') + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='spanSrcGrp', + aci_rn='srcgrp-{0}'.format(src_group), + module_object=src_group, + target_filter={'name': src_group}, + ), + child_classes=['spanSpanLbl'], + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='spanSrcGrp', + class_config=dict( + adminSt=admin_state, + descr=description, + name=src_group, + nameAlias=name_alias, + ), + child_configs=[{'spanSpanLbl': {'attributes': {'name': dst_group}}}], + ) + + aci.get_diff(aci_class='spanSrcGrp') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group_to_dst_group.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group_to_dst_group.py new file mode 100644 index 00000000..c547320f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group_to_dst_group.py @@ -0,0 +1,260 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_tenant_span_src_group_to_dst_group +short_description: Bind SPAN source groups to destination groups (span:SpanLbl) +description: +- Bind SPAN source groups to associated destination groups on Cisco ACI fabrics. +options: + description: + description: + - The description for Span source group to destination group binding. + type: str + aliases: [ descr ] + dst_group: + description: + - The Span destination group to associate with the source group. + type: str + src_group: + description: + - The name of the Span source group. + type: str + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str + tenant: + description: + - The name of the Tenant. + type: str + aliases: [ tenant_name ] +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(tenant), C(src_group), and C(dst_group) must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant), M(cisco.aci.aci_tenant_span_src_group), and M(cisco.aci.aci_tenant_span_dst_group) modules can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- module: cisco.aci.aci_tenant_span_src_group +- module: cisco.aci.aci_tenant_span_dst_group +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(span:SrcGrp). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +# FIXME: Add more, better examples +EXAMPLES = r''' +- cisco.aci.aci_tenant_span_src_group_to_dst_group: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + src_group: "{{ src_group }}" + dst_group: "{{ dst_group }}" + description: "{{ description }}" + delegate_to: localhost +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + dst_group=dict(type='str'), # Not required for querying all objects + src_group=dict(type='str'), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['dst_group', 'src_group', 'tenant']], + ['state', 'present', ['dst_group', 'src_group', 'tenant']], + ], + ) + + description = module.params.get('description') + dst_group = module.params.get('dst_group') + src_group = module.params.get('src_group') + state = module.params.get('state') + tenant = module.params.get('tenant') + name_alias = module.params.get('name_alias') + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='spanSrcGrp', + aci_rn='srcgrp-{0}'.format(src_group), + module_object=src_group, + target_filter={'name': src_group}, + ), + subclass_2=dict( + aci_class='spanSpanLbl', + aci_rn='spanlbl-{0}'.format(dst_group), + module_object=dst_group, + target_filter={'name': dst_group}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='spanSpanLbl', + class_config=dict( + descr=description, + name=dst_group, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='spanSpanLbl') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool.py new file mode 100644 index 00000000..542d63c6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool.py @@ -0,0 +1,283 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) +# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_vlan_pool +short_description: Manage VLAN pools (fvns:VlanInstP) +description: +- Manage VLAN pools on Cisco ACI fabrics. +options: + pool_allocation_mode: + description: + - The method used for allocating VLANs to resources. + type: str + choices: [ dynamic, static] + aliases: [ allocation_mode, mode ] + description: + description: + - Description for the C(pool). + type: str + aliases: [ descr ] + pool: + description: + - The name of the pool. + type: str + aliases: [ name, pool_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- module: cisco.aci.aci_encap_pool +- module: cisco.aci.aci_vlan_pool_encap_block +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fvns:VlanInstP). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Add a new VLAN pool + cisco.aci.aci_vlan_pool: + host: apic + username: admin + password: SomeSecretPassword + pool: production + pool_allocation_mode: dynamic + description: Production VLANs + state: present + delegate_to: localhost + +- name: Remove a VLAN pool + cisco.aci.aci_vlan_pool: + host: apic + username: admin + password: SomeSecretPassword + pool: production + pool_allocation_mode: dynamic + state: absent + delegate_to: localhost + +- name: Query a VLAN pool + cisco.aci.aci_vlan_pool: + host: apic + username: admin + password: SomeSecretPassword + pool: production + pool_allocation_mode: dynamic + state: query + delegate_to: localhost + register: query_result + +- name: Query all VLAN pools + cisco.aci.aci_vlan_pool: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + pool=dict(type='str', aliases=['name', 'pool_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + pool_allocation_mode=dict(type='str', aliases=['allocation_mode', 'mode'], choices=['dynamic', 'static']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['pool']], + ['state', 'present', ['pool']], + ], + ) + + description = module.params.get('description') + pool = module.params.get('pool') + pool_allocation_mode = module.params.get('pool_allocation_mode') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + pool_name = pool + + # ACI Pool URL requires the allocation mode for vlan and vsan pools (ex: uni/infra/vlanns-[poolname]-static) + if pool is not None: + if pool_allocation_mode is not None: + pool_name = '[{0}]-{1}'.format(pool, pool_allocation_mode) + else: + module.fail_json(msg="ACI requires the 'pool_allocation_mode' when 'pool' is provided") + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvnsVlanInstP', + aci_rn='infra/vlanns-{0}'.format(pool_name), + module_object=pool, + target_filter={'name': pool}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fvnsVlanInstP', + class_config=dict( + allocMode=pool_allocation_mode, + descr=description, + name=pool, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class='fvnsVlanInstP') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool_encap_block.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool_encap_block.py new file mode 100644 index 00000000..e7204c63 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool_encap_block.py @@ -0,0 +1,362 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Jacob McGill (jmcgill298) +# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_vlan_pool_encap_block +short_description: Manage encap blocks assigned to VLAN pools (fvns:EncapBlk) +description: +- Manage VLAN encap blocks that are assigned to VLAN pools on Cisco ACI fabrics. +options: + allocation_mode: + description: + - The method used for allocating encaps to resources. + type: str + choices: [ dynamic, inherit, static] + aliases: [ mode ] + description: + description: + - Description for the pool encap block. + type: str + aliases: [ descr ] + pool: + description: + - The name of the pool that the encap block should be assigned to. + type: str + aliases: [ pool_name ] + pool_allocation_mode: + description: + - The method used for allocating encaps to resources. + type: str + choices: [ dynamic, static] + aliases: [ pool_mode ] + block_end: + description: + - The end of encap block. + type: int + aliases: [ end ] + block_name: + description: + - The name to give to the encap block. + type: str + aliases: [ name ] + block_start: + description: + - The start of the encap block. + type: int + aliases: [ start ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci + +notes: +- The C(pool) must exist in order to add or delete a encap block. +seealso: +- module: cisco.aci.aci_encap_pool_range +- module: cisco.aci.aci_vlan_pool +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fvns:EncapBlk). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +- Dag Wieers (@dagwieers) +''' + +EXAMPLES = r''' +- name: Add a new VLAN encap block + cisco.aci.aci_vlan_pool_encap_block: + host: apic + username: admin + password: SomeSecretPassword + pool: production + block_start: 20 + block_end: 50 + state: present + delegate_to: localhost + +- name: Remove a VLAN encap block + cisco.aci.aci_vlan_pool_encap_block: + host: apic + username: admin + password: SomeSecretPassword + pool: production + block_start: 20 + block_end: 50 + state: absent + delegate_to: localhost + +- name: Query a VLAN encap block + cisco.aci.aci_vlan_pool_encap_block: + host: apic + username: admin + password: SomeSecretPassword + pool: production + block_start: 20 + block_end: 50 + state: query + delegate_to: localhost + register: query_result + +- name: Query a VLAN pool for encap blocks + cisco.aci.aci_vlan_pool_encap_block: + host: apic + username: admin + password: SomeSecretPassword + pool: production + state: query + delegate_to: localhost + register: query_result + +- name: Query all VLAN encap blocks + cisco.aci.aci_vlan_pool_encap_block: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + pool=dict(type='str', aliases=['pool_name']), # Not required for querying all objects + block_name=dict(type='str', aliases=['name']), # Not required for querying all objects + block_end=dict(type='int', aliases=['end']), # Not required for querying all objects + block_start=dict(type='int', aliases=["start"]), # Not required for querying all objects + allocation_mode=dict(type='str', aliases=['mode'], choices=['dynamic', 'inherit', 'static']), + description=dict(type='str', aliases=['descr']), + pool_allocation_mode=dict(type='str', aliases=['pool_mode'], choices=['dynamic', 'static']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['pool', 'block_end', 'block_name', 'block_start']], + ['state', 'present', ['pool', 'block_end', 'block_name', 'block_start']], + ], + ) + + allocation_mode = module.params.get('allocation_mode') + description = module.params.get('description') + pool = module.params.get('pool') + pool_allocation_mode = module.params.get('pool_allocation_mode') + block_end = module.params.get('block_end') + block_name = module.params.get('block_name') + block_start = module.params.get('block_start') + state = module.params.get('state') + name_alias = module.params.get('name_alias') + + if block_end is not None: + encap_end = 'vlan-{0}'.format(block_end) + else: + encap_end = None + + if block_start is not None: + encap_start = 'vlan-{0}'.format(block_start) + else: + encap_start = None + + # Collect proper mo information + aci_block_mo = 'from-[{0}]-to-[{1}]'.format(encap_start, encap_end) + pool_name = pool + + # Validate block_end and block_start are valid for its respective encap type + for encap_id in block_end, block_start: + if encap_id is not None: + if not 1 <= encap_id <= 4094: + module.fail_json(msg="vlan pools must have 'block_start' and 'block_end' values between 1 and 4094") + + if block_end is not None and block_start is not None: + # Validate block_start is less than block_end + if block_start > block_end: + module.fail_json(msg="The 'block_start' must be less than or equal to the 'block_end'") + + elif block_end is None and block_start is None: + if block_name is None: + # Reset range managed object to None for aci util to properly handle query + aci_block_mo = None + + # ACI Pool URL requires the allocation mode (ex: uni/infra/vlanns-[poolname]-static) + if pool is not None: + if pool_allocation_mode is not None: + pool_name = '[{0}]-{1}'.format(pool, pool_allocation_mode) + else: + module.fail_json(msg="ACI requires the 'pool_allocation_mode' when 'pool' is provided") + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvnsVlanInstP', + aci_rn='infra/vlanns-{0}'.format(pool_name), + module_object=pool, + target_filter={'name': pool}, + ), + subclass_1=dict( + aci_class='fvnsEncapBlk', + aci_rn=aci_block_mo, + module_object=aci_block_mo, + target_filter={'from': encap_start, 'to': encap_end, 'name': block_name}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fvnsEncapBlk', + class_config={ + "allocMode": allocation_mode, + "descr": description, + "from": encap_start, + "name": block_name, + "to": encap_end, + "nameAlias": name_alias, + }, + ) + + aci.get_diff(aci_class='fvnsEncapBlk') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_vmm_credential.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_vmm_credential.py new file mode 100644 index 00000000..b9a8a8c7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_vmm_credential.py @@ -0,0 +1,316 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: aci_vmm_credential +short_description: Manage virtual domain credential profiles (vmm:UsrAccP) +description: +- Manage virtual domain credential profiles on Cisco ACI fabrics. +options: + name: + description: + - Name of the credential profile. + type: str + aliases: [ credential_name, credential_profile ] + credential_password: + description: + - VMM controller password. + type: str + aliases: [] + credential_username: + description: + - VMM controller username. + type: str + aliases: [] + description: + description: + - Description for the tenant. + type: str + aliases: [ descr ] + domain: + description: + - Name of the virtual domain profile. + type: str + aliases: [ domain_name, domain_profile, name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str + vm_provider: + description: + - The VM platform for VMM Domains. + - Support for Kubernetes was added in ACI v3.0. + - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1. + type: str + choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ] +extends_documentation_fragment: +- cisco.aci.aci + +seealso: +- module: cisco.aci.aci_domain +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(vmm:DomP) + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jason Juenger (@jasonjuenger) +''' + +EXAMPLES = r''' +- name: Add credential to VMware VMM domain + cisco.aci.aci_vmm_credential: + host: apic + username: admin + password: SomeSecretPassword + domain: vmware_dom + description: secure credential + name: vCenterCredential + credential_username: vCenterUsername + credential_password: vCenterPassword + vm_provider: vmware + state: present + +- name: Remove credential from VMware VMM domain + cisco.aci.aci_vmm_credential: + host: apic + username: admin + password: SomeSecretPassword + domain: vmware_dom + name: myCredential + vm_provider: vmware + state: absent + +- name: Query a specific VMware VMM credential + cisco.aci.aci_vmm_credential: + host: apic + username: admin + password: SomeSecretPassword + domain: vmware_dom + name: vCenterCredential + vm_provider: vmware + state: query + delegate_to: localhost + register: query_result + +- name: Query all VMware VMM credentials + cisco.aci.aci_vmm_credential: + host: apic + username: admin + password: SomeSecretPassword + domain: vmware_dom + vm_provider: vmware + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +VM_PROVIDER_MAPPING = dict( + cloudfoundry='CloudFoundry', + kubernetes='Kubernetes', + microsoft='Microsoft', + openshift='OpenShift', + openstack='OpenStack', + redhat='Redhat', + vmware='VMware', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + name=dict(type='str', aliases=['credential_name', 'credential_profile']), + credential_password=dict(type='str', no_log=True), + credential_username=dict(type='str'), + description=dict(type='str', aliases=['descr']), + domain=dict(type='str', aliases=['domain_name', 'domain_profile']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + vm_provider=dict(type='str', choices=list(VM_PROVIDER_MAPPING.keys())), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['domain']], + ['state', 'present', ['domain']], + ], + ) + + name = module.params.get('name') + credential_password = module.params.get('credential_password') + credential_username = module.params.get('credential_username') + description = module.params.get('description') + domain = module.params.get('domain') + state = module.params.get('state') + vm_provider = module.params.get('vm_provider') + name_alias = module.params.get('name_alias') + + credential_class = 'vmmUsrAccP' + usracc_mo = 'uni/vmmp-{0}/dom-{1}/usracc-{2}'.format(VM_PROVIDER_MAPPING.get(vm_provider), domain, name) + usracc_rn = 'vmmp-{0}/dom-{1}/usracc-{2}'.format(VM_PROVIDER_MAPPING.get(vm_provider), domain, name) + + # Ensure that querying all objects works when only domain is provided + if name is None: + usracc_mo = None + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class=credential_class, + aci_rn=usracc_rn, + module_object=usracc_mo, + target_filter={'name': domain, 'usracc': name}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class=credential_class, + class_config=dict( + descr=description, + name=name, + pwd=credential_password, + usr=credential_username, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class=credential_class) + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_vrf.py b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_vrf.py new file mode 100644 index 00000000..6bb230c2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/plugins/modules/aci_vrf.py @@ -0,0 +1,324 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: aci_vrf +short_description: Manage contexts or VRFs (fv:Ctx) +description: +- Manage contexts or VRFs on Cisco ACI fabrics. +- Each context is a private network associated to a tenant, i.e. VRF. +- Enable Preferred Groups for VRF +options: + tenant: + description: + - The name of the Tenant the VRF should belong to. + type: str + aliases: [ tenant_name ] + vrf: + description: + - The name of the VRF. + type: str + aliases: [ context, name, vrf_name ] + policy_control_direction: + description: + - Determines if the policy should be enforced by the fabric on ingress or egress. + type: str + choices: [ egress, ingress ] + policy_control_preference: + description: + - Determines if the fabric should enforce contract policies to allow routing and packet forwarding. + type: str + choices: [ enforced, unenforced ] + description: + description: + - The description for the VRF. + type: str + aliases: [ descr ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str + preferred_group: + description: + - Enables preferred groups for the VRF under vzAny + type: str + choices: [enabled, disabled] + match_type: + description: + - Configures match type for contracts under vzAny + type: str + choices: [ all, at_least_one, at_most_one, none] +extends_documentation_fragment: +- cisco.aci.aci +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fv:Ctx). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jacob McGill (@jmcgill298) +''' + +EXAMPLES = r''' +- name: Add a new VRF to a tenant + cisco.aci.aci_vrf: + host: apic + username: admin + password: SomeSecretPassword + vrf: vrf_lab + tenant: lab_tenant + descr: Lab VRF + policy_control_preference: enforced + policy_control_direction: ingress + state: present + delegate_to: localhost + +- name: Remove a VRF for a tenant + cisco.aci.aci_vrf: + host: apic + username: admin + password: SomeSecretPassword + vrf: vrf_lab + tenant: lab_tenant + state: absent + delegate_to: localhost + +- name: Query a VRF of a tenant + cisco.aci.aci_vrf: + host: apic + username: admin + password: SomeSecretPassword + vrf: vrf_lab + tenant: lab_tenant + state: query + delegate_to: localhost + register: query_result + +- name: Query all VRFs + cisco.aci.aci_vrf: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec + +MATCH_TYPE_MAPPING = dict( + all='All', + at_least_one='AtleastOne', + at_most_one='AtmostOne', + none='None', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects + vrf=dict(type='str', aliases=['context', 'name', 'vrf_name']), # Not required for querying all objects + description=dict(type='str', aliases=['descr']), + policy_control_direction=dict(type='str', choices=['egress', 'ingress']), + policy_control_preference=dict(type='str', choices=['enforced', 'unenforced']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + preferred_group=dict(type='str', choices=['enabled', 'disabled']), + match_type=dict(type='str', choices=['all', 'at_least_one', 'at_most_one', 'none']), + name_alias=dict(type='str'), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['tenant', 'vrf']], + ['state', 'present', ['tenant', 'vrf']], + ], + ) + + description = module.params.get('description') + policy_control_direction = module.params.get('policy_control_direction') + policy_control_preference = module.params.get('policy_control_preference') + state = module.params.get('state') + tenant = module.params.get('tenant') + vrf = module.params.get('vrf') + name_alias = module.params.get('name_alias') + preferred_group = module.params.get('preferred_group') + match_type = module.params.get('match_type') + + if match_type is not None: + match_type = MATCH_TYPE_MAPPING[match_type] + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='fvTenant', + aci_rn='tn-{0}'.format(tenant), + module_object=tenant, + target_filter={'name': tenant}, + ), + subclass_1=dict( + aci_class='fvCtx', + aci_rn='ctx-{0}'.format(vrf), + module_object=vrf, + target_filter={'name': vrf}, + ), + child_classes=['vzAny'] + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class='fvCtx', + class_config=dict( + descr=description, + pcEnfDir=policy_control_direction, + pcEnfPref=policy_control_preference, + name=vrf, + nameAlias=name_alias, + ), + child_configs=[ + dict(vzAny=dict(attributes=dict(prefGrMemb=preferred_group, matchT=match_type))), + ], + ) + + aci.get_diff(aci_class='fvCtx') + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/.gitignore b/collections-debian-merged/ansible_collections/cisco/aci/tests/.gitignore new file mode 100644 index 00000000..ea1472ec --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/.gitignore @@ -0,0 +1 @@ +output/ diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/inventory.networking b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/inventory.networking new file mode 100644 index 00000000..736d93a2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/inventory.networking @@ -0,0 +1,11 @@ +[aci] +lh-dmz1-pod3-sim-v32 ansible_host=173.36.219.136 ansible_connection=local aci_hostname=173.36.219.136 aci_username=ansible_github_ci aci_password="jZqwpB*r9&5mx3mn8e5" +lh-dmz1-pod3-sim-v42 ansible_host=173.36.219.68 ansible_connection=local aci_hostname=173.36.219.68 aci_username=ansible_github_ci aci_password="jZqwpB*r9&5mx3mn8e5" + +[aci:vars] +aws_aci_hostname=54.176.140.90 +aws_aci_username=ansible_github_ci +aws_aci_password=sJ94G92#8dq2hx*K4qh +azure_aci_hostname=104.42.26.226 +azure_aci_username=ansible_github_ci +azure_aci_password=sJ94G92#8dq2hx*K4qh
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/network-integration.requirements.txt b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/network-integration.requirements.txt new file mode 100644 index 00000000..5546336e --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/network-integration.requirements.txt @@ -0,0 +1,2 @@ +lxml +xmljson
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/target-prefixes.network b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/target-prefixes.network new file mode 100644 index 00000000..dc092c88 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/target-prefixes.network @@ -0,0 +1 @@ +aci diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user/tasks/main.yml new file mode 100644 index 00000000..b3841f90 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user/tasks/main.yml @@ -0,0 +1,235 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (dagwieers) <dag@wieers.com> +# +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + + +# CLEAN ENVIRONMENT +- name: Remove any pre-existing user + cisco.aci.aci_aaa_user: &user_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aaa_user: ansible + state: absent + + +# ADD USER +- name: Add user (check_mode) + cisco.aci.aci_aaa_user: &user_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aaa_user: ansible + description: Ansible test user + email: ansible@ansible.lan + enabled: yes + expiration: never + expires: no + first_name: Test + last_name: User + phone: 1-234-555-678 + check_mode: yes + register: cm_add_user + +# NOTE: Setting password is not idempotent, see #35544 +- name: Add user (normal mode) + cisco.aci.aci_aaa_user: + <<: *user_present + aaa_password: 12!Ab:cD!34 + register: nm_add_user + +- name: Add user again (check mode) + cisco.aci.aci_aaa_user: *user_present + check_mode: yes + register: cm_add_user_again + +- name: Add user again (normal mode) + cisco.aci.aci_aaa_user: *user_present + register: nm_add_user_again + +- name: Verify add user + assert: + that: + - cm_add_user is changed + - nm_add_user is changed + - nm_add_user.current.0.aaaUser.attributes.descr == 'Ansible test user' + - cm_add_user_again is not changed + - nm_add_user_again is not changed + - nm_add_user_again.current.0.aaaUser.attributes.descr == 'Ansible test user' + + +# MODIFY USER +- name: Modify user (check_mode) + cisco.aci.aci_aaa_user: &user_changed + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aaa_user: ansible + description: Ansible test user for integration tests + email: aci-ansible@ansible.lan + expiration: '2123-12-12' + expires: yes + phone: 2-345-555-678 + check_mode: yes + register: cm_modify_user + +- name: Modify user (normal mode) + cisco.aci.aci_aaa_user: *user_changed + register: nm_modify_user + +- name: Modify user again (check mode) + cisco.aci.aci_aaa_user: *user_changed + check_mode: yes + register: cm_modify_user_again + +- name: Modify user again (normal mode) + cisco.aci.aci_aaa_user: *user_changed + register: nm_modify_user_again + +- name: Verify modify user + assert: + that: + - cm_modify_user is changed + - nm_modify_user is changed + - nm_modify_user.current.0.aaaUser.attributes.descr == 'Ansible test user for integration tests' + - cm_modify_user_again is not changed + - nm_modify_user_again is not changed + - nm_modify_user_again.current.0.aaaUser.attributes.descr == 'Ansible test user for integration tests' + + +# CLEAR PASSWORD HISTORY +- name: Clear password history (check_mode) + cisco.aci.aci_aaa_user: &clear_password_history + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aaa_user: ansible + clear_password_history: yes + check_mode: yes + register: cm_clear_password_history + +- name: Clear password history (normal mode) + cisco.aci.aci_aaa_user: *clear_password_history + register: nm_clear_password_history + +- name: Clear password history (check mode) + cisco.aci.aci_aaa_user: *clear_password_history + check_mode: yes + register: cm_clear_password_history_again + +- name: Clear password history (normal mode) + cisco.aci.aci_aaa_user: *clear_password_history + register: nm_clear_password_history_again + +- name: Verify clear password history + assert: + that: + # NOTE: Clearing password history is a changing action, everytime + - cm_clear_password_history is changed + - nm_clear_password_history is changed + - cm_clear_password_history_again is changed + - nm_clear_password_history_again is changed + + +# QUERY ALL USERS +- name: Query all users (check_mode) + cisco.aci.aci_aaa_user: &user_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aaa_user: ansible + state: query + check_mode: yes + register: cm_query_all_users + +- name: Query all users (normal mode) + cisco.aci.aci_aaa_user: *user_query + register: nm_query_all_users + +- name: Verify query_all_users + assert: + that: + - cm_query_all_users is not changed + - nm_query_all_users is not changed + # NOTE: Order of users is not stable between calls + #- cm_query_all_users == nm_query_all_users + + +# QUERY OUR USER +- name: Query our user (check_mode) + cisco.aci.aci_aaa_user: + <<: *user_query + check_mode: yes + register: cm_query_user + +- name: Query our user (normal mode) + cisco.aci.aci_aaa_user: + <<: *user_query + register: nm_query_user + +- name: Verify query_user + assert: + that: + - cm_query_user is not changed + - nm_query_user is not changed + - cm_query_user == nm_query_user + - nm_query_user.current.0.aaaUser.attributes.accountStatus == 'active' + - nm_query_user.current.0.aaaUser.attributes.descr == 'Ansible test user for integration tests' + - nm_query_user.current.0.aaaUser.attributes.email == 'aci-ansible@ansible.lan' + - nm_query_user.current.0.aaaUser.attributes.expiration == '2123-12-12T00:00:00.000+00:00' + - nm_query_user.current.0.aaaUser.attributes.expires == 'yes' + - nm_query_user.current.0.aaaUser.attributes.phone == '2-345-555-678' + + +# REMOVE USER +- name: Remove user (check_mode) + cisco.aci.aci_aaa_user: *user_absent + check_mode: yes + register: cm_remove_user + +- name: Remove user (normal mode) + cisco.aci.aci_aaa_user: *user_absent + register: nm_remove_user + +- name: Remove user again (check_mode) + cisco.aci.aci_aaa_user: *user_absent + check_mode: yes + register: cm_remove_user_again + +- name: Remove user again (normal mode) + cisco.aci.aci_aaa_user: *user_absent + register: nm_remove_user_again + +- name: Verify remove_user + assert: + that: + - cm_remove_user is changed + - nm_remove_user is changed + - cm_remove_user_again is not changed + - nm_remove_user_again is not changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/admin.crt b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/admin.crt new file mode 100644 index 00000000..cfac5531 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/admin.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICODCCAaGgAwIBAgIJAIt8XMntue0VMA0GCSqGSIb3DQEBCwUAMDQxDjAMBgNV +BAMMBUFkbWluMRUwEwYDVQQKDAxZb3VyIENvbXBhbnkxCzAJBgNVBAYTAlVTMCAX +DTE4MDEwOTAwNTk0NFoYDzIxMTcxMjE2MDA1OTQ0WjA0MQ4wDAYDVQQDDAVBZG1p +bjEVMBMGA1UECgwMWW91ciBDb21wYW55MQswCQYDVQQGEwJVUzCBnzANBgkqhkiG +9w0BAQEFAAOBjQAwgYkCgYEAohG/7axtt7CbSaMP7r+2mhTKbNgh0Ww36C7Ta14i +v+VmLyKkQHnXinKGhp6uy3Nug+15a+eIu7CrgpBVMQeCiWfsnwRocKcQJWIYDrWl +XHxGQn31yYKR6mylE7Dcj3rMFybnyhezr5D8GcP85YRPmwG9H2hO/0Y1FUnWu9Iw +AQkCAwEAAaNQME4wHQYDVR0OBBYEFD0jLXfpkrU/ChzRvfruRs/fy1VXMB8GA1Ud +IwQYMBaAFD0jLXfpkrU/ChzRvfruRs/fy1VXMAwGA1UdEwQFMAMBAf8wDQYJKoZI +hvcNAQELBQADgYEAOmvre+5tgZ0+F3DgsfxNQqLTrGiBgGCIymPkP/cBXXkNuJyl +3ac7tArHQc7WEA4U2R2rZbEq8FC3UJJm4nUVtCPvEh3G9OhN2xwYev79yt6pIn/l +KU0Td2OpVyo0eLqjoX5u2G90IBWzhyjFbo+CcKMrSVKj1YOdG0E3OuiJf00= +-----END CERTIFICATE----- diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/admin.key b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/admin.key new file mode 100644 index 00000000..63bb00cc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/admin.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKIRv+2sbbewm0mj +D+6/tpoUymzYIdFsN+gu02teIr/lZi8ipEB514pyhoaerstzboPteWvniLuwq4KQ +VTEHgoln7J8EaHCnECViGA61pVx8RkJ99cmCkepspROw3I96zBcm58oXs6+Q/BnD +/OWET5sBvR9oTv9GNRVJ1rvSMAEJAgMBAAECgYByu3QO0qF9h7X3JEu0Ld4cKBnB +giQ2uJC/et7KxIJ/LOvw9GopBthyt27KwG1ntBkJpkTuAaQHkyNns7vLkNB0S0IR ++owVFEcKYq9VCHTaiQU8TDp24gN+yPTrpRuH8YhDVq5SfVdVuTMgHVQdj4ya4VlF +Gj+a7+ipxtGiLsVGrQJBAM7p0Fm0xmzi+tBOASUAcVrPLcteFIaTBFwfq16dm/ON +00Khla8Et5kMBttTbqbukl8mxFjBEEBlhQqb6EdQQ0sCQQDIhHx1a9diG7y/4DQA +4KvR3FCYwP8PBORlSamegzCo+P1OzxiEo0amX7yQMA5UyiP/kUsZrme2JBZgna8S +p4R7AkEAr7rMhSOPUnMD6V4WgsJ5g1Jp5kqkzBaYoVUUSms5RASz4+cwJVCwTX91 +Y1jcpVIBZmaaY3a0wrx13ajEAa0dOQJBAIpjnb4wqpsEh7VpmJqOdSdGxb1XXfFQ +sA0T1OQYqQnFppWwqrxIL+d9pZdiA1ITnNqyvUFBNETqDSOrUHwwb2cCQGArE+vu +ffPUWQ0j+fiK+covFG8NL7H+26NSGB5+Xsn9uwOGLj7K/YT6CbBtr9hJiuWjM1Al +0V4ltlTuu2mTMaw= +-----END PRIVATE KEY----- diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/admin_invalid.key b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/admin_invalid.key new file mode 100644 index 00000000..22f5fae4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/admin_invalid.key @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +This is an invalid private key +-----END PRIVATE KEY----- diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/openssh_rsa.key b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/openssh_rsa.key new file mode 100644 index 00000000..0c18da5c --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/openssh_rsa.key @@ -0,0 +1,16 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn +NhAAAAAwEAAQAAAIEA3VnrdPOQbr3DPF5GbC31W7ScloEpU9BSDqPmpyYPUdsWl21UXBB8 +exip3GVOl+7GbB1WkDKYr7uMuBjsfDzMzZkDAFVEpud+IUzZB7aSfSd+L9bdeFG2sGI+Fv +y1QmiMBT5gcvXaM16vRKe4FywM07/Fmd3REm/+wtmFG/C4sYUAAAIQLuIWNS7iFjUAAAAH +c3NoLXJzYQAAAIEA3VnrdPOQbr3DPF5GbC31W7ScloEpU9BSDqPmpyYPUdsWl21UXBB8ex +ip3GVOl+7GbB1WkDKYr7uMuBjsfDzMzZkDAFVEpud+IUzZB7aSfSd+L9bdeFG2sGI+Fvy1 +QmiMBT5gcvXaM16vRKe4FywM07/Fmd3REm/+wtmFG/C4sYUAAAADAQABAAAAgHj5rhALFg +MQP2X8+GwjahemzHYNPXMLRe2ucl8kE/de0CgOnq56bC4yupMz4xJyc4ufNTI2FPDmhfAP +3x+/cwZeYFsipyGdL1IYbfk0QYSP65Btr2yq8+QyN7zWdFXQ8csT0ImZgNiQKehc69ctLH +XcyelsdwNiUCRZYa7kCpf5AAAAQQCo7OSWQUa16xP9KrKm0F3fnaAKewhQNDIwok5PRgoN +03k/IpGOCAjvNuOb7DkXmVvxjO8Rj4L16vL+RTzHg8n7AAAAQQD7tej6gJy3MLcmrQ4aHb +FeLzQ/ZXS2IgdIRC8rcNB1h9Rso7+fySVFwnmwy2Um7wwsjNnr2xyhigwfQCSyRubfAAAA +QQDhH5EX7+hdm/fPLM6Goz9N3ERbIgBq2Mel5CCi/Ns7vDfBQiEla1atdKTV0S2EYfxIw2 +ehkMGbmXl2/9JHxKgbAAAAFGNpemhhb0BDSVpIQU8tTS05MjhRAQIDBAUG +-----END OPENSSH PRIVATE KEY----- diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/rsa_ansible.key b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/rsa_ansible.key new file mode 100644 index 00000000..ac63a005 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/rsa_ansible.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDVyLS8/ix6QOH7R83B4WuhsliL6nffBvrkHXXsqViit3OZd+/K +fSrNlZysUvHS4hxfRtJrFQfpkogwXEEupBPF3p0xy7wZzvjjWWJk0NQ8PoVlOhUY +emZTfMX+FFNr9pAjjjaVHb9jCuxko7upAyj8POhhETY2zYoJoa8TR6fLZwIDAQAB +AoGBALo5GzeGMThNTIyW/6Tjt94ifP9kPwcIDYSoJRECczNKmmgVEcxRO/fZW6DA +n+YTEKPuDV059KqB+iAmPKFkS9N41kaq+NUAknlFJPV6Vs3gpvJGqWgu++73dhR5 +cKsHTlK2KBsRtsXnOJ9odKWFjiTnZ1Eyvmhw7ct+Fojb/7ABAkEA9+Wwm+HGlYqw +ghuFaBtNuqC/S2vO6SEfdQvTDQKKO5ROei5m+ryjWj6flbCcG+5dLs8l4Zh3sQUL +kc0RQfHSWQJBANzFkdO6wXXPOw7RhAEP2sA2W2VacMbGynjsoDJXmypeJ7Z+odLb +5gNXET9RA77RY/saIBdwR4JNnku2WnoxU78CQQDhYirVP0vu8H6UfHMpeRGNqdLi +vq0LlrrkDxEe1f1aN/e17HRiaZnXVfKABWeZmXmNMndNifLgtiaTtC+JllRZAkEA +ydAdV0SANvaCETC7j9DzcgP+lm8PatYsHlCIvJxS7m71tKCbw0pbQDBmRtADMXrt +/4vJTEPKSrYzfxiqKstOtwJAXkWXaqVhJeKjbMj1buo6s/1qGIfSrZR/AjozvJ03 +JehevfULS3668jOYJZW6BoNhysx6+Hqf5Id8fB4iDWPQhA== +-----END RSA PRIVATE KEY----- diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/rsa_user.crt b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/rsa_user.crt new file mode 100644 index 00000000..de222350 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/rsa_user.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICODCCAaGgAwIBAgIUNOqiIBh811X/tPWSUgr9rajJ7t4wDQYJKoZIhvcNAQEL +BQAwLTEOMAwGA1UEAwwFQWRtaW4xDjAMBgNVBAoMBWNpc2NvMQswCQYDVQQGEwJV +UzAgFw0yMDEwMjkyMjQ0NTNaGA8yMTIwMTAwNTIyNDQ1M1owLTEOMAwGA1UEAwwF +QWRtaW4xDjAMBgNVBAoMBWNpc2NvMQswCQYDVQQGEwJVUzCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAwjb3/W3x/bPX+bylh2PjXbcFPwpdTPJwqTxCdUinJRKv +HXW7rwRiV9TdoNZZ946RvVM6l2LzUJyaK4wZZHf6WKJ2veL6LIrORA32vN+ofmpn +XcTAUQ1JVyHbriy0GaT+1wYClqImWj8HxiskgpD+pKc+kzgl33xwwwqyuF1N7ikC +AwEAAaNTMFEwHQYDVR0OBBYEFAK18YAZAaPQW7bHvqRwselDeGskMB8GA1UdIwQY +MBaAFAK18YAZAaPQW7bHvqRwselDeGskMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADgYEAgIvzyP0t4CjsmUmgG7QP977c3+Uvbt2wlCwe+rrXlqvuSeaW +l4DaTyv8kYyiqIxgrTVI/G+HbpHgTO2yH57njTIAdRjsJgMU9z0oCazOtVD8KpXj +SKFUtJVbY27BQAnbuDOawX96a0UDY44Ia9NaPuq0/mEcdCKSpQP4ZuvvKVc= +-----END CERTIFICATE----- diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/rsa_user.key b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/rsa_user.key new file mode 100644 index 00000000..354dbbdb --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/pki/rsa_user.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMI29/1t8f2z1/m8 +pYdj4123BT8KXUzycKk8QnVIpyUSrx11u68EYlfU3aDWWfeOkb1TOpdi81CcmiuM +GWR3+liidr3i+iyKzkQN9rzfqH5qZ13EwFENSVch264stBmk/tcGApaiJlo/B8Yr +JIKQ/qSnPpM4Jd98cMMKsrhdTe4pAgMBAAECgYAX8c8BX9zF+rZWA/wkhRwzIa1z +6EM4iWt6cgN/kkWJPJR6fVl2aoP1cDki60qMSveM8AX5RCnbdnNLiypWSLSEogdd +bRWyFeF4ZXvivd+Lds2u5Ni3PiCrIpHfNvid2ERCaKhblQRdhi/dTH9Z+3kGspwc +jpKzWMmGjBpqWjWOQQJBAOB3cS/AxbwJ6Fzvbi6sLiK6Ry8eSIMlce3Yyw89oU+M +DGkIbggICCYKxXYIWtBbyxthdQudKFZYbLpCkLSMBXsCQQDdf5ICNN2R0ptYLhSX +kQ4tiGigi1hq93+25Ov1rI8eIFSYlKNcyA/cvwv5ptlXmy1UAyoAdGCbS47pgCwT +Nz+rAkEAtzHkR5PuDXSMluS2KRNPJ/qdxB/UEGzMGdEYkNy8vX5QVpyRqK5dcCbU +V2ukKm7wSe11KEBgPnA2dKGFFkU85wJAD895Vpr7bdtAp2yyn5cFEg74mO0ZZJlC +DoYMqb6lgJsCLtn9RzQonbMtYaadQPmcpLCNIPctpiggjV5OxxhcfQJBAM1ETm8p +/9beBPTS8cJdWHvCRE149H/ZCUxqjFZriJzFYvi0xor85eK8/3V7xaWtTkK25i3+ +xWk+sA3DYYDPGM8= +-----END PRIVATE KEY----- diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/tasks/main.yml new file mode 100644 index 00000000..78053d38 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aaa_user_certificate/tasks/main.yml @@ -0,0 +1,279 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (dagwieers) <dag@wieers.com> +# +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + + +# CLEAN ENVIRONMENT +- name: Making sure running user has correct cert + cisco.aci.aci_aaa_user_certificate: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aaa_user: '{{ aci_username }}' + certificate_name: admin + certificate: "{{ lookup('file', 'pki/admin.crt') }}" + state: present + +- name: Remove any pre-existing certificate + cisco.aci.aci_aaa_user_certificate: &cert_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aaa_user: '{{ aci_username }}' + certificate_name: "{{ item }}" + state: absent + loop: + - admin + - user + - rsa_user + +# ADD USER CERTIFICATE +- name: Add user certificate (check_mode) + cisco.aci.aci_aaa_user_certificate: &cert_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aaa_user: '{{ aci_username }}' + certificate_name: admin + certificate: "{{ lookup('file', 'pki/admin.crt') }}" + state: present + check_mode: yes + register: cm_add_cert + +- name: Add user certificate (normal mode) + cisco.aci.aci_aaa_user_certificate: *cert_present + register: nm_add_cert + +- name: Add user certificate again (check mode) + cisco.aci.aci_aaa_user_certificate: *cert_present + check_mode: yes + register: cm_add_cert_again + +- name: Add user certificate again (normal mode) + cisco.aci.aci_aaa_user_certificate: *cert_present + register: nm_add_cert_again + +- name: Verify add_cert + assert: + that: + - cm_add_cert is changed + - nm_add_cert is change + - cm_add_cert_again is not changed + - nm_add_cert_again is not changed + +- name: Add rsa_user certificates + cisco.aci.aci_aaa_user_certificate: + <<: *cert_present + certificate_name: "{{ item }}" + certificate: "{{ lookup('file', 'pki/rsa_user.crt') }}" + state: present + loop: + - user + - rsa_user + +- name: Add admin certificate with user name + cisco.aci.aci_aaa_user_certificate: + <<: *cert_present + certificate_name: '{{ aci_username }}' + state: present + +# QUERY ALL USER CERTIFICATES +- name: Query all user certificates using signature-based authentication (check_mode) + cisco.aci.aci_aaa_user_certificate: &cert_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + #password: '{{ aci_password }}' + private_key: '{{ role_path }}/pki/admin.key' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aaa_user: '{{ aci_username }}' + state: query + check_mode: yes + register: cm_query_all_certs + +- name: Query all user certificates using signature-based authentication (normal mode) + cisco.aci.aci_aaa_user_certificate: *cert_query + register: nm_query_all_certs + +- name: Verify query_all_certs + assert: + that: + - cm_query_all_certs is not changed + - nm_query_all_certs is not changed + # NOTE: Order of certs is not stable between calls + #- cm_query_all_certs == nm_query_all_certs + +- name: Query all user certificates using signature-based authentication (valid rsa private key) + cisco.aci.aci_aaa_user_certificate: + <<: *cert_query + private_key: '{{ role_path }}/pki/rsa_user.key' + register: nm_query_all_rsa + +- name: Verify nm_query_all_rsa + assert: + that: + - nm_query_all_rsa is not changed + - nm_query_all_rsa.current.0.aaaUser.children | length >= 4 + +- name: Query all certificates using signature-based authentication (private key content for admin certificate) + cisco.aci.aci_aaa_user_certificate: + <<: *cert_query + private_key: | + -----BEGIN PRIVATE KEY----- + MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKIRv+2sbbewm0mj + D+6/tpoUymzYIdFsN+gu02teIr/lZi8ipEB514pyhoaerstzboPteWvniLuwq4KQ + VTEHgoln7J8EaHCnECViGA61pVx8RkJ99cmCkepspROw3I96zBcm58oXs6+Q/BnD + /OWET5sBvR9oTv9GNRVJ1rvSMAEJAgMBAAECgYByu3QO0qF9h7X3JEu0Ld4cKBnB + giQ2uJC/et7KxIJ/LOvw9GopBthyt27KwG1ntBkJpkTuAaQHkyNns7vLkNB0S0IR + +owVFEcKYq9VCHTaiQU8TDp24gN+yPTrpRuH8YhDVq5SfVdVuTMgHVQdj4ya4VlF + Gj+a7+ipxtGiLsVGrQJBAM7p0Fm0xmzi+tBOASUAcVrPLcteFIaTBFwfq16dm/ON + 00Khla8Et5kMBttTbqbukl8mxFjBEEBlhQqb6EdQQ0sCQQDIhHx1a9diG7y/4DQA + 4KvR3FCYwP8PBORlSamegzCo+P1OzxiEo0amX7yQMA5UyiP/kUsZrme2JBZgna8S + p4R7AkEAr7rMhSOPUnMD6V4WgsJ5g1Jp5kqkzBaYoVUUSms5RASz4+cwJVCwTX91 + Y1jcpVIBZmaaY3a0wrx13ajEAa0dOQJBAIpjnb4wqpsEh7VpmJqOdSdGxb1XXfFQ + sA0T1OQYqQnFppWwqrxIL+d9pZdiA1ITnNqyvUFBNETqDSOrUHwwb2cCQGArE+vu + ffPUWQ0j+fiK+covFG8NL7H+26NSGB5+Xsn9uwOGLj7K/YT6CbBtr9hJiuWjM1Al + 0V4ltlTuu2mTMaw= + -----END PRIVATE KEY----- + register: nm_query_all_key_content + +# QUERY OUR USER CERTIFICATE +- name: Query our certificate using signature-based authentication (check_mode) + cisco.aci.aci_aaa_user_certificate: + <<: *cert_query + certificate_name: admin + check_mode: yes + register: cm_query_cert + +- name: Query our certificate using signature-based authentication (normal mode) + cisco.aci.aci_aaa_user_certificate: + <<: *cert_query + certificate_name: admin + register: nm_query_cert + +- name: Verify query_cert + assert: + that: + - cm_query_cert is not changed + - nm_query_cert is not changed + - cm_query_cert == nm_query_cert + +- name: Query our certificate using signature-based authentication (invalid private key file) + cisco.aci.aci_aaa_user_certificate: + <<: *cert_query + private_key: '{{ role_path }}/pki/admin_invalid.key' + certificate_name: admin + register: query_cert_invalid_key + ignore_errors: yes + +- name: Query our certificate using signature-based authentication (certificate file) + cisco.aci.aci_aaa_user_certificate: + <<: *cert_query + private_key: '{{ role_path }}/pki/admin.crt' + certificate_name: admin + register: query_cert_invalid_key + ignore_errors: yes + + +- name: Query our certificate using signature-based authentication (ansible rsa private key file) + cisco.aci.aci_aaa_user_certificate: + <<: *cert_query + private_key: '{{ role_path }}/pki/rsa_ansible.key' + certificate_name: admin + register: query_cert_openssh_rsa_key + ignore_errors: yes + +- name: Query our certificate using signature-based authentication (valid rsa private key file) + cisco.aci.aci_aaa_user_certificate: + <<: *cert_query + private_key: '{{ role_path }}/pki/rsa_user.key' + certificate_name: "{{ item }}" + register: query_cert_ansible_rsa_key + loop: + - "rsa_user" + - "user" + #- "admin" + +- name: Query our certificate using signature-based authentication (private key content for rsa_user certificate) + cisco.aci.aci_aaa_user_certificate: + <<: *cert_query + private_key: | + -----BEGIN PRIVATE KEY----- + MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMI29/1t8f2z1/m8 + pYdj4123BT8KXUzycKk8QnVIpyUSrx11u68EYlfU3aDWWfeOkb1TOpdi81CcmiuM + GWR3+liidr3i+iyKzkQN9rzfqH5qZ13EwFENSVch264stBmk/tcGApaiJlo/B8Yr + JIKQ/qSnPpM4Jd98cMMKsrhdTe4pAgMBAAECgYAX8c8BX9zF+rZWA/wkhRwzIa1z + 6EM4iWt6cgN/kkWJPJR6fVl2aoP1cDki60qMSveM8AX5RCnbdnNLiypWSLSEogdd + bRWyFeF4ZXvivd+Lds2u5Ni3PiCrIpHfNvid2ERCaKhblQRdhi/dTH9Z+3kGspwc + jpKzWMmGjBpqWjWOQQJBAOB3cS/AxbwJ6Fzvbi6sLiK6Ry8eSIMlce3Yyw89oU+M + DGkIbggICCYKxXYIWtBbyxthdQudKFZYbLpCkLSMBXsCQQDdf5ICNN2R0ptYLhSX + kQ4tiGigi1hq93+25Ov1rI8eIFSYlKNcyA/cvwv5ptlXmy1UAyoAdGCbS47pgCwT + Nz+rAkEAtzHkR5PuDXSMluS2KRNPJ/qdxB/UEGzMGdEYkNy8vX5QVpyRqK5dcCbU + V2ukKm7wSe11KEBgPnA2dKGFFkU85wJAD895Vpr7bdtAp2yyn5cFEg74mO0ZZJlC + DoYMqb6lgJsCLtn9RzQonbMtYaadQPmcpLCNIPctpiggjV5OxxhcfQJBAM1ETm8p + /9beBPTS8cJdWHvCRE149H/ZCUxqjFZriJzFYvi0xor85eK8/3V7xaWtTkK25i3+ + xWk+sA3DYYDPGM8= + -----END PRIVATE KEY----- + certificate_name: "{{ item }}" + register: query_cert_key_content + loop: + - "rsa_user" + - "user" + #- "admin" + +# REMOVE CERTIFICATE +- name: Remove certificate (check_mode) + cisco.aci.aci_aaa_user_certificate: &cert_delete + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aaa_user: '{{ aci_username }}' + certificate_name: admin + state: absent + check_mode: yes + register: cm_remove_cert + +- name: Remove certificate (normal mode) + cisco.aci.aci_aaa_user_certificate: *cert_delete + register: nm_remove_cert + +- name: Remove certificate again (check_mode) + cisco.aci.aci_aaa_user_certificate: *cert_delete + check_mode: yes + register: cm_remove_cert_again + +- name: Remove certificate again (normal mode) + cisco.aci.aci_aaa_user_certificate: *cert_delete + register: nm_remove_cert_again + +- name: Verify remove_cert + assert: + that: + - cm_remove_cert is changed + - nm_remove_cert is changed + - cm_remove_cert_again is not changed + - nm_remove_cert_again is not changed
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_port_block_to_access_port/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_port_block_to_access_port/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_port_block_to_access_port/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_port_block_to_access_port/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_port_block_to_access_port/tasks/main.yml new file mode 100644 index 00000000..d8b84168 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_port_block_to_access_port/tasks/main.yml @@ -0,0 +1,254 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com> +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Ensuring Interface Policy Leaf profile does not exist + cisco.aci.aci_interface_policy_leaf_profile: &aci_interface_policy_leaf_profile_absent + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: absent + leaf_interface_profile: leafintprftest + register: leaf_profile_present + +- name: Ensuring Interface Policy Leaf profile does not exist on fex + cisco.aci.aci_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_absent + type: fex + leaf_interface_profile: leafintprftest_fex + +- name: Ensuring Interface Policy Leaf profile exists for kick off + cisco.aci.aci_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_absent + leaf_interface_profile: leafintprftest + state: present + +- name: Ensuring Interface Policy Leaf profile exists for kick off + cisco.aci.aci_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_absent + type: fex + leaf_interface_profile: leafintprftest_fex + state: present + +- name: Ensure Interface Access Port Selector exists for kick of + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: &aci_access_port_to_interface_policy_leaf_profile_present + <<: *aci_interface_policy_leaf_profile_absent + access_port_selector: anstest_accessportselector + state: present + +- name: Ensure Interface Access Port Selector exists for kick of + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + type: fex + leaf_interface_profile: leafintprftest_fex + access_port_selector: anstest_accessportselector_fex + +# TODO: Ensure that leaf Policy Group Exists (module missing) (infra:AccPortGrp) +# Ensure block does not exist. +- name: Bind an Access Port Block to an Interface Access Port Selector - check mode works + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + leaf_port_blk: anstest_leafportblkname + state: absent + +- name: Bind an Access Port Block to an Interface Access Port Selector - check mode works + cisco.aci.aci_access_port_block_to_access_port: &aci_access_port_block_to_access_port_present + <<: *aci_access_port_to_interface_policy_leaf_profile_present + leaf_port_blk: anstest_leafportblkname + leaf_port_blk_description: anstest_leafportblkdesc + fromPort: 13 + toPort: 16 + check_mode: yes + register: accessportblock_to_accessport_check_mode_present + +- name: Bind an Access Port Block to an Interface Access Port Selector - creation works + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_block_to_access_port_present + register: accessportblock_to_accessport_present + +- name: Bind an Access Port Block to an Interface Access Port Selector - 2 + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_block_to_access_port_present + leaf_port_blk: anstest_leafportblkname_2 + leaf_port_blk_description: anstest_leafportblkdesc + fromPort: 25 + toPort: 26 + register: accessportblock_to_accessport_check_mode_present_2 + +- name: Bind an Access Port Block to an Interface Access Port Selector - idempotency works + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_block_to_access_port_present + register: accessportblock_to_accessport_idempotent + +- name: Bind an Access Port Block to an Interface Access Port Selector - update works + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_block_to_access_port_present + toPort: 15 + register: accessportblock_to_accessport_update + +- name: Associate an access port block (single port) to an interface selector on a fex - creation works + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_block_to_access_port_present + type: fex + leaf_interface_profile: leafintprftest_fex + access_port_selector: anstest_accessportselector_fex + leaf_port_blk_description: anstest_leafportblkdesc_fex + leaf_port_blk: fex_blk + fromPort: 14 + toPort: 17 + register: accessportblock_to_accessport_present_fex + +- name: Associate an access port block (single port) to an interface selector on a fex - creation works 2 + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_block_to_access_port_present + type: fex + leaf_interface_profile: leafintprftest_fex + access_port_selector: anstest_accessportselector_fex + leaf_port_blk_description: anstest_leafportblkdesc_fex_2 + leaf_port_blk: fex_blk_2 + fromPort: 20 + toPort: 21 + register: accessportblock_to_accessport_present_fex_2 + + +- name: Associate an access port block (single port) to an interface selector on a fex - idempotency works + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_block_to_access_port_present + type: fex + leaf_interface_profile: leafintprftest_fex + access_port_selector: anstest_accessportselector_fex + leaf_port_blk_description: anstest_leafportblkdesc_fex + leaf_port_blk: fex_blk + fromPort: 14 + toPort: 17 + register: accessportblock_to_accessport_present_fex_idemp + +- name: Associate an access port block (single port) to an interface selector on a fex - update works + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_block_to_access_port_present + type: fex + leaf_interface_profile: leafintprftest_fex + access_port_selector: anstest_accessportselector_fex + leaf_port_blk_description: anstest_leafportblkdesc_fex + leaf_port_blk: fex_blk + fromPort: 23 + toPort: 24 + register: accessportblock_to_accessport_present_fex_update + +# TODO: also test for errors +- name: present assertions - create / indempotency / update works + assert: + that: + - accessportblock_to_accessport_check_mode_present is changed + - accessportblock_to_accessport_present is changed + - accessportblock_to_accessport_present.previous == [] + - accessportblock_to_accessport_present.sent.infraPortBlk.attributes.descr == 'anstest_leafportblkdesc' + - accessportblock_to_accessport_present.sent.infraPortBlk.attributes.name == 'anstest_leafportblkname' + - accessportblock_to_accessport_present.sent.infraPortBlk.attributes.fromPort == '13' + - accessportblock_to_accessport_present.sent.infraPortBlk.attributes.toPort == '16' + - accessportblock_to_accessport_idempotent is not changed + - accessportblock_to_accessport_idempotent.sent == {} + - accessportblock_to_accessport_update is changed + - accessportblock_to_accessport_update.sent.infraPortBlk.attributes.toPort == '15' + - accessportblock_to_accessport_present_fex is changed + - accessportblock_to_accessport_present_fex_idemp is not changed + - accessportblock_to_accessport_present_fex.sent.infraPortBlk.attributes.descr == 'anstest_leafportblkdesc_fex' + - accessportblock_to_accessport_present_fex.sent.infraPortBlk.attributes.fromPort == '14' + - accessportblock_to_accessport_present_fex.sent.infraPortBlk.attributes.toPort == '17' + - accessportblock_to_accessport_present_fex_update.sent.infraPortBlk.attributes.toPort == '24' + +- name: Query Specific port block and access_port_selector binding + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_block_to_access_port_present + state: query + register: binding_query + +- name: Query Specific port block and access_port_selector binding fex + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_block_to_access_port_present + type: fex + leaf_interface_profile: leafintprftest_fex + access_port_selector: anstest_accessportselector_fex + leaf_port_blk: fex_blk + state: query + register: binding_query_fex + +- name: query assertions + assert: + that: + - binding_query is not changed + - binding_query_fex is not changed + - binding_query_fex.current | length >= 1 + - binding_query.current | length >= 1 + - '"api/mo/uni/infra/accportprof-leafintprftest/hports-anstest_accessportselector-typ-range/portblk-anstest_leafportblkname.json" in binding_query.url' + +- name: Remove binding of Access Port Block and Interface Access Port Selector - check mode + cisco.aci.aci_access_port_block_to_access_port: &aci_access_port_block_to_access_port_absent + <<: *aci_access_port_block_to_access_port_present + state: absent + check_mode: yes + register: accessportblock_to_accessport_check_mode_absent + +- name: Remove binding of Access Port Block and Interface Access Port Selector - delete works + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_block_to_access_port_absent + register: accessportblock_to_accessport_absent + +- name: Remove binding of Access Port Block and Interface Access Port Selector - idempotency works + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_block_to_access_port_absent + register: accessportblock_to_accessport_absent_idempotent + +- name: Remove binding of Access Port Block and Interface Access Port Selector - check mode + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + #leaf_port_blk: anstest_leafportblkname + state: absent + ignore_errors: yes + register: accessportblock_to_accessport_absent_missing_param + +- name: Remove binding of Access Port Block and Interface Access Port Selector - delete works + cisco.aci.aci_access_port_block_to_access_port: + <<: *aci_access_port_block_to_access_port_absent + type: fex + leaf_interface_profile: leafintprftest_fex + access_port_selector: anstest_accessportselector_fex + leaf_port_blk: fex_blk + state: absent + register: accessportblock_to_accessport_absent_fex + + +- name: absent assertions + assert: + that: + - accessportblock_to_accessport_check_mode_absent is changed + - accessportblock_to_accessport_check_mode_absent.previous != [] + - accessportblock_to_accessport_absent is changed + - accessportblock_to_accessport_absent.previous == accessportblock_to_accessport_check_mode_absent.previous + - accessportblock_to_accessport_absent_idempotent is not changed + - accessportblock_to_accessport_absent_idempotent.previous == [] + - accessportblock_to_accessport_absent_missing_param is failed + - 'accessportblock_to_accessport_absent_missing_param.msg == "state is absent but all of the following are missing: port_blk"' + - accessportblock_to_accessport_absent_fex is changed + +- name: Ensuring Interface Policy Leaf profile does not exist + cisco.aci.aci_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_absent + leaf_interface_profile: leafintprftest + +- name: Ensuring Interface Policy Leaf profile does not exist on fex + cisco.aci.aci_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_absent + type: fex + leaf_interface_profile: leafintprftest_fex
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_port_to_interface_policy_leaf_profile/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_port_to_interface_policy_leaf_profile/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_port_to_interface_policy_leaf_profile/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_port_to_interface_policy_leaf_profile/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_port_to_interface_policy_leaf_profile/tasks/main.yml new file mode 100644 index 00000000..b13ea6ff --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_port_to_interface_policy_leaf_profile/tasks/main.yml @@ -0,0 +1,205 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com> +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Remove Interface policy leaf profile - Cleanup + cisco.aci.aci_interface_policy_leaf_profile: &aci_interface_policy_leaf_profile_absent + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + leaf_interface_profile: leafintprftest + state: absent + +- name: Remove Interface policy fex profile - Cleanup + cisco.aci.aci_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_absent + type: fex + leaf_interface_profile: fexintprftest + state: absent + +- name: Ensuring bindings do not already exist + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: &aci_access_port_to_interface_policy_leaf_profile_absent + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + leaf_interface_profile: leafintprftest + access_port_selector: anstest_accessportselector + state: absent + +- name: Ensuring bindings do not already exist + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_absent + leaf_interface_profile: leafintprftest_fex + access_port_selector: anstest_accessportselector_fex + state: absent + +- name: Ensuring Interface Policy Leaf profile exists for kick off + cisco.aci.aci_interface_policy_leaf_profile: &aci_interface_policy_leaf_profile_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: present + leaf_interface_profile: leafintprftest + register: leaf_profile_present + +- name: Ensuring Interface Policy Fex profile exists for kick off + cisco.aci.aci_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_present + type: fex + leaf_interface_profile: fexintprftest + +# TODO: Ensure that leaf Policy Group Exists (module missing) (infra:AccPortGrp) + +- name: Bind an Interface Access Port Selector to an Interface Policy Leaf Profile with a Policy Group - check mode works + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: &aci_access_port_to_interface_policy_leaf_profile_present + <<: *aci_interface_policy_leaf_profile_present + access_port_selector: anstest_accessportselector + check_mode: yes + register: accessport_to_intf_check_mode_present + +- name: Bind an Interface Access Port Selector to an Interface Policy Leaf Profile with a Policy Group - creation works + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + register: accessport_to_intf_present + +- name: Bind an Interface Access Port Selector to an Interface Policy Leaf Profile with a Policy Group - idempotency works + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + register: accessport_to_intf_idempotent + +- name: Bind an Interface Access Port Selector to an Interface Policy Profile with a Policy Group - update works + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + policy_group: anstest_policygroupname + register: accessport_to_intf_update + +- name: Bind an Interface Access Port Selector to an Interface Policy Fex Profile with a Policy Group - creation works + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + type: fex + access_port_selector: anstest_fex_accessportselector + leaf_interface_profile: fexintprftest + register: accessport_to_intf_present_fex + +- name: Bind an Interface Access Port Selector to an Interface Policy Fex Profile with a Policy Group - idempotency works + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + type: fex + access_port_selector: anstest_fex_accessportselector + leaf_interface_profile: fexintprftest + register: accessport_to_intf_idempotent_fex + +- name: Bind an Interface Access Port Selector to an Interface Policy Fex Profile with a Policy Group - update works + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + policy_group: anstest_fex_policygroupname + access_port_selector: anstest_fex_accessportselector + type: fex + leaf_interface_profile: fexintprftest + register: accessport_to_intf_update_fex + +# TODO: also test for errors +- name: present assertions + assert: + that: + - accessport_to_intf_check_mode_present is changed + - accessport_to_intf_present is changed + - accessport_to_intf_present.previous == [] + - accessport_to_intf_present.sent.infraHPortS.attributes.name == 'anstest_accessportselector' + - accessport_to_intf_idempotent is not changed + - accessport_to_intf_idempotent.sent == {} + - accessport_to_intf_update is changed + - accessport_to_intf_update.sent.infraHPortS.attributes == {} + - accessport_to_intf_update.sent.infraHPortS.children[0].infraRsAccBaseGrp.attributes.tDn == 'uni/infra/funcprof/accportgrp-anstest_policygroupname' + - accessport_to_intf_present_fex is changed + - accessport_to_intf_present_fex.previous == [] + - accessport_to_intf_present_fex.sent.infraHPortS.attributes.name == 'anstest_fex_accessportselector' + - accessport_to_intf_idempotent_fex is not changed + - accessport_to_intf_idempotent_fex.sent == {} + - accessport_to_intf_update_fex is changed + - accessport_to_intf_update_fex.sent.infraHPortS.children[0].infraRsAccBaseGrp.attributes.tDn == 'uni/infra/funcprof/accportgrp-anstest_fex_policygroupname' + +- name: Query Specific access_port_selector and leaf_interface_profile binding + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_present + access_port_selector: anstest_accessportselector # "{{ fake_var | default(omit) }}" ? + state: query + register: binding_query + +- name: Query Specific access_port_selector and fex_interface_profile binding + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_present + type: fex + access_port_selector: anstest_fex_accessportselector + leaf_interface_profile: fexintprftest + state: query + register: binding_query_fex + +- name: present assertions + assert: + that: + - binding_query is not changed + - binding_query_fex is not changed + - binding_query.current | length >= 1 + - '"api/mo/uni/infra/accportprof-leafintprftest/hports-anstest_accessportselector-typ-range.json" in binding_query.url' + +- name: Remove binding of interface access port selector and Interface Policy Leaf Profile - check mode + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_absent + check_mode: yes + register: accessport_to_intf_check_mode_absent + +- name: Remove binding of interface access port selector and Interface Policy Leaf Profile - delete works + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_absent + register: accessport_to_intf_absent + +- name: Remove binding of interface access port selector and Interface Policy Leaf Profile - idempotency works + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_absent + register: accessport_to_intf_absent_idempotent + +- name: Remove binding of interface access port selector and Interface Policy Fex Profile + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_absent + type: fex + access_port_selector: anstest_fex_accessportselector + leaf_interface_profile: fexintprftest + state: absent + register: accessport_to_intf_absent_fex + +- name: absent assertions + assert: + that: + - accessport_to_intf_check_mode_absent is changed + - accessport_to_intf_absent_fex is changed + - accessport_to_intf_absent is changed + - accessport_to_intf_absent_idempotent is not changed + +- name: Remove Interface policy fex profile - Cleanup + cisco.aci.aci_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_absent + +- name: Remove Interface policy fex profile - Cleanup + cisco.aci.aci_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_absent + type: fex + leaf_interface_profile: fexintprftest + state: absent
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_sub_port_block_to_access_port/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_sub_port_block_to_access_port/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_sub_port_block_to_access_port/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_sub_port_block_to_access_port/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_sub_port_block_to_access_port/tasks/main.yml new file mode 100644 index 00000000..51a53e9b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_access_sub_port_block_to_access_port/tasks/main.yml @@ -0,0 +1,141 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Ensuring Interface Policy Leaf profile exists for kick off + cisco.aci.aci_interface_policy_leaf_profile: &aci_interface_policy_leaf_profile_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: present + leaf_interface_profile: leafintprftest + register: leaf_profile_present + +- name: Ensure Interface Access Port Selector exists for kick of + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: &aci_access_port_to_interface_policy_leaf_profile_present + <<: *aci_interface_policy_leaf_profile_present + access_port_selector: anstest_accessportselector + +# TODO: Ensure that leaf Policy Group Exists (module missing) (infra:AccPortGrp) + +- name: Bind an Access Sub Port Block to an Interface Access Port Selector - check mode works + cisco.aci.aci_access_sub_port_block_to_access_port: &aci_access_sub_port_block_to_access_port_present + <<: *aci_access_port_to_interface_policy_leaf_profile_present + leaf_port_blk: anstest_leafportblkname + leaf_port_blk_description: anstest_leafportblkdesc + fromPort: 13 + toPort: 13 + fromSubPort: 1 + toSubPort: 3 + check_mode: yes + register: accesssubportblock_to_accessport_check_mode_present + +- name: Bind an Access Sub Port Block to an Interface Access Port Selector - creation works + cisco.aci.aci_access_sub_port_block_to_access_port: + <<: *aci_access_sub_port_block_to_access_port_present + register: accesssubportblock_to_accessport_present + +- name: Bind an Access Sub Port Block to an Interface Access Port Selector - idempotency works + cisco.aci.aci_access_sub_port_block_to_access_port: + <<: *aci_access_sub_port_block_to_access_port_present + register: accesssubportblock_to_accessport_idempotent + +- name: Bind an Access Sub Port Block to an Interface Access Port Selector - update works + cisco.aci.aci_access_sub_port_block_to_access_port: + <<: *aci_access_sub_port_block_to_access_port_present + toSubPort: 2 + register: accesssubportblock_to_accessport_update + +# TODO: also test for errors +- name: present assertions + assert: + that: + - accesssubportblock_to_accessport_check_mode_present is changed + - accesssubportblock_to_accessport_present is changed + - accesssubportblock_to_accessport_present.previous == [] + - accesssubportblock_to_accessport_present.sent.infraSubPortBlk.attributes.descr == 'anstest_leafportblkdesc' + - accesssubportblock_to_accessport_present.sent.infraSubPortBlk.attributes.name == 'anstest_leafportblkname' + - accesssubportblock_to_accessport_present.sent.infraSubPortBlk.attributes.fromPort == accesssubportblock_to_accessport_present.sent.infraSubPortBlk.attributes.toPort == '13' + - accesssubportblock_to_accessport_present.sent.infraSubPortBlk.attributes.fromSubPort == '1' + - accesssubportblock_to_accessport_present.sent.infraSubPortBlk.attributes.toSubPort == '3' + - accesssubportblock_to_accessport_idempotent is not changed + - accesssubportblock_to_accessport_idempotent.sent == {} + - accesssubportblock_to_accessport_update is changed + - accesssubportblock_to_accessport_update.sent.infraSubPortBlk.attributes.toSubPort == '2' + + +- name: Query Specific sub port block and access_port_selector binding + cisco.aci.aci_access_sub_port_block_to_access_port: + <<: *aci_access_sub_port_block_to_access_port_present + state: query + register: binding_query + +- name: present assertions + assert: + that: + - binding_query is not changed + - binding_query.current | length >= 1 + - '"api/mo/uni/infra/accportprof-leafintprftest/hports-anstest_accessportselector-typ-range/subportblk-anstest_leafportblkname.json" in binding_query.url' + +- name: Remove binding of Access Sub Port Block and Interface Access Port Selector - check mode + cisco.aci.aci_access_sub_port_block_to_access_port: &aci_access_sub_port_block_to_access_port_absent + <<: *aci_access_sub_port_block_to_access_port_present + state: absent + check_mode: yes + register: accesssubportblock_to_accessport_check_mode_absent + +- name: Remove binding of Access Sub Port Block and Interface Access Port Selector - delete works + cisco.aci.aci_access_sub_port_block_to_access_port: + <<: *aci_access_sub_port_block_to_access_port_absent + register: accesssubportblock_to_accessport_absent + +- name: Remove binding of Access Sub Port Block and Interface Access Port Selector - idempotency works + cisco.aci.aci_access_sub_port_block_to_access_port: + <<: *aci_access_sub_port_block_to_access_port_absent + register: accesssubportblock_to_accessport_absent_idempotent + +- name: Remove binding of Access Sub Port Block and Interface Access Port Selector - check mode + cisco.aci.aci_access_sub_port_block_to_access_port: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + #leaf_port_blk: anstest_leafportblkname + state: absent + ignore_errors: yes + register: accesssubportblock_to_accessport_absent_missing_param + +- name: absent assertions + assert: + that: + - accesssubportblock_to_accessport_check_mode_absent is changed + - accesssubportblock_to_accessport_check_mode_absent.previous != [] + - accesssubportblock_to_accessport_absent is changed + - accesssubportblock_to_accessport_absent.previous == accesssubportblock_to_accessport_check_mode_absent.previous + - accesssubportblock_to_accessport_absent_idempotent is not changed + - accesssubportblock_to_accessport_absent_idempotent.previous == [] + - accesssubportblock_to_accessport_absent_missing_param is failed + - 'accesssubportblock_to_accessport_absent_missing_param.msg == "state is absent but all of the following are missing: leaf_port_blk"' + + +- name: Remove binding of Access Sub Port Block and Interface Access Port Selector - Clean up + cisco.aci.aci_access_sub_port_block_to_access_port: + <<: *aci_access_sub_port_block_to_access_port_present + state: absent + +- name: Remove Interface Access Port Selector - Cleanup + cisco.aci.aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + state: absent + +- name: Remove Interface policy leaf profile - Cleanup + cisco.aci.aci_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_present + state: absent diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aep/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aep/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aep/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aep/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aep/tasks/main.yml new file mode 100644 index 00000000..90e17f38 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aep/tasks/main.yml @@ -0,0 +1,331 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + + +# CLEAN ENVIRONMENT +- name: Remove AEP + cisco.aci.aci_aep: &aep_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aep: '{{ item }}' + state: absent + loop: + - ansible_test + - ansible_test2 + - ansible_test3 + +# ADD AEP +- name: Add AEP (check_mode) + cisco.aci.aci_aep: &aep_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aep: ansible_test + infra_vlan: true + state: present + check_mode: yes + register: cm_add_aep + +- name: Add AEP (normal mode) + cisco.aci.aci_aep: *aep_present + register: nm_add_aep + +- name: Add another AEP (normal mode) + cisco.aci.aci_aep: + <<: *aep_present + aep: ansible_test2 + infra_vlan: true + register: nm_add_aep2 + +- name: Add another AEP (normal mode) + cisco.aci.aci_aep: + <<: *aep_present + aep: ansible_test3 + infra_vlan: false + register: nm_add_aep3 + +- name: Verify add_aep + assert: + that: + - cm_add_aep is changed + - nm_add_aep is changed + - nm_add_aep2 is changed + - nm_add_aep3 is changed + - nm_add_aep.previous == nm_add_aep.previous == cm_add_aep.current == [] + - nm_add_aep.current[0].infraAttEntityP.attributes.dn == 'uni/infra/attentp-ansible_test' + - nm_add_aep.current[0].infraAttEntityP.attributes.name == 'ansible_test' + - cm_add_aep.proposed.infraAttEntityP.attributes.name == nm_add_aep.proposed.infraAttEntityP.attributes.name == cm_add_aep.sent.infraAttEntityP.attributes.name == nm_add_aep.sent.infraAttEntityP.attributes.name == 'ansible_test' + - nm_add_aep2.current[0].infraAttEntityP.children[0].infraProvAcc.attributes.name == 'provacc' + - nm_add_aep3.current[0].infraAttEntityP | length == 1 + +- name: Change AEP infra(normal mode) + cisco.aci.aci_aep: + <<: *aep_present + aep: ansible_test2 + infra_vlan: false + register: nm_add_aep_true_to_false + +- name: Change AEP infra again(normal mode) + cisco.aci.aci_aep: + <<: *aep_present + aep: ansible_test2 + infra_vlan: false + register: nm_add_aep_true_to_false_again + +- name: Add AEP again (check_mode) + cisco.aci.aci_aep: *aep_present + check_mode: yes + register: cm_add_aep_again + +- name: Add AEP again (normal mode) + cisco.aci.aci_aep: *aep_present + register: nm_add_aep_again + +- name: Verify add_aep_again + assert: + that: + - cm_add_aep_again is not changed + - nm_add_aep_again is not changed + - nm_add_aep_again is not changed + - nm_add_aep_again.previous[0].infraAttEntityP.attributes.dn == nm_add_aep_again.previous[0].infraAttEntityP.attributes.dn == cm_add_aep_again.current[0].infraAttEntityP.attributes.dn == nm_add_aep_again.current[0].infraAttEntityP.attributes.dn == 'uni/infra/attentp-ansible_test' + - nm_add_aep_again.previous[0].infraAttEntityP.attributes.name == nm_add_aep_again.previous[0].infraAttEntityP.attributes.name == cm_add_aep_again.current[0].infraAttEntityP.attributes.name == nm_add_aep_again.current[0].infraAttEntityP.attributes.name == 'ansible_test' + - cm_add_aep_again.proposed.infraAttEntityP.attributes.name == nm_add_aep_again.proposed.infraAttEntityP.attributes.name == 'ansible_test' + - cm_add_aep_again.sent == nm_add_aep_again.sent == {} + - nm_add_aep_true_to_false.current[0].infraAttEntityP | length == 1 + - nm_add_aep_true_to_false_again.current[0].infraAttEntityP | length == 1 + +# CHANGE AEP +- name: Change description of AEP (check_mode) + cisco.aci.aci_aep: + <<: *aep_present + description: Ansible test AEP + check_mode: yes + register: cm_add_aep_descr + +- name: Change description of AEP (normal mode) + cisco.aci.aci_aep: + <<: *aep_present + description: Ansible test AEP + register: nm_add_aep_descr + +- name: Verify add_aep_descr + assert: + that: + - cm_add_aep_descr is changed + - nm_add_aep_descr is changed + - cm_add_aep_descr.proposed.infraAttEntityP.attributes.descr == nm_add_aep_descr.proposed.infraAttEntityP.attributes.descr == 'Ansible test AEP' + - cm_add_aep_descr.proposed.infraAttEntityP.attributes.name == nm_add_aep_descr.proposed.infraAttEntityP.attributes.name == 'ansible_test' + - cm_add_aep_descr.sent.infraAttEntityP.attributes.descr == nm_add_aep_descr.sent.infraAttEntityP.attributes.descr == 'Ansible test AEP' + - cm_add_aep_descr.previous.0.infraAttEntityP.attributes.dn == nm_add_aep_descr.previous.0.infraAttEntityP.attributes.dn == cm_add_aep_descr.current.0.infraAttEntityP.attributes.dn == 'uni/infra/attentp-ansible_test' + - cm_add_aep_descr.previous.0.infraAttEntityP.attributes.name == nm_add_aep_descr.previous.0.infraAttEntityP.attributes.name == cm_add_aep_descr.current.0.infraAttEntityP.attributes.name == 'ansible_test' + - nm_add_aep_descr.current.0.infraAttEntityP.attributes.descr == 'Ansible test AEP' + - nm_add_aep_descr.current.0.infraAttEntityP.attributes.dn == 'uni/infra/attentp-ansible_test' + - nm_add_aep_descr.current.0.infraAttEntityP.attributes.name == 'ansible_test' + +- name: Change description of AEP again (check_mode) + cisco.aci.aci_aep: + <<: *aep_present + description: Ansible test AEP + check_mode: yes + register: cm_add_aep_descr_again + +- name: Change description of AEP again (normal mode) + cisco.aci.aci_aep: + <<: *aep_present + description: Ansible test AEP + register: nm_add_aep_descr_again + +- name: Verify add_aep_descr_again + assert: + that: + - cm_add_aep_descr_again is not changed + - nm_add_aep_descr_again is not changed + - cm_add_aep_descr_again.proposed.infraAttEntityP.attributes.descr == nm_add_aep_descr_again.proposed.infraAttEntityP.attributes.descr == 'Ansible test AEP' + - cm_add_aep_descr_again.proposed.infraAttEntityP.attributes.name == nm_add_aep_descr_again.proposed.infraAttEntityP.attributes.name == 'ansible_test' + - cm_add_aep_descr_again.sent == nm_add_aep_descr_again.sent == {} + - cm_add_aep_descr_again.previous[0].infraAttEntityP.attributes.descr == nm_add_aep_descr_again.previous[0].infraAttEntityP.attributes.descr == cm_add_aep_descr_again.current[0].infraAttEntityP.attributes.descr == nm_add_aep_descr_again.current[0].infraAttEntityP.attributes.descr == 'Ansible test AEP' + - cm_add_aep_descr_again.previous[0].infraAttEntityP.attributes.dn == nm_add_aep_descr_again.previous[0].infraAttEntityP.attributes.dn == cm_add_aep_descr_again.current[0].infraAttEntityP.attributes.dn == nm_add_aep_descr_again.current[0].infraAttEntityP.attributes.dn == 'uni/infra/attentp-ansible_test' + - cm_add_aep_descr_again.previous[0].infraAttEntityP.attributes.name == nm_add_aep_descr_again.previous[0].infraAttEntityP.attributes.name == cm_add_aep_descr_again.current[0].infraAttEntityP.attributes.name == nm_add_aep_descr_again.current[0].infraAttEntityP.attributes.name == 'ansible_test' + + +# ADD AEP AGAIN +- name: Add AEP again with no description (check_mode) + cisco.aci.aci_aep: *aep_present + check_mode: yes + register: cm_add_aep_again_no_descr + +- name: Add AEP again with no description (normal mode) + cisco.aci.aci_aep: *aep_present + register: nm_add_aep_again_no_descr + +- name: Verify add_aep_again_no_descr + assert: + that: + - cm_add_aep_again_no_descr is not changed + - nm_add_aep_again_no_descr is not changed + - cm_add_aep_again_no_descr.proposed.infraAttEntityP.attributes.name == nm_add_aep_again_no_descr.proposed.infraAttEntityP.attributes.name == 'ansible_test' + - cm_add_aep_again_no_descr.sent == nm_add_aep_again_no_descr.sent == {} + - cm_add_aep_again_no_descr.previous[0].infraAttEntityP.attributes.descr == nm_add_aep_again_no_descr.previous[0].infraAttEntityP.attributes.descr == cm_add_aep_again_no_descr.current[0].infraAttEntityP.attributes.descr == nm_add_aep_again_no_descr.current[0].infraAttEntityP.attributes.descr == 'Ansible test AEP' + - cm_add_aep_again_no_descr.previous[0].infraAttEntityP.attributes.dn == nm_add_aep_again_no_descr.previous[0].infraAttEntityP.attributes.dn == cm_add_aep_again_no_descr.current[0].infraAttEntityP.attributes.dn == nm_add_aep_again_no_descr.current[0].infraAttEntityP.attributes.dn == 'uni/infra/attentp-ansible_test' + - cm_add_aep_again_no_descr.previous[0].infraAttEntityP.attributes.name == nm_add_aep_again_no_descr.previous[0].infraAttEntityP.attributes.name == cm_add_aep_again_no_descr.current[0].infraAttEntityP.attributes.name == nm_add_aep_again_no_descr.current[0].infraAttEntityP.attributes.name == 'ansible_test' + + +# QUERY ALL AEPS +- name: Query all AEPs (check_mode) + cisco.aci.aci_aep: &aep_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_aeps + +- name: Query all AEPs (normal mode) + cisco.aci.aci_aep: *aep_query + register: nm_query_all_aeps + +- name: Verify query_all_aeps + assert: + that: + - cm_query_all_aeps is not changed + - nm_query_all_aeps is not changed + - cm_query_all_aeps == nm_query_all_aeps + - nm_query_all_aeps.current|length >= 1 + + +# QUERY A AEP +- name: Query our AEP + cisco.aci.aci_aep: + <<: *aep_query + aep: ansible_test + check_mode: yes + register: cm_query_aep + +- name: Query our AEP + cisco.aci.aci_aep: + <<: *aep_query + aep: ansible_test + register: nm_query_aep + +- name: Verify query_aep + assert: + that: + - cm_query_aep is not changed + - nm_query_aep is not changed + - cm_query_aep == nm_query_aep + - nm_query_aep.current.0.infraAttEntityP.attributes.descr == "Ansible test AEP" + - nm_query_aep.current.0.infraAttEntityP.attributes.dn == "uni/infra/attentp-ansible_test" + - nm_query_aep.current.0.infraAttEntityP.attributes.name == "ansible_test" + + +# REMOVE AEP +- name: Remove AEP (check_mode) + cisco.aci.aci_aep: + <<: *aep_absent + aep: ansible_test + check_mode: yes + register: cm_remove_aep + +- name: Remove AEP (normal mode) + cisco.aci.aci_aep: + <<: *aep_absent + aep: ansible_test + register: nm_remove_aep + +- name: Verify remove_aep + assert: + that: + - cm_remove_aep is changed + - nm_remove_aep is changed + - cm_remove_aep.proposed == nm_remove_aep.proposed == {} + - cm_remove_aep.sent == nm_remove_aep.sent == {} + - cm_remove_aep.previous[0].infraAttEntityP.attributes.name == nm_remove_aep.previous[0].infraAttEntityP.attributes.name == cm_remove_aep.current[0].infraAttEntityP.attributes.name == 'ansible_test' + - cm_remove_aep.previous[0].infraAttEntityP.attributes.descr == nm_remove_aep.previous[0].infraAttEntityP.attributes.descr == cm_remove_aep.current[0].infraAttEntityP.attributes.descr == 'Ansible test AEP' + - cm_remove_aep.previous[0].infraAttEntityP.attributes.dn == nm_remove_aep.previous[0].infraAttEntityP.attributes.dn == cm_remove_aep.current[0].infraAttEntityP.attributes.dn == 'uni/infra/attentp-ansible_test' + - nm_remove_aep.current == [] + +- name: Remove AEP again (check_mode) + cisco.aci.aci_aep: + <<: *aep_absent + aep: ansible_test + check_mode: yes + register: cm_remove_aep_again + +- name: Remove AEP again (normal mode) + cisco.aci.aci_aep: + <<: *aep_absent + aep: ansible_test + register: nm_remove_aep_again + +- name: Verify remove_aep_again + assert: + that: + - cm_remove_aep_again is not changed + - nm_remove_aep_again is not changed + - cm_remove_aep_again.proposed == nm_remove_aep_again.proposed == {} + - cm_remove_aep_again.sent == nm_remove_aep_again.sent == {} + - cm_remove_aep_again.previous == nm_remove_aep_again.previous == cm_remove_aep_again.current == nm_remove_aep_again.current == [] + + +# QUERY NON-EXISTING AEP +- name: Query non-existing AEP (check_mode) + cisco.aci.aci_aep: + <<: *aep_query + aep: ansible_test + check_mode: yes + register: cm_query_non_aep + +- name: Query non-existing AEP (normal mode) + cisco.aci.aci_aep: + <<: *aep_query + aep: ansible_test + register: nm_query_non_aep + +- name: Verify query_non_aep + assert: + that: + - cm_query_non_aep is not changed + - nm_query_non_aep is not changed + - cm_query_non_aep == nm_query_non_aep + - cm_query_non_aep.current == nm_query_non_aep.current == [] + + +# PROVOKE ERRORS +- name: Error when required parameter is missing + cisco.aci.aci_aep: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: present + ignore_errors: yes + register: error_on_missing_required_param + +- name: Verify error_on_missing_required_param + assert: + that: + - error_on_missing_required_param is failed + - 'error_on_missing_required_param.msg == "state is present but all of the following are missing: aep"' diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aep_to_domain/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aep_to_domain/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aep_to_domain/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aep_to_domain/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aep_to_domain/tasks/main.yml new file mode 100644 index 00000000..09000b9c --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_aep_to_domain/tasks/main.yml @@ -0,0 +1,212 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# CLEAN ENVIRONMENT +- name: Remove AEP to domain binding + cisco.aci.aci_aep_to_domain: &binding_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aep: test_aep + domain: phys_dom + domain_type: phys + state: absent + +- name: Create AEP + cisco.aci.aci_aep: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aep: test_aep + description: Test AEP + state: present + +- name: Create physical domain + cisco.aci.aci_domain: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: phys_dom + domain_type: phys + state: present + + +# ADD BINDING +- name: Add AEP to domain binding (check_mode) + cisco.aci.aci_aep_to_domain: &binding_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + aep: test_aep + domain: phys_dom + domain_type: phys + state: present + check_mode: yes + register: cm_add_binding + +- name: Add AEP to domain binding (normal mode) + cisco.aci.aci_aep_to_domain: *binding_present + register: nm_add_binding + +- name: Verify add_binding + assert: + that: + - cm_add_binding is changed + - nm_add_binding is changed + - cm_add_binding.sent.infraRsDomP.attributes.tDn == nm_add_binding.sent.infraRsDomP.attributes.tDn == 'uni/phys-phys_dom' + - cm_add_binding.proposed.infraRsDomP.attributes.tDn == nm_add_binding.proposed.infraRsDomP.attributes.tDn == 'uni/phys-phys_dom' + - cm_add_binding.current == cm_add_binding.previous == nm_add_binding.previous == [] + - nm_add_binding.current[0].infraRsDomP.attributes.dn == 'uni/infra/attentp-test_aep/rsdomP-[uni/phys-phys_dom]' + - nm_add_binding.current[0].infraRsDomP.attributes.tDn == 'uni/phys-phys_dom' + +- name: Add AEP to domain binding again (check_mode) + cisco.aci.aci_aep_to_domain: *binding_present + check_mode: yes + register: cm_add_binding_again + +- name: Add AEP to domain binding again (normal mode) + cisco.aci.aci_aep_to_domain: *binding_present + register: nm_add_binding_again + +- name: Verify add_binding_again + assert: + that: + - cm_add_binding_again is not changed + - nm_add_binding_again is not changed + + +# QUERY ALL BINDINGS +- name: Query all AEP to domain bindings (check_mode) + cisco.aci.aci_aep_to_domain: &binding_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_bindings + +- name: Query all AEP to domain bindings (normal mode) + cisco.aci.aci_aep_to_domain: *binding_query + register: nm_query_all_bindings + +- name: Verify query_all_bindings + assert: + that: + - cm_query_all_bindings is not changed + - nm_query_all_bindings is not changed + - cm_query_all_bindings == nm_query_all_bindings + - nm_query_all_bindings.current|length >= 1 + + +# QUERY A BINDING +- name: Query our AEP to domain binding (check_mode) + cisco.aci.aci_aep_to_domain: + <<: *binding_query + aep: test_aep + domain: phys_dom + domain_type: phys + check_mode: yes + register: cm_query_binding + +- name: Query our AEP to domain binding (normal mode) + cisco.aci.aci_aep_to_domain: + <<: *binding_query + aep: test_aep + domain: phys_dom + domain_type: phys + register: nm_query_binding + +- name: Verify query_binding + assert: + that: + - cm_query_binding is not changed + - nm_query_binding is not changed + - cm_query_binding == nm_query_binding + - nm_query_binding.current.0.infraRsDomP.attributes.dn == 'uni/infra/attentp-test_aep/rsdomP-[uni/phys-phys_dom]' + - nm_query_binding.current.0.infraRsDomP.attributes.tCl == 'physDomP' + - nm_query_binding.current.0.infraRsDomP.attributes.tDn == 'uni/phys-phys_dom' + + +# REMOVE BINDING +- name: Remove AEP to domain binding (check_mode) + cisco.aci.aci_aep_to_domain: *binding_absent + check_mode: yes + register: cm_remove_binding + +- name: Remove AEP to domain binding (normal mode) + cisco.aci.aci_aep_to_domain: *binding_absent + register: nm_remove_binding + +- name: Verify remove_binding + assert: + that: + - cm_remove_binding is changed + - nm_remove_binding is changed + - cm_remove_binding.current.0.infraRsDomP.attributes.dn == cm_remove_binding.previous.0.infraRsDomP.attributes.dn == nm_remove_binding.previous.0.infraRsDomP.attributes.dn == 'uni/infra/attentp-test_aep/rsdomP-[uni/phys-phys_dom]' + - cm_remove_binding.current.0.infraRsDomP.attributes.tDn == cm_remove_binding.previous.0.infraRsDomP.attributes.tDn == nm_remove_binding.previous.0.infraRsDomP.attributes.tDn == 'uni/phys-phys_dom' + - nm_remove_binding.current == [] + +- name: Remove AEP to domain binding again (check_mode) + cisco.aci.aci_aep_to_domain: *binding_absent + check_mode: yes + register: cm_remove_binding_again + +- name: Remove AEP to domain binding again (normal mode) + cisco.aci.aci_aep_to_domain: *binding_absent + register: nm_remove_binding_again + +- name: Verify remove_binding_again + assert: + that: + - cm_remove_binding_again is not changed + - nm_remove_binding_again is not changed + + +# QUERY NON-EXISTING BINDING +- name: Query non-existing AEP to domain binding (check_mode) + cisco.aci.aci_aep_to_domain: + <<: *binding_query + aep: test_aep + domain: phys_dom + domain_type: phys + check_mode: yes + register: cm_query_non_binding + +- name: Query non-existing AEP to domain binding (normal mode) + cisco.aci.aci_aep_to_domain: + <<: *binding_query + aep: test_aep + domain: phys_dom + domain_type: phys + register: nm_query_non_binding + +- name: Verify query_non_binding + assert: + that: + - cm_query_non_binding is not changed + - nm_query_non_binding is not changed + - cm_query_non_binding == nm_query_non_binding + - nm_query_non_binding.current == [] diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_ap/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_ap/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_ap/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_ap/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_ap/tasks/main.yml new file mode 100644 index 00000000..104b620e --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_ap/tasks/main.yml @@ -0,0 +1,190 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) +# Copyright: (c) 2020, Shreyas Srish (@shrsr) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: present + tenant: anstest + register: tenant_present + +- name: ensure monitoring policy exists + cisco.aci.aci_epg_monitoring_policy: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + monitoring_policy: check + tenant: anstest + +- name: ensure ap does not exist initially + cisco.aci.aci_ap: + <<: *aci_tenant_present + ap: anstest + state: absent + +- name: create ap - check mode works + cisco.aci.aci_ap: &aci_ap_present + <<: *aci_tenant_present + ap: anstest + description: Ansible Test + monitoring_policy: check + check_mode: yes + register: ap_present_check_mode + +- name: create ap - creation works + cisco.aci.aci_ap: + <<: *aci_ap_present + register: ap_present + +- name: create ap - extra for query + cisco.aci.aci_ap: + <<: *aci_tenant_present + ap: anstest2 + +- name: create ap - idempotency works + cisco.aci.aci_ap: + <<: *aci_ap_present + register: ap_present_idempotent + +- name: update ap - update works + cisco.aci.aci_ap: + <<: *aci_ap_present + description: Ansible Test Update + register: ap_present_update + +- name: create ap - creation works + cisco.aci.aci_ap: + <<: *aci_tenant_present + ignore_errors: yes + register: ap_present_missing_param + +- name: present asserts + assert: + that: + - ap_present_check_mode is changed + - ap_present is changed + - ap_present.previous == [] + - ap_present.current.0.fvAp.children.0.fvRsApMonPol.attributes.tnMonEPGPolName == 'check' + - ap_present.sent == ap_present_check_mode.sent + - ap_present.sent.fvAp.attributes.descr == 'Ansible Test' + - ap_present.sent.fvAp.attributes.name == 'anstest' + - ap_present_idempotent is not changed + - ap_present_idempotent.previous != [] + - ap_present_idempotent.sent == {} + - ap_present_update is changed + - ap_present_update.sent.fvAp.attributes.descr == 'Ansible Test Update' + - ap_present_missing_param is failed + - 'ap_present_missing_param.msg == "state is present but all of the following are missing: ap"' + +- name: get ap - query specific ap + cisco.aci.aci_ap: &aci_ap_query + <<: *aci_ap_present + state: query + register: query_ap + +- name: get all ap for tenant - query tenant aps + cisco.aci.aci_ap: + <<: *aci_ap_query + ap: "{{ fakevar | default(omit) }}" + register: query_ap_tenant + +- name: get all ap by name - query ap name + cisco.aci.aci_ap: + <<: *aci_ap_query + tenant: "{{ fakevar | default(omit) }}" + register: query_ap_ap + +- name: get all aps - query general + cisco.aci.aci_ap: + <<: *aci_ap_query + tenant: "{{ fakevar | default(omit) }}" + ap: "{{ fakevar | default(omit) }}" + register: query_all + +- name: query assertions + assert: + that: + - query_ap is not changed + - query_ap.current | length == 1 + - query_ap.current.0.fvAp.attributes.name == "anstest" + - query_ap.current.0.fvAp.children.0.fvRsApMonPol.attributes.tnMonEPGPolName == 'check' + - '"tn-anstest/ap-anstest.json" in query_ap.url' + - query_ap_tenant is not changed + - query_ap_tenant.current | length == 1 + - query_ap_tenant.current.0.fvTenant.children | length == 2 + - '"rsp-subtree-class=fvAp" in query_ap_tenant.filter_string' + - '"tn-anstest.json" in query_ap_tenant.url' + - query_ap_ap is not changed + - query_ap_ap.current != [] + - query_ap_ap.current.0.fvAp is defined + - '"query-target-filter=eq(fvAp.name,\"anstest\")" in query_ap_ap.filter_string' + - '"class/fvAp.json" in query_ap_ap.url' + - query_all is not changed + - query_all.current | length > 1 + - '"class/fvAp.json" in query_all.url' + +- name: delete ap - check_mode works + cisco.aci.aci_ap: &aci_ap_absent + <<: *aci_ap_present + state: absent + check_mode: yes + register: ap_delete_check_mode + +- name: delete ap - delete works + cisco.aci.aci_ap: + <<: *aci_ap_absent + register: ap_delete + +- name: delete ap - delete idempotency works + cisco.aci.aci_ap: + <<: *aci_ap_absent + register: ap_delete_idempotent + +- name: delete ap - missing param error + cisco.aci.aci_ap: + <<: *aci_ap_absent + tenant: "{{ fakevar | default(omit) }}" + ignore_errors: yes + register: ap_delete_missing_param + +- name: delete ap remove ap used for query + cisco.aci.aci_ap: + <<: *aci_ap_absent + ap: anstest2 + +- name: absent assertions + assert: + that: + - ap_delete_check_mode is changed + - ap_delete_check_mode.previous != [] + - '"tn-anstest/ap-anstest.json" in ap_delete_check_mode.url' + - ap_delete is changed + - ap_delete.previous == ap_delete_check_mode.previous + - ap_delete_idempotent is not changed + - ap_delete_idempotent.previous == [] + - ap_delete_missing_param is failed + - 'ap_delete_missing_param.msg == "state is absent but all of the following are missing: tenant"' + +- name: delete tenant - cleanup before ending tests + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent + when: tenant_present is changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd/tasks/main.yml new file mode 100644 index 00000000..2d5df49b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd/tasks/main.yml @@ -0,0 +1,213 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: present + tenant: anstest + register: tenant_present + +- name: ensure vrf exists for tests to kick off + cisco.aci.aci_vrf: &aci_vrf_present + <<: *aci_tenant_present + vrf: anstest + register: vrf_present + +- name: ensure bd anstest does not exist + cisco.aci.aci_bd: + <<: *aci_tenant_present + bd: anstest + state: absent + +- name: ensure bd anstest2 does not exist + cisco.aci.aci_bd: + <<: *aci_tenant_present + bd: anstest2 + state: absent + +- name: create bd - check mode works + cisco.aci.aci_bd: &aci_bd_present + <<: *aci_tenant_present + bd: anstest + description: Ansible Test + check_mode: yes + register: bd_present_check_mode + +- name: create bd - creation works + cisco.aci.aci_bd: + <<: *aci_bd_present + register: bd_present + +- name: create bd again - idempotency works + cisco.aci.aci_bd: + <<: *aci_bd_present + register: bd_present_idempotent + +- name: update bd - update works + cisco.aci.aci_bd: + <<: *aci_bd_present + vrf: anstest + description: Ansible Test Update + register: bd_update + +- name: create another bd - check more params + cisco.aci.aci_bd: + <<: *aci_bd_present + bd: anstest2 + ip_learning: "no" + l2_unknown_unicast: flood + l3_unknown_multicast: opt-flood + multi_dest: drop + enable_routing: "no" + arp_flooding: "yes" + register: bd_present_2 + +- name: create bd without all necessary params - failure message works + cisco.aci.aci_bd: + <<: *aci_bd_present + tenant: "{{ fake_var | default(omit) }}" + ignore_errors: yes + register: bd_present_missing_param + +- name: present asserts + assert: + that: + - bd_present_check_mode is changed + - bd_present_check_mode.sent.fvBD.attributes.descr == 'Ansible Test' + - bd_present_check_mode.sent.fvBD.attributes.name == 'anstest' + - bd_present is changed + - bd_present.sent == bd_present_check_mode.sent + - bd_present.previous == [] + - bd_present_idempotent is not changed + - bd_present_idempotent.previous != [] + - bd_update is changed + - bd_update.previous != [] + - bd_update.sent != bd_update.proposed + - bd_update.sent.fvBD.attributes.descr == 'Ansible Test Update' + - bd_update.sent.fvBD.children.0.fvRsCtx.attributes.tnFvCtxName == 'anstest' + - bd_present_2.sent.fvBD.attributes.arpFlood == 'yes' + - bd_present_2.sent.fvBD.attributes.descr == 'Ansible Test' + - bd_present_2.sent.fvBD.attributes.ipLearning == 'no' + - bd_present_2.sent.fvBD.attributes.multiDstPktAct == 'drop' + - bd_present_2.sent.fvBD.attributes.name == 'anstest2' + - bd_present_2.sent.fvBD.attributes.unicastRoute == 'no' + - bd_present_2.sent.fvBD.attributes.unkMacUcastAct == 'flood' + - bd_present_2.sent.fvBD.attributes.unkMcastAct == 'opt-flood' + - bd_present_missing_param is failed + - 'bd_present_missing_param.msg == "state is present but all of the following are missing: tenant"' + +- name: get all bd + cisco.aci.aci_bd: &aci_query + <<: *aci_tenant_present + state: query + tenant: "{{ fake_var | default(omit) }}" + register: query_all + +- name: get all in tenant + cisco.aci.aci_bd: + <<: *aci_query + tenant: anstest + register: query_tenant + +- name: get all with name + cisco.aci.aci_bd: + <<: *aci_query + bd: anstest + register: query_bd_bd + +- name: get bd + cisco.aci.aci_bd: + <<: *aci_bd_present + state: query + register: query_bd + +- name: query asserts + assert: + that: + - query_all is not changed + - query_all.current | length > 1 + - query_all.current.0.fvBD is defined + - '"rsp-subtree-class=fvRsBDToNdP,fvRsBdToEpRet,fvRsCtx,fvRsIgmpsn" in query_all.filter_string' + - '"class/fvBD.json" in query_all.url' + - query_tenant is not changed + - query_tenant.current | length == 1 + - query_tenant.current.0.fvTenant.children | length == 2 + - '"rsp-subtree-class=fvBD,fvRsBDToNdP,fvRsBdToEpRet,fvRsCtx,fvRsIgmpsn" in query_tenant.filter_string' + - '"tn-anstest.json" in query_tenant.url' + - query_bd_bd is not changed + - query_bd_bd.current != [] + - '"query-target-filter=eq(fvBD.name,\"anstest\")" in query_bd_bd.filter_string' + - '"rsp-subtree-class=fvRsBDToNdP,fvRsBdToEpRet,fvRsCtx,fvRsIgmpsn" in query_bd_bd.filter_string' + - '"class/fvBD.json" in query_bd_bd.url' + - query_bd is not changed + - query_bd.current | length == 1 + - query_bd.current.0.fvBD.attributes.name == "anstest" + - '"rsp-subtree-class=fvRsBDToNdP,fvRsBdToEpRet,fvRsCtx,fvRsIgmpsn" in query_bd.filter_string' + - '"tn-anstest/BD-anstest.json" in query_bd.url' + +- name: delete bd - check mode works + cisco.aci.aci_bd: &aci_bd_absent + <<: *aci_bd_present + state: absent + check_mode: yes + register: bd_absent_check_mode + +- name: delete bd - delete works + cisco.aci.aci_bd: + <<: *aci_bd_absent + register: bd_absent + +- name: delete bd again - idempotency works + cisco.aci.aci_bd: + <<: *aci_bd_absent + register: bd_absent_idempotent + +- name: delete bd - cleanup + cisco.aci.aci_bd: + <<: *aci_bd_absent + name: anstest2 + +- name: delete bd missing param - fails properly + cisco.aci.aci_bd: + <<: *aci_bd_absent + bd: "{{ fakevar | default(omit) }}" + ignore_errors: yes + register: bd_absent_missing_param + +- name: asserts for deletion task + assert: + that: + - bd_absent_check_mode is changed + - bd_absent_check_mode.proposed == {} + - bd_absent is changed + - bd_absent.previous != [] + - bd_absent_idempotent is not changed + - bd_absent_idempotent.previous == [] + - bd_absent_missing_param is failed + - 'bd_absent_missing_param.msg == "state is absent but all of the following are missing: bd"' + +- name: delete vrf - cleanup before ending tests + cisco.aci.aci_vrf: + <<: *aci_vrf_present + state: absent + when: vrf_present is changed + +- name: delete tenant - cleanup before ending tests + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent + when: tenant_present is changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd_dhcp_label/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd_dhcp_label/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd_dhcp_label/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd_dhcp_label/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd_dhcp_label/tasks/main.yml new file mode 100644 index 00000000..8e753180 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd_dhcp_label/tasks/main.yml @@ -0,0 +1,147 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Shreyas Srish (@shrsr) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Add a new tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + +- name: create bd - creation works + aci_bd: + <<: *aci_info + tenant: ansible_tenant + bd: database + register: bd_present + +#CREATE LABELS +- name: Create a new DHCP Relay Label to a Bridge Domain - check mode + aci_bd_dhcp_label: + <<: *aci_info + tenant: ansible_tenant + bd: database + dhcp_label: label1 + owner: infra + state: present + check_mode: yes + register: cm_dhcp_label1 + +- name: Verify creation of label1 in check mode + assert: + that: + - cm_dhcp_label1 is changed + - cm_dhcp_label1.sent.dhcpLbl.attributes.name == 'label1' + +- name: Create a new DHCP Relay Label to a Bridge Domain - normal mode + aci_bd_dhcp_label: + <<: *aci_info + tenant: ansible_tenant + bd: database + dhcp_label: label1 + owner: infra + state: present + register: nm_dhcp_label1 + +- name: Verify creation of label1 + assert: + that: + - nm_dhcp_label1 is changed + - nm_dhcp_label1.current.0.dhcpLbl.attributes.dn == 'uni/tn-ansible_tenant/BD-database/dhcplbl-label1' + - nm_dhcp_label1.current.0.dhcpLbl.attributes.name == 'label1' + +- name: Create another DHCP Relay Label to a Bridge Domain - normal mode + aci_bd_dhcp_label: + <<: *aci_info + tenant: ansible_tenant + bd: database + dhcp_label: label2 + owner: infra + state: present + register: nm_dhcp_label2 + +- name: Verify creation of label2 + assert: + that: + - nm_dhcp_label2 is changed + - nm_dhcp_label2.current.0.dhcpLbl.attributes.dn == 'uni/tn-ansible_tenant/BD-database/dhcplbl-label2' + - nm_dhcp_label2.current.0.dhcpLbl.attributes.name == 'label2' + +#QUERY LABELS +- name: Query a DHCP Relay Label of a Bridge Domain + aci_bd_dhcp_label: + <<: *aci_info + tenant: ansible_tenant + bd: database + dhcp_label: label1 + owner: infra + state: query + register: query_dhcp_label1 + +- name: Verify query of label + assert: + that: + - query_dhcp_label1 is not changed + - query_dhcp_label1.current.0.dhcpLbl.attributes.dn == 'uni/tn-ansible_tenant/BD-database/dhcplbl-label1' + +- name: Query all DHCP Relay Labels of a Bridge Domain + aci_bd_dhcp_label: + <<: *aci_info + tenant: ansible_tenant + bd: database + state: query + register: query_all_labels + +- name: Verify query all of labels + assert: + that: + - query_all_labels is not changed + +#REMOVE LABELS +- name: Remove a DHCP Relay Label for a Bridge Domain + aci_bd_dhcp_label: + <<: *aci_info + tenant: ansible_tenant + bd: database + dhcp_label: label1 + owner: infra + state: absent + register: delete_dhcp_label + +- name: Verify deletion of label + assert: + that: + - delete_dhcp_label is changed + - delete_dhcp_label.current == [] + +# CLEAN ENVIRONMENT AGAIN +- name: Remove the ansible_tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + state: absent
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd_subnet/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd_subnet/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd_subnet/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd_subnet/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd_subnet/tasks/main.yml new file mode 100644 index 00000000..a1545393 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_bd_subnet/tasks/main.yml @@ -0,0 +1,240 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: present + tenant: anstest + register: tenant_present + +- name: ensure bd exists for tests to kick off + cisco.aci.aci_bd: &aci_bd_present + <<: *aci_tenant_present + bd: anstest + register: bd_present + +- name: ensure subnet does not exist for tests to kick off + cisco.aci.aci_bd_subnet: &aci_subnet_absent + <<: *aci_bd_present + state: absent + gateway: 10.100.100.1 + mask: 24 + +- name: ensure subnet does not exist for tests to kick off + cisco.aci.aci_bd_subnet: &aci_subnet2_absent + <<: *aci_subnet_absent + gateway: 10.100.101.1 + mask: 25 + +- name: create subnet - check mode works + cisco.aci.aci_bd_subnet: &aci_subnet_present + <<: *aci_subnet_absent + state: present + subnet_name: anstest + descr: Ansible Test + check_mode: yes + register: create_check_mode + +- name: create subnet - creation works + cisco.aci.aci_bd_subnet: + <<: *aci_subnet_present + register: create_subnet + +- name: create new subnet - creation works + cisco.aci.aci_bd_subnet: &aci_subnet2_present + <<: *aci_subnet2_absent + state: present + descr: Ansible Test + scope: [private, shared] + route_profile: default + route_profile_l3_out: default + register: create_subnet2 + +- name: create subnet again - idempotency works + cisco.aci.aci_bd_subnet: + <<: *aci_subnet2_present + register: create_idempotency + +- name: modify subnet - update works + cisco.aci.aci_bd_subnet: + <<: *aci_subnet_present + scope: [shared, public] + subnet_control: querier_ip + register: modify_subnet + +- name: create subnet with bad scope - failure message works + cisco.aci.aci_bd_subnet: + <<: *aci_subnet_present + scope: [private, public] + register: create_bad_scope + ignore_errors: yes + +- name: create subnet without all necessary params - failure message works + cisco.aci.aci_bd_subnet: + <<: *aci_subnet_present + bd: "{{ fake_var | default(omit) }}" + register: create_incomplete_data + ignore_errors: yes + +- name: asserts for subnet creation tasks + assert: + that: + - create_check_mode is changed + - create_check_mode.sent.fvSubnet.attributes.descr == create_subnet.sent.fvSubnet.attributes.descr == 'Ansible Test' + - create_check_mode.sent.fvSubnet.attributes.ip == create_subnet.sent.fvSubnet.attributes.ip == '10.100.100.1/24' + - create_check_mode.sent.fvSubnet.attributes.name == create_subnet.sent.fvSubnet.attributes.name == 'anstest' + - create_subnet is changed + - create_subnet.previous == [] + - create_subnet2 is changed + - create_subnet2.sent == create_subnet2.proposed + - create_subnet2.sent.fvSubnet.attributes.scope == "private,shared" + - create_subnet2.sent.fvSubnet.children.0.fvRsBDSubnetToProfile.attributes.tnL3extOutName == 'default' + - create_subnet2.sent.fvSubnet.children.0.fvRsBDSubnetToProfile.attributes.tnRtctrlProfileName == 'default' + - create_idempotency is not changed + - create_idempotency.previous != [] + - modify_subnet is changed + - modify_subnet.previous != [] + - modify_subnet.sent != modify_subnet.proposed + - modify_subnet.sent.fvSubnet.attributes.ctrl == 'querier' + - modify_subnet.sent.fvSubnet.attributes.scope == 'public,shared' + - create_bad_scope is failed + - create_bad_scope.msg.startswith("Parameter 'scope' cannot be both 'private' and 'public'") + - create_incomplete_data is failed + - 'create_incomplete_data.msg == "state is present but all of the following are missing: bd"' + +- name: get all subnets + cisco.aci.aci_bd_subnet: &aci_query + <<: *aci_tenant_present + state: query + tenant: "{{ fake_var | default(omit) }}" + register: get_all + +- name: get all in tenant + cisco.aci.aci_bd_subnet: + <<: *aci_query + tenant: anstest + register: get_all_tenant + +- name: get all in bd + cisco.aci.aci_bd_subnet: + <<: *aci_query + bd: anstest + register: get_all_bd + +- name: get all tenant and bd + cisco.aci.aci_bd_subnet: + <<: *aci_bd_present + state: query + register: get_all_tenant_bd + +- name: get subnet in tenant + cisco.aci.aci_bd_subnet: + <<: *aci_subnet_present + state: query + bd: "{{ fake_var | default(omit) }}" + register: get_subnet_tenant + +- name: get subnet in bd + cisco.aci.aci_bd_subnet: + <<: *aci_subnet_present + state: query + tenant: "{{ fake_var | default(omit) }}" + register: get_subnet_bd + +- name: get specific subnet + cisco.aci.aci_bd_subnet: + <<: *aci_subnet_present + state: query + register: get_subnet + +- name: get all subnets matching gateway + cisco.aci.aci_bd_subnet: + <<: *aci_subnet_present + state: query + tenant: "{{ fake_var | default(omit) }}" + bd: "{{ fake_var | default(omit) }}" + register: get_subnets_gateway + +- name: asserts for query tasks + assert: + that: + - get_all is not changed + - get_all.current | length > 1 + - get_all_tenant is not changed + - '"tn-anstest.json" in get_all_tenant.url' + - get_all_bd is not changed + - '"query-target-filter=eq(fvBD.name,\"anstest\")" in get_all_bd.filter_string' + - '"class/fvBD.json" in get_all_bd.url' + - get_all_tenant_bd is not changed + - '"tn-anstest/BD-anstest.json" in get_all_tenant_bd.url' + - get_all_tenant_bd.current.0.fvBD.children | length > 1 + - get_subnet_tenant is not changed + - '"rsp-subtree-filter=eq(fvSubnet.ip,\"10.100.100.1/24\")" in get_subnet_tenant.filter_string' + - '"tn-anstest.json" in get_subnet_tenant.url' + - get_subnet_bd is not changed + - '"query-target-filter=eq(fvBD.name,\"anstest\")"' + - '"rsp-subtree-filter=eq(fvSubnet.ip,\"10.100.100.1/24\")" in get_subnet_bd.filter_string' + - '"class/fvBD.json" in get_subnet_bd.url' + - get_subnet is not changed + - get_subnet.current | length == 1 + - '"tn-anstest/BD-anstest/subnet-[10.100.100.1/24].json" in get_subnet.url' + - get_subnets_gateway is not changed + - '"query-target-filter=eq(fvSubnet.ip,\"10.100.100.1/24\")" in get_subnets_gateway.filter_string' + - '"class/fvSubnet.json" in get_subnets_gateway.url' + +- name: delete subnet - check mode works + cisco.aci.aci_bd_subnet: + <<: *aci_subnet_absent + check_mode: yes + register: delete_check_mode + +- name: delete subnet - delete works + cisco.aci.aci_bd_subnet: + <<: *aci_subnet_absent + register: delete_subnet + +- name: delete subnet - cleanup + cisco.aci.aci_bd_subnet: + <<: *aci_subnet2_absent + +- name: delete subnet again - idempotency works + cisco.aci.aci_bd_subnet: + <<: *aci_subnet2_absent + register: delete_idempotency + +- name: asserts for deletion task + assert: + that: + - delete_check_mode is changed + - delete_check_mode.proposed == {} + - delete_subnet is changed + - delete_subnet.previous != [] + - delete_subnet.method == "DELETE" + - delete_idempotency is not changed + - delete_idempotency.previous == [] + +- name: delete bd - cleanup before ending tests + cisco.aci.aci_bd: + <<: *aci_bd_present + state: absent + when: bd_present is changed + +- name: delete tenant - cleanup before ending tests + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent + when: tenant_present is changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_cidr/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_cidr/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_cidr/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_cidr/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_cidr/tasks/main.yml new file mode 100644 index 00000000..7f287d17 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_cidr/tasks/main.yml @@ -0,0 +1,303 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Cindy Zhao (@cizhao) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +# CLEAN ENVIRONMENT +- name: Set vars + set_fact: + aci_info: &aci_info + host: '{{ aws_aci_hostname }}' + username: '{{ aws_aci_username }}' + password: '{{ aws_aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +- name: Ensure tenant doesn't exist + aci_tenant: + <<: *aci_info + state: absent + tenant: anstest + register: tenant_absent + +- name: Ensure tenant exists for tests to kick off + aci_tenant: + <<: *aci_info + state: present + tenant: anstest + register: tenant_present + +- name: Ensure aci cloud context profile does not exists + aci_cloud_ctx_profile: + <<: *aci_info + tenant: anstest + name: ctx_profile_1 + state: absent + register: rm_ctx_profile_1 + +- name: Create aci cloud context profile + aci_cloud_ctx_profile: + <<: *aci_info + tenant: anstest + name: ctx_profile_1 + vrf: ctx_profile_vrf_1 + region: us-east-1 + primary_cidr: '10.10.0.0/16' + cloud: aws + state: present + register: nm_add_aci_ctx_profile + +- name: Create non_primary CIDR (check_mode) + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + address: 10.0.0.0/16 + cloud_context_profile: ctx_profile_1 + state: present + check_mode: yes + register: cm_non_primary_cidr + +- name: Create non_primary CIDR (normal_mode) + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + address: 10.0.0.0/16 + cloud_context_profile: ctx_profile_1 + state: present + register: nm_non_primary_cidr + +- name: Verify cm_non_primary_cidr and nm_non_primary_cidr + assert: + that: + - cm_non_primary_cidr is changed + - nm_non_primary_cidr is changed + - cm_non_primary_cidr.previous == [] + - nm_non_primary_cidr.previous == [] + - cm_non_primary_cidr.proposed.cloudCidr.attributes.addr == "10.0.0.0/16" + - cm_non_primary_cidr.proposed.cloudCidr.attributes.dn == "uni/tn-anstest/ctxprofile-ctx_profile_1/cidr-[10.0.0.0/16]" + - cm_non_primary_cidr.proposed.cloudCidr.attributes.primary == "no" + - nm_non_primary_cidr.current.0.cloudCidr.attributes.addr == "10.0.0.0/16" + - nm_non_primary_cidr.current.0.cloudCidr.attributes.dn == "uni/tn-anstest/ctxprofile-ctx_profile_1/cidr-[10.0.0.0/16]" + - nm_non_primary_cidr.current.0.cloudCidr.attributes.primary == "no" + +- name: Create non_primary CIDR again + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + address: 10.0.0.0/16 + cloud_context_profile: ctx_profile_1 + state: present + register: nm_non_primary_cidr_again + +- name: Verify nm_non_primary_cidr_again + assert: + that: + - nm_non_primary_cidr_again is not changed + - nm_non_primary_cidr_again.previous.0.cloudCidr.attributes.addr == "10.0.0.0/16" + - nm_non_primary_cidr_again.previous.0.cloudCidr.attributes.dn == "uni/tn-anstest/ctxprofile-ctx_profile_1/cidr-[10.0.0.0/16]" + - nm_non_primary_cidr_again.previous.0.cloudCidr.attributes.primary == "no" + - nm_non_primary_cidr_again.current.0.cloudCidr.attributes.addr == "10.0.0.0/16" + - nm_non_primary_cidr_again.current.0.cloudCidr.attributes.dn == "uni/tn-anstest/ctxprofile-ctx_profile_1/cidr-[10.0.0.0/16]" + - nm_non_primary_cidr_again.current.0.cloudCidr.attributes.primary == "no" + +- name: Change primary CIDR to non_primary (check_mode) + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + address: 10.10.0.0/16 + cloud_context_profile: ctx_profile_1 + state: present + check_mode: yes + register: cm_change_to_non_primary_cidr + +- name: Change primary CIDR to non_primary (normal_mode) + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + address: 10.10.0.0/16 + cloud_context_profile: ctx_profile_1 + state: present + ignore_errors: yes + register: nm_change_to_non_primary_cidr + +- name: Verify cm_primary_cidr and nm_change_to_non_primary_cidr + assert: + that: + - nm_change_to_non_primary_cidr.msg == "APIC Error 1{{':'}} Invalid Configuration {{':'}} Exactly one Primary CIDR expected for{{':'}} uni/tn-anstest/ctxprofile-ctx_profile_1, but found{{':'}} 0" + - cm_change_to_non_primary_cidr.proposed.cloudCidr.attributes.primary == "no" + - cm_change_to_non_primary_cidr.previous.0.cloudCidr.attributes.primary == cm_change_to_non_primary_cidr.current.0.cloudCidr.attributes.primary == "yes" + +- name: Remove primary CIDR (check_mode) + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + address: 10.10.0.0/16 + cloud_context_profile: ctx_profile_1 + state: absent + check_mode: yes + register: cm_remove_primary_cidr + +- name: Remove primary CIDR (normal_mode) + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + address: 10.10.0.0/16 + cloud_context_profile: ctx_profile_1 + state: absent + ignore_errors: yes + register: nm_remove_primary_cidr + +- name: Verify cm_remove_primary_cidr and nm_remove_primary_cidr + assert: + that: + - nm_remove_primary_cidr.msg == "APIC Error 1{{':'}} Invalid Configuration {{':'}} Exactly one Primary CIDR expected for{{':'}} uni/tn-anstest/ctxprofile-ctx_profile_1, but found{{':'}} 0" + - cm_remove_primary_cidr.proposed == {} + - cm_remove_primary_cidr.previous.0.cloudCidr.attributes.primary == cm_remove_primary_cidr.current.0.cloudCidr.attributes.primary == "yes" + +- name: Create second non_primary CIDR + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + address: 10.18.0.0/16 + cloud_context_profile: ctx_profile_1 + state: present + register: nm_add_second_non_primary_cidr + +- name: Verify nm_add_second_non_primary_cidr + assert: + that: + - nm_add_second_non_primary_cidr is changed + - nm_add_second_non_primary_cidr.previous == [] + - nm_add_second_non_primary_cidr.current.0.cloudCidr.attributes.addr == "10.18.0.0/16" + - nm_add_second_non_primary_cidr.current.0.cloudCidr.attributes.primary == "no" + - nm_add_second_non_primary_cidr.current.0.cloudCidr.attributes.dn == "uni/tn-anstest/ctxprofile-ctx_profile_1/cidr-[10.18.0.0/16]" + +- name: Change setting of second non_primary CIDR + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + cidr: 10.18.0.0/16 + cloud_context_profile: ctx_profile_1 + description: This is not a primary CIDR + name_alias: cidr_block_range + state: present + register: change_non_primary_cidr + +- name: Verify change_non_primary_cidr + assert: + that: + - change_non_primary_cidr is changed + - change_non_primary_cidr.current.0.cloudCidr.attributes.addr == "10.18.0.0/16" + - change_non_primary_cidr.current.0.cloudCidr.attributes.descr == "This is not a primary CIDR" + - change_non_primary_cidr.current.0.cloudCidr.attributes.nameAlias == "cidr_block_range" + - change_non_primary_cidr.current.0.cloudCidr.attributes.dn == "uni/tn-anstest/ctxprofile-ctx_profile_1/cidr-[10.18.0.0/16]" + +- name: Query all CIDRs + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + cloud_context_profile: ctx_profile_1 + state: query + register: query_all + +- name: Verify query_all + assert: + that: + - query_all is not changed + - query_all.current.0.cloudCtxProfile.attributes.name == "ctx_profile_1" + - query_all.current.0.cloudCtxProfile.children | length == 3 + - query_all.current.0.cloudCtxProfile.children.0.cloudCidr.attributes.addr == "10.18.0.0/16" + - query_all.current.0.cloudCtxProfile.children.0.cloudCidr.attributes.primary == "no" + - query_all.current.0.cloudCtxProfile.children.1.cloudCidr.attributes.addr == "10.0.0.0/16" + - query_all.current.0.cloudCtxProfile.children.1.cloudCidr.attributes.primary == "no" + - query_all.current.0.cloudCtxProfile.children.2.cloudCidr.attributes.addr == "10.10.0.0/16" + - query_all.current.0.cloudCtxProfile.children.2.cloudCidr.attributes.primary == "yes" + +- name: Query primary CIDR + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + cloud_context_profile: ctx_profile_1 + address: 10.10.0.0/16 + state: query + register: query_primary + +- name: Verify query_primary + assert: + that: + - query_primary is not changed + - query_primary.current.0.cloudCidr.attributes.addr == "10.10.0.0/16" + - query_primary.current.0.cloudCidr.attributes.dn == "uni/tn-anstest/ctxprofile-ctx_profile_1/cidr-[10.10.0.0/16]" + - query_primary.current.0.cloudCidr.attributes.primary == "yes" + +- name: Query non_primary CIDR + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + cloud_context_profile: ctx_profile_1 + address: 10.0.0.0/16 + state: query + register: query_non_primary + +- name: Verify query_non_primary + assert: + that: + - query_non_primary is not changed + - query_non_primary.current.0.cloudCidr.attributes.addr == "10.0.0.0/16" + - query_non_primary.current.0.cloudCidr.attributes.dn == "uni/tn-anstest/ctxprofile-ctx_profile_1/cidr-[10.0.0.0/16]" + - query_non_primary.current.0.cloudCidr.attributes.primary == "no" + +- name: Query non_exsisting CIDR + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + cloud_context_profile: ctx_profile_1 + address: non_exsisting + state: query + ignore_errors: yes + register: query_non_existing + +- name: Verify query_non_existing + assert: + that: + - query_non_existing.msg == "APIC Error 104{{':'}} Invalid RN cidr-[non_exsisting]" + +- name: Remove non_primary CIDR(check_mode) + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + address: 10.0.0.0/16 + cloud_context_profile: ctx_profile_1 + state: absent + check_mode: yes + register: cm_remove_non_primary_cidr + +- name: Remove non_primary CIDR(normal_mode) + aci_cloud_cidr: + <<: *aci_info + tenant: anstest + address: 10.0.0.0/16 + cloud_context_profile: ctx_profile_1 + state: absent + register: nm_remove_non_primary_cidr + +- name: Verify cm_remove_non_primary_cidr and nm_remove_non_primary_cidr + assert: + that: + - cm_remove_non_primary_cidr is changed + - nm_remove_non_primary_cidr is changed + - cm_remove_non_primary_cidr.proposed == {} + - nm_remove_non_primary_cidr.current == [] + - cm_remove_non_primary_cidr.previous.0.cloudCidr.attributes.addr == "10.0.0.0/16" + - cm_remove_non_primary_cidr.previous.0.cloudCidr.attributes.primary == "no" + - cm_remove_non_primary_cidr.previous.0.cloudCidr.attributes.dn == "uni/tn-anstest/ctxprofile-ctx_profile_1/cidr-[10.0.0.0/16]" + - nm_remove_non_primary_cidr.previous.0.cloudCidr.attributes.addr == "10.0.0.0/16" + - nm_remove_non_primary_cidr.previous.0.cloudCidr.attributes.primary == "no" + - nm_remove_non_primary_cidr.previous.0.cloudCidr.attributes.dn == "uni/tn-anstest/ctxprofile-ctx_profile_1/cidr-[10.0.0.0/16]" diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_ctx_profile/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_ctx_profile/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_ctx_profile/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_ctx_profile/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_ctx_profile/tasks/main.yml new file mode 100644 index 00000000..a063087d --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_ctx_profile/tasks/main.yml @@ -0,0 +1,323 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Cindy Zhao (@cizhao) <cizhao@cisco.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set cloud + set_fact: + cloud: + - aci_hostname: "{{aws_aci_hostname}}" + aci_username: "{{aws_aci_username}}" + aci_password: "{{aws_aci_password}}" + cloud_type: aws + region: us-east-1 + region_2: us-west-1 + - aci_hostname: "{{azure_aci_hostname}}" + aci_username: "{{azure_aci_username}}" + aci_password: "{{azure_aci_password}}" + cloud_type: azure + region: westus + region_2: westus2 + +# CLEAN ENVIRONMENT +- name: Set vars + set_fact: + aci_info: &aci_info + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +- name: Ensure tenant doesn't exist + aci_tenant: + <<: *aci_info + state: absent + tenant: ansible_test + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + register: tenant_absent + loop: "{{cloud}}" + +- name: Ensure tenant exists for tests to kick off + aci_tenant: + <<: *aci_info + state: present + tenant: ansible_test + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + register: tenant_present + loop: "{{cloud}}" + +- name: Ensure aci cloud context profile does not exists + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + tenant: ansible_test + name: ctx_profile_1 + state: absent + register: rm_ctx_profile_1 + loop: "{{cloud}}" + +- name: Verify rm_ctx_profile_1 + assert: + that: + - item.current == [] + loop: "{{rm_ctx_profile_1.results}}" + +- name: Ensure aci cloud context profile 2 does not exists + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + tenant: ansible_test + name: ctx_profile_2 + state: absent + register: rm_ctx_profile_2 + loop: "{{cloud}}" + +- name: Verify rm_ctx_profile_2 + assert: + that: + - item.current == [] + loop: "{{rm_ctx_profile_2.results}}" + +- name: Create aci cloud context profile (check mode) + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + tenant: ansible_test + cloud: "{{item.cloud_type}}" + name: ctx_profile_1 + vrf: ctx_profile_vrf_1 + region: us-east-1 + primary_cidr: '10.100.0.0/16' + state: present + check_mode: yes + register: cm_add_aci_ctx_profile + loop: "{{cloud}}" + +- name: Create aci cloud context profile (normal mode) + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + tenant: ansible_test + cloud: "{{item.cloud_type}}" + name: ctx_profile_1 + vrf: ctx_profile_vrf_1 + region: "{{item.region}}" + primary_cidr: '10.100.0.0/16' + state: present + register: nm_add_aci_ctx_profile + loop: "{{cloud}}" + +- name: Verify cm_add_aci_ctx_profile + assert: + that: + - item is changed + - item.previous == [] + - item.proposed.cloudCtxProfile.attributes.name == "ctx_profile_1" + - item.proposed.cloudCtxProfile.attributes.dn == "uni/tn-ansible_test/ctxprofile-ctx_profile_1" + - item.proposed.cloudCtxProfile.children[0].cloudRsToCtx.attributes.tnFvCtxName == "ctx_profile_vrf_1" + - item.proposed.cloudCtxProfile.children[2].cloudCidr.attributes.addr == "10.100.0.0/16" + - item.proposed.cloudCtxProfile.children[2].cloudCidr.attributes.primary == "yes" + loop: "{{cm_add_aci_ctx_profile.results}}" + +- name: Verify nm_add_aci_ctx_profile + assert: + that: + - item is changed + - item.previous == [] + - item.current[0].cloudCtxProfile.attributes.name == "ctx_profile_1" + - item.current[0].cloudCtxProfile.attributes.dn == "uni/tn-ansible_test/ctxprofile-ctx_profile_1" + - item.current[0].cloudCtxProfile.children[2].cloudRsToCtx.attributes.tnFvCtxName == "ctx_profile_vrf_1" + - item.current[0].cloudCtxProfile.children[0].cloudCidr.attributes.addr == "10.100.0.0/16" + - item.current[0].cloudCtxProfile.children[0].cloudCidr.attributes.primary == "yes" + loop: "{{nm_add_aci_ctx_profile.results}}" + +- name: Add aci cloud context profile again (check_mode) + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + tenant: ansible_test + cloud: "{{item.cloud_type}}" + name: ctx_profile_1 + vrf: ctx_profile_vrf_1 + region: "{{item.region}}" + primary_cidr: '10.100.0.0/16' + state: present + check_mode: yes + register: cm_add_aci_ctx_profile_again + loop: "{{cloud}}" + +- name: Verify cm_add_aci_ctx_profile_again + assert: + that: + - item is not changed + - item.previous[0].cloudCtxProfile.attributes.name == "ctx_profile_1" + - item.previous[0].cloudCtxProfile.attributes.dn == "uni/tn-ansible_test/ctxprofile-ctx_profile_1" + - item.previous[0].cloudCtxProfile.children[2].cloudRsToCtx.attributes.tnFvCtxName == "ctx_profile_vrf_1" + - item.previous[0].cloudCtxProfile.children[0].cloudCidr.attributes.addr == "10.100.0.0/16" + - item.previous[0].cloudCtxProfile.children[0].cloudCidr.attributes.primary == "yes" + loop: "{{cm_add_aci_ctx_profile_again.results}}" + +- name: Add aci cloud context profile again (normal_mode) + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + tenant: ansible_test + cloud: "{{item.cloud_type}}" + name: ctx_profile_1 + vrf: ctx_profile_vrf_1 + region: "{{item.region}}" + primary_cidr: '10.100.0.0/16' + state: present + register: nm_add_aci_ctx_profile_again + loop: "{{cloud}}" + +- name: Verify nm_add_aci_ctx_profile_again + assert: + that: + - item is not changed + - item.current[0].cloudCtxProfile.attributes.name == "ctx_profile_1" + - item.current[0].cloudCtxProfile.attributes.dn == "uni/tn-ansible_test/ctxprofile-ctx_profile_1" + - item.current[0].cloudCtxProfile.children[2].cloudRsToCtx.attributes.tnFvCtxName == "ctx_profile_vrf_1" + - item.current[0].cloudCtxProfile.children[0].cloudCidr.attributes.addr == "10.100.0.0/16" + - item.current[0].cloudCtxProfile.children[0].cloudCidr.attributes.primary == "yes" + loop: "{{nm_add_aci_ctx_profile_again.results}}" + +- name: Add another aci cloud context profile (check_mode) + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + tenant: ansible_test + cloud: "{{item.cloud_type}}" + name: ctx_profile_2 + vrf: ctx_profile_vrf_2 + region: "{{item.region_2}}" + primary_cidr: '10.101.0.0/16' + description: "add ctx_profile_2" + state: present + check_mode: yes + register: cm_add_another_aci_ctx_profile + loop: "{{cloud}}" + +- name: Verify cm_add_another_aci_ctx_profile + assert: + that: + - item is changed + - item.previous == [] + - item.proposed.cloudCtxProfile.attributes.name == "ctx_profile_2" + - item.proposed.cloudCtxProfile.attributes.dn == "uni/tn-ansible_test/ctxprofile-ctx_profile_2" + - item.proposed.cloudCtxProfile.children[0].cloudRsToCtx.attributes.tnFvCtxName == "ctx_profile_vrf_2" + - item.proposed.cloudCtxProfile.children[2].cloudCidr.attributes.addr == "10.101.0.0/16" + - item.proposed.cloudCtxProfile.children[2].cloudCidr.attributes.primary == "yes" + - item.proposed.cloudCtxProfile.attributes.descr == "add ctx_profile_2" + loop: "{{cm_add_another_aci_ctx_profile.results}}" + +- name: Add another aci cloud context profile (normal_mode) + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + tenant: ansible_test + cloud: "{{item.cloud_type}}" + name: ctx_profile_2 + vrf: ctx_profile_vrf_2 + region: "{{item.region_2}}" + primary_cidr: '10.101.0.0/16' + description: "add ctx_profile_2" + state: present + register: nm_add_another_aci_ctx_profile + loop: "{{cloud}}" + +- name: Verify nm_add_another_aci_ctx_profile + assert: + that: + - item is changed + - item.previous == [] + - item.current[0].cloudCtxProfile.attributes.name == "ctx_profile_2" + - item.current[0].cloudCtxProfile.attributes.dn == "uni/tn-ansible_test/ctxprofile-ctx_profile_2" + - item.current[0].cloudCtxProfile.children[2].cloudRsToCtx.attributes.tnFvCtxName == "ctx_profile_vrf_2" + - item.current[0].cloudCtxProfile.children[0].cloudCidr.attributes.addr == "10.101.0.0/16" + - item.current[0].cloudCtxProfile.children[0].cloudCidr.attributes.primary == "yes" + - item.current[0].cloudCtxProfile.attributes.descr == "add ctx_profile_2" + loop: "{{nm_add_another_aci_ctx_profile.results}}" + +- name: Query aci cloud context profile ctx_profile_1 + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + tenant: ansible_test + name: ctx_profile_1 + state: query + register: query_aci_cloud_profile_1 + loop: "{{cloud}}" + +- name: Verify query_aci_cloud_profile_1 + assert: + that: + - item is not changed + - item.current[0].cloudCtxProfile.attributes.name == "ctx_profile_1" + - item.current[0].cloudCtxProfile.children | length == 3 + loop: "{{query_aci_cloud_profile_1.results}}" + +- name: Query all aci cloud context profiles + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + tenant: ansible_test + state: query + register: query_all + loop: "{{cloud}}" + +- name: Verify query_all + assert: + that: + - item is not changed + - item.current | length == 1 + - item.current.0.fvTenant.children | length == 2 + loop: "{{query_all.results}}" + +- name: Remove aci cloud context profile + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + tenant: ansible_test + name: ctx_profile_1 + state: absent + register: rm_ctx_profile + loop: "{{cloud}}" + +- name: Verify rm_ctx_profile + assert: + that: + - item.current == [] + - item.previous.0.cloudCtxProfile.attributes.name == "ctx_profile_1" + loop: "{{rm_ctx_profile.results}}"
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_provider/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_provider/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_provider/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_provider/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_provider/tasks/main.yml new file mode 100644 index 00000000..a69b819f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_provider/tasks/main.yml @@ -0,0 +1,51 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Lionel Hercot (@lhercot) <lhercot@cisco.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +# CLEAN ENVIRONMENT +- name: Set vars + set_fact: + aci_info: &aci_info + host: '{{ aws_aci_hostname }}' + username: '{{ aws_aci_username }}' + password: '{{ aws_aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +- name: Query system information + aci_system: + <<: *aci_info + id: 1 + state: query + register: version + +# QUERY OBJECTS +- name: Query cloud provider object + aci_cloud_provider: + <<: *aci_info + state: query + register: query_cloud + when: version.current.0.topSystem.attributes.version is version('4.1', '>=') + +- name: Verify query_cloud for all sites + assert: + that: + - query_cloud is not changed + +- name: Verify query_cloud for Cloud Sites + assert: + that: + - query_cloud is not changed + - query_cloud.current.0.cloudProvP.attributes.environment == "public-cloud" + - '"vendor" in query_cloud.current.0.cloudProvP.attributes' + when: + - version.current.0.topSystem.attributes.version is version('4.1', '>=') + - query_cloud.current | length > 0
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_region/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_region/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_region/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_region/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_region/tasks/main.yml new file mode 100644 index 00000000..6315f8c1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_region/tasks/main.yml @@ -0,0 +1,66 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Cindy Zhao (@cizhao) <cizhao@cisco.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +# CLEAN ENVIRONMENT +- name: Set vars + set_fact: + aci_info: &aci_info + host: '{{ aws_aci_hostname }}' + username: '{{ aws_aci_username }}' + password: '{{ aws_aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +- name: Query all regions + aci_cloud_region: + <<: *aci_info + cloud: 'aws' + state: query + register: query_all + +- name: Verify query_all + assert: + that: + - query_all is not changed + - query_all.current.0.cloudProvP.attributes.dn == "uni/clouddomp/provp-aws" + - query_all.current.0.cloudProvP.attributes.vendor == "aws" + - query_all.current.0.cloudProvP.children | length > 10 + +- name: Query a specific region + aci_cloud_region: + <<: *aci_info + cloud: 'aws' + region: eu-west-2 + state: query + register: query_region + +- name: Verify query_region + assert: + that: + - query_region is not changed + - query_region.current.0.cloudRegion.attributes.adminSt == "unmanaged" + - query_region.current.0.cloudRegion.attributes.dn == "uni/clouddomp/provp-aws/region-eu-west-2" + - query_region.current.0.cloudRegion.attributes.name == "eu-west-2" + +- name: Query non_existing region + aci_cloud_region: + <<: *aci_info + cloud: 'aws' + region: non_existing + state: query + register: query_non_existing_region + +- name: Verify query_non_existing_region + assert: + that: + - query_non_existing_region is not changed + - query_non_existing_region.current == []
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_subnet/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_subnet/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_subnet/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_subnet/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_subnet/tasks/main.yml new file mode 100644 index 00000000..b6b53bd9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_subnet/tasks/main.yml @@ -0,0 +1,347 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Cindy Zhao (@cizhao) <cizhao@cisco.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set cloud + set_fact: + cloud: + - aci_hostname: "{{ aws_aci_hostname }}" + aci_username: "{{ aws_aci_username }}" + aci_password: "{{ aws_aci_password }}" + cloud_type: aws + cloud_region: us-west-1 + cloud_zone: us-west-1a + - aci_hostname: "{{ azure_aci_hostname }}" + aci_username: "{{ azure_aci_username }}" + aci_password: "{{ azure_aci_password }}" + cloud_type: azure + cloud_region: westus + vnet_gateway: true + +# CLEAN ENVIRONMENT +- name: Set vars + set_fact: + aci_info: &aci_info + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +- name: Ensure tenant doesn't exist + aci_tenant: + <<: *aci_info + state: absent + tenant: ansible_test + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + register: tenant_absent + loop: "{{ cloud }}" + +- name: Ensure tenant exists for tests to kick off + aci_tenant: + <<: *aci_info + state: present + tenant: ansible_test + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + register: tenant_present + loop: "{{ cloud }}" + +- name: Ensure aci cloud context profile does not exists + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + tenant: ansible_test + name: ctx_profile_1 + state: absent + register: rm_ctx_profile_1 + loop: "{{ cloud }}" + +- name: Verify rm_ctx_profile_1 + assert: + that: + - item.current == [] + loop: "{{ rm_ctx_profile_1.results }}" + +- name: Create aci cloud context profile (normal mode) + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud: "{{ item.cloud_type }}" + name: ctx_profile_1 + vrf: ctx_profile_vrf_1 + region: "{{ item.cloud_region }}" + primary_cidr: '10.50.0.0/16' + state: present + register: nm_add_aci_ctx_profile + loop: "{{ cloud }}" + +- name: Create aci cloud subnet (check_mode) + aci_cloud_subnet: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + cidr: '10.50.0.0/16' + availability_zone: "{{ item.cloud_zone | default(omit) }}" + address: 10.50.0.1 + description: test description + check_mode: yes + register: cm_add_subnet + loop: "{{ cloud }}" + +- name: Create aci cloud subnet (normal_mode) + aci_cloud_subnet: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + cidr: '10.50.0.0/16' + availability_zone: "{{ item.cloud_zone | default(omit) }}" + address: 10.50.0.1 + description: test description + register: nm_add_subnet + loop: "{{ cloud }}" + +- name: Verify cm_add_subnet + assert: + that: + - item is changed + - item.previous == [] + - item.proposed.cloudSubnet.attributes.ip == "10.50.0.1" + - item.proposed.cloudSubnet.attributes.descr == "test description" + - item.proposed.cloudSubnet.attributes.dn == "uni/tn-ansible_test/ctxprofile-ctx_profile_1/cidr-[10.50.0.0/16]/subnet-[10.50.0.1]" + loop: "{{ cm_add_subnet.results }}" + +- name: Create aci cloud subnet again (normal_mode) + aci_cloud_subnet: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + cidr: '10.50.0.0/16' + availability_zone: "{{ item.cloud_zone | default(omit) }}" + address: 10.50.0.1 + description: test description + register: nm_add_subnet_again + loop: "{{ cloud }}" + +- name: Verify nm_add_subnet_again + assert: + that: + - item is not changed + loop: "{{ nm_add_subnet_again.results }}" + +- name: Create another cloud subnet (normal_mode) + aci_cloud_subnet: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + cidr: '10.50.0.0/16' + availability_zone: "{{ item.cloud_zone | default(omit) }}" + address: 10.50.0.2 + description: another subnet + register: nm_add_another_subnet + loop: "{{ cloud }}" + +- name: Verify nm_add_another_subnet + assert: + that: + - item is changed + - item.previous == [] + - item.current.0.cloudSubnet.attributes.descr == "another subnet" + - item.current.0.cloudSubnet.attributes.dn == "uni/tn-ansible_test/ctxprofile-ctx_profile_1/cidr-[10.50.0.0/16]/subnet-[10.50.0.2]" + - item.current.0.cloudSubnet.attributes.ip == "10.50.0.2" + - item.current.0.cloudSubnet.attributes.scope == "private" + - item.current.0.cloudSubnet.attributes.usage == "user" + loop: "{{ nm_add_another_subnet.results }}" + +- name: Create cloud subnet 3(normal_mode) + aci_cloud_subnet: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + cidr: '10.50.0.0/16' + availability_zone: "{{ item.cloud_zone | default(omit) }}" + address: 10.50.0.3 + name: subnet_3 + register: nm_add_subnet_3 + loop: "{{ cloud }}" + +- name: Specify subnet as VpnGateway enabled + aci_cloud_subnet: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + cidr: '10.50.0.0/16' + availability_zone: "{{ item.cloud_zone | default(omit) }}" + address: 10.50.0.1 + # name: subnet_1 + description: change subnet 1 + vnet_gateway: "{{ item.vnet_gateway | default(omit)}}" + #scope: public + register: nm_change_subnet_1 + loop: "{{ cloud }}" + +# Enable vpn_gateway router in cloud ctx profile +- name: Enable VpnGateway + aci_cloud_vpn_gateway: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + state: present + loop: "{{ cloud }}" + +# Try to disable vpn_gateway router again in cloud ctx profile +- name: Disable VpnGateway + aci_cloud_vpn_gateway: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + state: absent + loop: "{{ cloud }}" + +- name: Query all subnets + aci_cloud_subnet: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + cidr: '10.50.0.0/16' + state: query + register: query_all + loop: "{{ cloud }}" + +- name: Verify query_all + assert: + that: + - item is not changed + - item.current.0.cloudCidr.attributes.addr == "10.50.0.0/16" + - item.current.0.cloudCidr.children | length == 3 + loop: "{{ query_all.results }}" + +- name: Query a specific subnet + aci_cloud_subnet: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + cidr: '10.50.0.0/16' + address: 10.50.0.1 + state: query + register: query_subnet_1 + loop: "{{ cloud }}" + +- name: Verify query_subnet_1 + assert: + that: + - item is not changed + - item.current.0.cloudSubnet.attributes.ip == "10.50.0.1" + - item.current.0.cloudSubnet.attributes.scope == "private" + - item.current.0.cloudSubnet.attributes.dn == "uni/tn-ansible_test/ctxprofile-ctx_profile_1/cidr-[10.50.0.0/16]/subnet-[10.50.0.1]" + loop: "{{ query_subnet_1.results }}" + +- name: Remove subnet 3 (check_mode) + aci_cloud_subnet: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + cidr: '10.50.0.0/16' + address: 10.50.0.3 + state: absent + check_mode: yes + register: cm_remove_subnet_3 + loop: "{{ cloud }}" + +- name: Verify cm_remove_subnet_3 + assert: + that: + - item is changed + - item.proposed == {} + - item.previous.0.cloudSubnet.attributes.ip == "10.50.0.3" + loop: "{{ cm_remove_subnet_3.results }}" + +- name: Remove subnet 3 (normal_mode) + aci_cloud_subnet: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + cidr: '10.50.0.0/16' + address: 10.50.0.3 + state: absent + register: nm_remove_subnet_3 + loop: "{{ cloud }}" + +- name: Verify nm_remove_subnet_3 + assert: + that: + - item is changed + - item.current == [] + - item.previous.0.cloudSubnet.attributes.ip == "10.50.0.3" + loop: "{{ nm_remove_subnet_3.results }}" + +- name: Remove subnet 3 again + aci_cloud_subnet: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + cidr: '10.50.0.0/16' + address: 10.50.0.3 + state: absent + register: nm_remove_subnet_3_again + loop: "{{ cloud }}" + +- name: Verify nm_remove_subnet_3_again + assert: + that: + - item is not changed + - item.previous == [] + - item.current == [] + loop: "{{ nm_remove_subnet_3_again.results }}"
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_vpn_gateway/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_vpn_gateway/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_vpn_gateway/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_vpn_gateway/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_vpn_gateway/tasks/main.yml new file mode 100644 index 00000000..521a683f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_vpn_gateway/tasks/main.yml @@ -0,0 +1,157 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Cindy Zhao (@cizhao) <cizhao@cisco.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set cloud + set_fact: + cloud: + - aci_hostname: "{{ aws_aci_hostname }}" + aci_username: "{{ aws_aci_username }}" + aci_password: "{{ aws_aci_password }}" + cloud_type: aws + cloud_region: us-west-1 + cloud_zone: us-west-1a + - aci_hostname: "{{ azure_aci_hostname }}" + aci_username: "{{ azure_aci_username }}" + aci_password: "{{ azure_aci_password }}" + cloud_type: azure + cloud_region: westus + vnet_gateway: true + +# CLEAN ENVIRONMENT +- name: Set vars + set_fact: + aci_info: &aci_info + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +- name: Ensure tenant doesn't exist + aci_tenant: + <<: *aci_info + state: absent + tenant: ansible_test + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + register: tenant_absent + loop: "{{ cloud }}" + +- name: Ensure tenant exists for tests to kick off + aci_tenant: + <<: *aci_info + state: present + tenant: ansible_test + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + register: tenant_present + loop: "{{ cloud }}" + +- name: Ensure aci cloud context profile does not exists + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{item.aci_hostname}}" + username: "{{item.aci_username}}" + password: "{{item.aci_password}}" + tenant: ansible_test + name: ctx_profile_1 + state: absent + register: rm_ctx_profile_1 + loop: "{{ cloud }}" + +- name: Verify rm_ctx_profile_1 + assert: + that: + - item.current == [] + loop: "{{ rm_ctx_profile_1.results }}" + +- name: Create aci cloud context profile (normal mode) + aci_cloud_ctx_profile: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud: "{{ item.cloud_type }}" + name: ctx_profile_1 + vrf: ctx_profile_vrf_1 + region: "{{ item.cloud_region }}" + primary_cidr: '10.50.0.0/16' + state: present + register: nm_add_aci_ctx_profile + loop: "{{ cloud }}" + +- name: Create aci cloud subnet with VpnGateway enabled (normal_mode) + aci_cloud_subnet: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + cidr: '10.50.0.0/16' + availability_zone: "{{ item.cloud_zone | default(omit) }}" + address: 10.50.0.1 + description: specify this subnet as VpnGateway router + vnet_gateway: "{{ item.vnet_gateway | default(omit)}}" + register: nm_add_subnet + loop: "{{ cloud }}" + +- name: Enable VpnGateway + aci_cloud_vpn_gateway: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + state: present + loop: "{{ cloud }}" + +- name: Disable VpnGateway + aci_cloud_vpn_gateway: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + state: absent + loop: "{{ cloud }}" + +- name: Enable VpnGateway again + aci_cloud_vpn_gateway: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + state: present + loop: "{{ cloud }}" + +- name: Query VpnGateway + aci_cloud_vpn_gateway: + <<: *aci_info + host: "{{ item.aci_hostname }}" + username: "{{ item.aci_username }}" + password: "{{ item.aci_password }}" + tenant: ansible_test + cloud_context_profile: ctx_profile_1 + register: query_vpn_gateway + loop: "{{ cloud }}" + +- name: Verify VpnGateway + assert: + that: + - item is not changed + - item.current.0.cloudRouterP.children | length == 3 + loop: "{{ query_vpn_gateway.results }}"
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_zone/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_zone/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_zone/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_zone/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_zone/tasks/main.yml new file mode 100644 index 00000000..1314d242 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_cloud_zone/tasks/main.yml @@ -0,0 +1,90 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Cindy Zhao (@cizhao) <cizhao@cisco.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +# CLEAN ENVIRONMENT +- name: Set vars + set_fact: + aci_info: &aci_info + host: '{{ aws_aci_hostname }}' + username: '{{ aws_aci_username }}' + password: '{{ aws_aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +- name: Query all zones under us-west-1 + aci_cloud_zone: + <<: *aci_info + cloud: 'aws' + region: us-west-1 + state: query + register: query_all + +- name: Verify query_all + assert: + that: + - query_all is not changed + - query_all.current.0.cloudRegion.attributes.name == "us-west-1" + - query_all.current.0.cloudRegion.children | length >= 2 + +- name: Query a specific zone under region us-west-1 + aci_cloud_zone: + <<: *aci_info + cloud: 'aws' + region: us-west-1 + zone: us-west-1a + state: query + register: query_zone_1 + +- name: Query another specific zone under region us-west-1 + aci_cloud_zone: + <<: *aci_info + cloud: 'aws' + region: us-west-1 + zone: us-west-1b + state: query + register: query_zone_2 + +- name: Verify query_zone_1 and query_zone_2 + assert: + that: + - query_zone_1 is not changed + - query_zone_2 is not changed + - query_zone_1.current.0.cloudZone.attributes.name == "us-west-1a" + - query_zone_1.current.0.cloudZone.attributes.dn == "uni/clouddomp/provp-aws/region-us-west-1/zone-us-west-1a" + - query_zone_2.current.0.cloudZone.attributes.name == "us-west-1b" + - query_zone_2.current.0.cloudZone.attributes.dn == "uni/clouddomp/provp-aws/region-us-west-1/zone-us-west-1b" + +- name: Query non_existing zone under region us-west-1 + aci_cloud_zone: + <<: *aci_info + cloud: 'aws' + region: us-west-1 + zone: non_existing + state: query + register: query_non_existing_zone + +- name: Query zone under non_existing region + aci_cloud_zone: + <<: *aci_info + cloud: 'aws' + region: non_existing + zone: us-west-1a + state: query + register: query_non_existing_region + +- name: Verify query_non_existing_zone + assert: + that: + - query_non_existing_zone is not changed + - query_non_existing_zone.current == [] + - query_non_existing_region is not changed + - query_non_existing_region.current == []
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_config_rollback/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_config_rollback/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_config_rollback/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_config_rollback/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_config_rollback/tasks/main.yml new file mode 100644 index 00000000..64c36d57 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_config_rollback/tasks/main.yml @@ -0,0 +1,108 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: ensure tenant does not exist for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_absent + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: absent + tenant: anstest + +- name: create a snapshot + cisco.aci.aci_config_snapshot: &create_snapshot + <<: *aci_tenant_absent + state: present + tenant: "{{ fakevar | default(omit) }}" + export_policy: anstest + +- name: create a tenant - use for rollback + cisco.aci.aci_tenant: &aci_tenant + <<: *create_snapshot + export_policy: "{{ fakevar | default(omit) }}" + tenant: anstest + register: tenant_present + +- name: create a new snapshot + cisco.aci.aci_config_snapshot: + <<: *create_snapshot + +- name: waiting for snapshot to be finished before querying + pause: + seconds: 10 + +- name: get snapshots + cisco.aci.aci_config_snapshot: + <<: *create_snapshot + state: query + register: snapshots + +- name: sort snapshot list + set_fact: + sorted_snapshots: '{{ snapshots.current.0.configSnapshotCont.children | sort(attribute="configSnapshot.attributes.createTime", reverse=True) }}' + +- name: compare snapshots + cisco.aci.aci_config_rollback: &preview_rollback + <<: *create_snapshot + state: preview + compare_export_policy: anstest + compare_snapshot: "{{ sorted_snapshots[0].configSnapshot.attributes.name }}" + snapshot: "{{ sorted_snapshots[1].configSnapshot.attributes.name }}" + register: rollback_preview + +- name: rollback to snapshot with missing parameters + cisco.aci.aci_config_rollback: &aci_rollback + <<: *create_snapshot + state: rollback + snapshot: "{{ sorted_snapshots[1].configSnapshot.attributes.name }}" + ignore_errors: yes + register: rollback_missing_param + +- name: rollback to snapshot + cisco.aci.aci_config_rollback: + <<: *aci_rollback + import_policy: anstest + import_type: replace + import_mode: atomic + register: rollback_rollback + +- name: pause execution to let rollback take effect + pause: + seconds: 15 + +- name: ensure tenant doesn't exist after rollback + cisco.aci.aci_tenant: + <<: *aci_tenant_absent + register: tenant_removed + +- debug: + msg: '{{ rollback_preview }}' + +- name: rollback assertions + assert: + that: + - rollback_preview is not changed + - rollback_preview.preview.polUni.children[0].fvTenant.attributes.name == 'anstest' + - rollback_preview.preview.polUni.children[0].fvTenant.attributes.status == 'created' + - '"snapshots.diff.xml" in rollback_preview.url' + - rollback_missing_param is failed + - 'rollback_missing_param.msg == "state is rollback but all of the following are missing: import_policy"' + - rollback_rollback is changed + - '"ce2_" in rollback_rollback.sent.configImportP.attributes.fileName' + - '".tar.gz" in rollback_rollback.sent.configImportP.attributes.fileName' + - '"ce2_" in rollback_rollback.proposed.configImportP.attributes.fileName' + - '".tar.gz" in rollback_rollback.proposed.configImportP.attributes.fileName' + - '"fabric/configimp-anstest.json" in rollback_rollback.url' + - tenant_removed is not changed + - tenant_removed.previous == [] diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_config_snapshot/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_config_snapshot/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_config_snapshot/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_config_snapshot/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_config_snapshot/tasks/main.yml new file mode 100644 index 00000000..e6170b32 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_config_snapshot/tasks/main.yml @@ -0,0 +1,144 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: create a snapshot - creation works + cisco.aci.aci_config_snapshot: &create_snapshot + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + export_policy: anstest + max_count: 10 + include_secure: no + format: json + description: ansible test + register: create + +- name: update snapshot to include secure and use xml - update works + cisco.aci.aci_config_snapshot: + <<: *create_snapshot + include_secure: yes + format: xml + register: create_update + +- name: create a snapshot invalid max_count - error message + cisco.aci.aci_config_snapshot: + <<: *create_snapshot + max_count: 11 + ignore_errors: yes + register: invalid_max_count + +- name: create a snapshot invalid max_count - error message + cisco.aci.aci_config_snapshot: + <<: *create_snapshot + export_policy: "{{ fake_var | default(omit) }}" + ignore_errors: yes + register: missing_param + +- name: present assertion tests + assert: + that: + - create is not failed + - create is changed + - create.sent.configExportP.attributes.adminSt == "triggered" + - create_update is not failed + - create_update is changed + - create_update.sent.configExportP.attributes.adminSt == 'triggered' + - create_update.sent.configExportP.attributes.format == 'xml' + - create_update.sent.configExportP.attributes.includeSecureFields == 'yes' + - invalid_max_count is failed + - invalid_max_count.msg == "Parameter 'max_count' must be a number between 1 and 10" + - missing_param is failed + - 'missing_param.msg == "state is present but all of the following are missing: export_policy"' + +- name: query with export_policy + cisco.aci.aci_config_snapshot: &query_snapshot + <<: *create_snapshot + state: query + register: query_export + +- name: generate snapshot name + set_fact: + test_snapshot: "{{ query_export.current.0.configSnapshotCont.children.0.configSnapshot.attributes.rn.strip('snapshot-') }}" + +- name: query with export_policy and snapshot + cisco.aci.aci_config_snapshot: &query_both + <<: *query_snapshot + snapshot: "{{ test_snapshot }}" + register: query_export_snapshot + +- name: query with snapshot - module add run- to snapshot + cisco.aci.aci_config_snapshot: + <<: *query_snapshot + export_policy: "{{ fake_var | default(omit) }}" + snapshot: "{{ test_snapshot.strip('run-') }}" + register: query_snapshot + +- name: query no params + cisco.aci.aci_config_snapshot: + <<: *query_snapshot + export_policy: "{{ fake_var | default(omit) }}" + register: query_all + +- name: query assertion tests + assert: + that: + - query_export is not failed + - query_export is not changed + - '"snapshots-[uni/fabric/configexp-anstest].json" in query_export.url' + - query_export.current.0.configSnapshotCont.attributes.name == "anstest" + - query_export.current.0.configSnapshotCont.children | length > 1 + - query_export_snapshot is not failed + - query_export_snapshot is not changed + - '"snapshots-[uni/fabric/configexp-anstest]/snapshot-{{ test_snapshot }}.json" in query_export_snapshot.url' + - query_export_snapshot.current | length == 1 + - query_snapshot is not failed + - query_snapshot is not changed + - '"class/configSnapshot.json" in query_snapshot.url' + - '"configSnapshot.name,\"{{ test_snapshot }}\"" in query_snapshot.filter_string' + - query_all is not failed + - query_all is not changed + - '"class/configSnapshot.json" in query_all.url' + - query_all.current | length > 1 + +- name: delete works + cisco.aci.aci_config_snapshot: &delete + <<: *query_both + state: absent + register: delete_snapshot + +- name: delete works - idempotency + cisco.aci.aci_config_snapshot: + <<: *delete + register: delete_idempotent + +- name: delete missing param + cisco.aci.aci_config_snapshot: + <<: *delete + snapshot: "{{ fake_var | default(omit) }}" + ignore_errors: yes + register: delete_missing_param + +- name: absent assertion tests + assert: + that: + - delete_snapshot is not failed + - delete_snapshot is changed + - delete_snapshot.sent.configSnapshot.attributes.retire == 'yes' + - delete_snapshot.previous != [] + - delete_snapshot.previous.0.configSnapshot.attributes.name == test_snapshot + - delete_idempotent is not failed + - delete_idempotent is not changed + - delete_idempotent.previous == [] + - delete_missing_param is failed + - 'delete_missing_param.msg == "state is absent but all of the following are missing: snapshot"' diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract/tasks/main.yml new file mode 100644 index 00000000..f31e2fbc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract/tasks/main.yml @@ -0,0 +1,164 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + tenant: anstest + state: present + register: tenant_present + +- name: create contract - check mode works + cisco.aci.aci_contract: &aci_contract_present + <<: *aci_tenant_present + contract: anstest + description: Ansible Test + check_mode: yes + register: present_check_mode + +- name: create contract - creation works + cisco.aci.aci_contract: + <<: *aci_contract_present + register: contract_present + +- name: create contract - idempotency works + cisco.aci.aci_contract: + <<: *aci_contract_present + register: present_idempotent + +- name: update contract - update works + cisco.aci.aci_contract: + <<: *aci_contract_present + scope: application-profile + register: present_update + +- name: create contract - used for query + cisco.aci.aci_contract: + <<: *aci_contract_present + contract: anstest2 + +- name: missing param - failure message works + cisco.aci.aci_contract: + <<: *aci_tenant_present + ignore_errors: yes + register: present_missing_param + +- name: present assertions + assert: + that: + - present_check_mode is changed + - present_check_mode.previous == [] + - present_check_mode.sent.vzBrCP.attributes.name == 'anstest' + - present_check_mode.sent.vzBrCP.attributes.descr == 'Ansible Test' + - contract_present is changed + - contract_present.sent == present_check_mode.sent + - present_idempotent is not changed + - present_update is changed + - present_update.sent != present_update.proposed + - present_update.sent.vzBrCP.attributes.scope == "application-profile" + - present_missing_param is failed + - 'present_missing_param.msg == "state is present but all of the following are missing: contract"' + +- name: query contract + cisco.aci.aci_contract: &aci_contract_query + <<: *aci_contract_present + state: query + register: query_contract + +- name: query all in tenant + cisco.aci.aci_contract: + <<: *aci_contract_query + contract: "{{ fakevar | default(omit) }}" + register: query_tenant + +- name: query all with name + cisco.aci.aci_contract: + <<: *aci_contract_query + tenant: "{{ fakevar | default(omit) }}" + register: query_name + +- name: query all + cisco.aci.aci_contract: + <<: *aci_contract_query + tenant: "{{ fakevar | default(omit) }}" + contract: "{{ fakevar | default(omit) }}" + register: query_all + +- name: query assertions + assert: + that: + - query_contract is not changed + - query_contract.current | length == 1 + - '"tn-anstest/brc-anstest.json" in query_contract.url' + - query_tenant is not changed + - query_tenant.current | length == 1 + - query_tenant.current.0.fvTenant.children | length > 1 + - '"rsp-subtree-class=vzBrCP" in query_tenant.filter_string' + - '"tn-anstest.json" in query_tenant.url' + - query_name is not changed + - query_name.current != [] + - '"query-target-filter=eq(vzBrCP.name,\"anstest\")" in query_name.filter_string' + - '"class/vzBrCP.json" in query_name.url' + - query_all is not changed + - query_all.current | length > 1 + - '"class/vzBrCP.json" in query_all.url' + +- name: delete contract - check mode works + cisco.aci.aci_contract: &aci_contract_absent + <<: *aci_contract_present + state: absent + check_mode: yes + register: absent_check_mode + +- name: delete contract - deletion works + cisco.aci.aci_contract: + <<: *aci_contract_absent + register: contract_absent + +- name: delete contract - idempotency works + cisco.aci.aci_contract: + <<: *aci_contract_absent + register: absent_idempotent + +- name: delete contract - cleanup second contract + cisco.aci.aci_contract: + <<: *aci_contract_absent + contract: anstest2 + +- name: missing param - fail message works + cisco.aci.aci_contract: + <<: *aci_contract_absent + tenant: "{{ fakevar | default(omit) }}" + ignore_errors: yes + register: absent_missing_param + +- name: absent assertions + assert: + that: + - absent_check_mode is changed + - absent_check_mode.previous != [] + - contract_absent is changed + - contract_absent.previous == absent_check_mode.previous + - absent_idempotent is not changed + - absent_idempotent.previous == [] + - absent_missing_param is failed + - 'absent_missing_param.msg == "state is absent but all of the following are missing: tenant"' + +- name: cleanup tenant + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent + when: tenant_present is changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract_subject/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract_subject/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract_subject/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract_subject/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract_subject/tasks/main.yml new file mode 100644 index 00000000..f60d7ee7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract_subject/tasks/main.yml @@ -0,0 +1,246 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: present + tenant: anstest + register: tenant_present + +- name: ensure contract exists for tests to kick off + cisco.aci.aci_contract: &aci_contract_present + <<: *aci_tenant_present + contract: anstest + register: contract_present + +- name: create subject - check mode works + cisco.aci.aci_contract_subject: &aci_subject_present + <<: *aci_contract_present + subject: anstest + description: Ansible Test + check_mode: yes + register: subject_present_check_mode + +- name: create subject - creation works + cisco.aci.aci_contract_subject: + <<: *aci_subject_present + register: subject_present + +- name: create subject - idempotency works + cisco.aci.aci_contract_subject: + <<: *aci_subject_present + register: subject_present_idempotent + +- name: update subject - update works + cisco.aci.aci_contract_subject: + <<: *aci_subject_present + description: Ansible Test + reverse_filter: "yes" + provider_match: at_most_one + priority: level2 + register: subject_update + +- name: create subject - try additional params + cisco.aci.aci_contract_subject: &aci_subject_present_2 + <<: *aci_contract_present + subject: anstest2 + reverse_filter: "no" + consumer_match: all + priority: level3 + register: subject_present_2 + +- name: missing param - failure message works + cisco.aci.aci_contract_subject: + <<: *aci_tenant_present + ignore_errors: yes + register: present_missing_param + +- name: present assertions + assert: + that: + - subject_present_check_mode is changed + - subject_present_check_mode.sent.vzSubj.attributes.descr == 'Ansible Test' + - subject_present_check_mode.sent.vzSubj.attributes.name == 'anstest' + - subject_present is changed + - subject_present.previous == [] + - subject_present.sent == subject_present_check_mode.sent + - subject_present_idempotent is not changed + - subject_present_idempotent.previous != [] + - subject_update is changed + - subject_update.sent != subject_update.proposed + - subject_update.sent.vzSubj.attributes.prio == 'level2' + - subject_update.sent.vzSubj.attributes.provMatchT == 'AtmostOne' + - subject_present_2 is changed + - subject_present_2.sent.vzSubj.attributes.consMatchT == 'All' + - subject_present_2.sent.vzSubj.attributes.name == 'anstest2' + - subject_present_2.sent.vzSubj.attributes.prio == 'level3' + - subject_present_2.sent.vzSubj.attributes.revFltPorts == 'no' + - present_missing_param is failed + - 'present_missing_param.msg == "state is present but all of the following are missing: contract, subject"' + +- name: query tenant contract subject + cisco.aci.aci_contract_subject: &aci_query_subject + <<: *aci_subject_present + state: query + register: query_tenant_contract_subject + +- name: query tenant contract + cisco.aci.aci_contract_subject: + <<: *aci_query_subject + subject: "{{ fakevar | default(omit) }}" + register: query_tenant_contract + +- name: query tenant subject + cisco.aci.aci_contract_subject: + <<: *aci_query_subject + contract: "{{ fakevar | default(omit) }}" + register: query_tenant_subject + +- name: query contract subject + cisco.aci.aci_contract_subject: + <<: *aci_query_subject + tenant: "{{ fakevar | default(omit) }}" + register: query_contract_subject + +- name: query tenant + cisco.aci.aci_contract_subject: + <<: *aci_tenant_present + state: query + register: query_tenant + +- name: query contract + cisco.aci.aci_contract_subject: + <<: *aci_contract_present + state: query + tenant: "{{ fakevar | default(omit) }}" + register: query_contract + +- name: query subject + cisco.aci.aci_contract_subject: + <<: *aci_query_subject + tenant: "{{ fakevar | default(omit) }}" + contract: "{{ fakevar | default(omit) }}" + register: query_subject + +- name: query all + cisco.aci.aci_contract_subject: + <<: *aci_tenant_present + state: query + tenant: "{{ fakevar | default(omit) }}" + register: query_all + +- name: query assertions + assert: + that: + - query_tenant_contract_subject is not changed + - query_tenant_contract_subject.current | length == 1 + - query_tenant_contract_subject.current.0.vzSubj.attributes.name == "anstest" + - '"tn-anstest/brc-anstest/subj-anstest.json" in query_tenant_contract_subject.url' + - query_tenant_contract is not changed + - query_tenant_contract.current | length == 1 + - query_tenant_contract.current.0.vzBrCP.attributes.name == "anstest" + - query_tenant_contract.current.0.vzBrCP.children | length == 2 + - '"rsp-subtree-class=vzSubj" in query_tenant_contract.filter_string' + - '"tn-anstest/brc-anstest.json" in query_tenant_contract.url' + - query_tenant_subject is not changed + - query_tenant_subject.current | length == 1 + - query_tenant_subject.current.0.fvTenant.attributes.name == "anstest" + - query_tenant_subject.current.0.fvTenant.children.0.vzBrCP.children | length == 1 + - query_tenant_subject.current.0.fvTenant.children.0.vzBrCP.children.0.vzSubj.attributes.name == "anstest" + - '"rsp-subtree-filter=eq(vzSubj.name,\"anstest\")" in query_tenant_subject.filter_string' + - '"rsp-subtree-class=vzSubj" in query_tenant_subject.filter_string' + - '"tn-anstest.json" in query_tenant_subject.url' + - query_contract_subject is not changed + - query_contract_subject.current.0.vzBrCP.attributes.name == "anstest" + - query_contract_subject.current.0.vzBrCP.children | length == 1 + - query_contract_subject.current.0.vzBrCP.children.0.vzSubj.attributes.name == "anstest" + - '"query-target-filter=eq(vzBrCP.name,\"anstest\")" in query_contract_subject.filter_string' + - '"rsp-subtree-filter=eq(vzSubj.name,\"anstest\")" in query_contract_subject.filter_string' + - '"rsp-subtree-class=vzSubj" in query_contract_subject.filter_string' + - '"class/vzBrCP.json" in query_contract_subject.url' + - query_tenant is not changed + - query_tenant.current | length == 1 + - query_tenant.current.0.fvTenant.attributes.name == "anstest" + - '"rsp-subtree-class=vzBrCP,vzSubj" in query_tenant.filter_string' + - '"tn-anstest.json" in query_tenant.url' + - query_contract is not changed + - query_contract.current.0.vzBrCP.attributes.name == "anstest" + - '"query-target-filter=eq(vzBrCP.name,\"anstest\")" in query_contract.filter_string' + - '"rsp-subtree-class=vzSubj" in query_contract.filter_string' + - '"class/vzBrCP.json" in query_contract.url' + - query_subject is not changed + - query_subject.current.0.vzSubj.attributes.name == "anstest" + - '"query-target-filter=eq(vzSubj.name,\"anstest\")" in query_subject.filter_string' + - '"class/vzSubj.json" in query_subject.url' + - query_all is not changed + - query_all.current|length > 1 + - query_all.current.0.vzSubj is defined + - '"class/vzSubj.json" in query_all.url' + +- name: delete subject - check mode works + cisco.aci.aci_contract_subject: &aci_subject_absent + <<: *aci_subject_present + state: absent + check_mode: yes + register: subject_absent_check_mode + +- name: delete subject - deletion works + cisco.aci.aci_contract_subject: + <<: *aci_subject_absent + register: subject_absent + +- name: delete subject - idempotency works + cisco.aci.aci_contract_subject: + <<: *aci_subject_absent + register: subject_absent_idempotent + +- name: delete subject - cleanup + cisco.aci.aci_contract_subject: + <<: *aci_subject_present_2 + state: absent + +- name: missing params - failure message works + cisco.aci.aci_contract_subject: + <<: *aci_subject_absent + subject: "{{ fakevar | default(omit) }}" + ignore_errors: yes + register: absent_missing_param + +- name: absent assertions + assert: + that: + - subject_absent_check_mode is changed + - subject_absent_check_mode.previous != [] + - subject_absent_check_mode.proposed == {} + - subject_absent is changed + - subject_absent.previous == subject_absent_check_mode.previous + - subject_absent_idempotent is not changed + - subject_absent_idempotent.previous == [] + - absent_missing_param is failed + - 'absent_missing_param.msg == "state is absent but all of the following are missing: subject"' + +- name: cleanup contract + cisco.aci.aci_contract: + <<: *aci_contract_present + state: absent + when: contract_present is changed + +- name: cleanup tenant + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent + when: tenant_present is changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract_subject_to_filter/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract_subject_to_filter/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract_subject_to_filter/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract_subject_to_filter/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract_subject_to_filter/tasks/main.yml new file mode 100644 index 00000000..8448f2c9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_contract_subject_to_filter/tasks/main.yml @@ -0,0 +1,194 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: anstest + state: present + register: tenant_present + +- name: ensure filter exists for tests to kick off + cisco.aci.aci_filter: &aci_filter_present + <<: *aci_tenant_present + filter: anstest + register: filter_present + +- name: ensure filter exists for tests to kick off + cisco.aci.aci_filter: &aci_filter_present_2 + <<: *aci_tenant_present + filter: anstest2 + register: filter_present_2 + +- name: ensure contract exists for tests to kick off + cisco.aci.aci_contract: &aci_contract_present + <<: *aci_tenant_present + contract: anstest + register: contract_present + +- name: ensure subject exists for tests to kick off + cisco.aci.aci_contract_subject: &aci_subject_present + <<: *aci_contract_present + subject: anstest + register: subject_present + +- name: create subject filter binding - check mode works + cisco.aci.aci_contract_subject_to_filter: &aci_subject_filter_present + <<: *aci_subject_present + filter: anstest + log: log + check_mode: yes + register: subject_filter_present_check_mode + +- name: create subject filter binding - creation works + cisco.aci.aci_contract_subject_to_filter: + <<: *aci_subject_filter_present + register: subject_filter_present + +- name: create subject filter binding - additional testing + cisco.aci.aci_contract_subject_to_filter: &aci_subject_filter_present_2 + <<: *aci_subject_filter_present + filter: anstest2 + register: subject_filter_present_2 + +- name: create subject filter binding - idempotency works + cisco.aci.aci_contract_subject_to_filter: + <<: *aci_subject_filter_present + register: subject_filter_present_idempotent + +- name: update subject filter binding - update works + cisco.aci.aci_contract_subject_to_filter: + <<: *aci_subject_filter_present + log: none + register: subject_filter_update + +- name: missing param - failure message works + cisco.aci.aci_contract_subject_to_filter: + <<: *aci_tenant_present + ignore_errors: yes + register: present_missing_param + +- name: present assertions + assert: + that: + - subject_filter_present_check_mode is changed + - subject_filter_present_check_mode.previous == [] + - subject_filter_present_check_mode.sent.vzRsSubjFiltAtt.attributes.directives == 'log' + - subject_filter_present_check_mode.sent.vzRsSubjFiltAtt.attributes.tnVzFilterName == 'anstest' + - subject_filter_present is changed + - subject_filter_present.previous == [] + - subject_filter_present.sent == subject_filter_present_check_mode.sent + - subject_filter_present_2 is changed + - subject_filter_present_idempotent is not changed + - subject_filter_present_idempotent.previous != [] + - subject_filter_update is changed + - subject_filter_update.sent.vzRsSubjFiltAtt.attributes.directives == '' + - present_missing_param is failed + - 'present_missing_param.msg == "state is present but all of the following are missing: contract, filter, subject"' + +- name: query all + cisco.aci.aci_contract_subject_to_filter: + <<: *aci_tenant_present + state: query + tenant: "{{ fakevar | default(omit) }}" + register: query_all + +- name: query binding + cisco.aci.aci_contract_subject_to_filter: + <<: *aci_subject_filter_present + state: query + register: query_binding + +- name: query assertions + assert: + that: + - query_all is not changed + - query_all.current | length > 1 + - query_all.current.0.vzRsSubjFiltAtt is defined + - query_binding is not changed + - query_binding.current != [] + +- name: delete subject filter binding - check mode works + cisco.aci.aci_contract_subject_to_filter: &aci_subject_filter_absent + <<: *aci_subject_filter_present + state: absent + check_mode: yes + register: subject_filter_absent_check_mode + +- name: delete subject filter binding - deletion works + cisco.aci.aci_contract_subject_to_filter: + <<: *aci_subject_filter_absent + register: subject_filter_absent + +- name: delete subject filter binding - idempotency works + cisco.aci.aci_contract_subject_to_filter: + <<: *aci_subject_filter_absent + register: subject_filter_absent_idempotent + +- name: missing param - failure message works + cisco.aci.aci_contract_subject_to_filter: + <<: *aci_subject_filter_absent + filter: "{{ fakevar | default(omit) }}" + ignore_errors: yes + register: absent_missing_param + +- name: cleanup subject filter binding + cisco.aci.aci_contract_subject_to_filter: + <<: *aci_subject_filter_present_2 + state: absent + +- name: absent assertions + assert: + that: + - subject_filter_absent_check_mode is changed + - subject_filter_absent_check_mode.proposed == {} + - subject_filter_absent_check_mode.previous != [] + - subject_filter_absent is changed + - subject_filter_absent.previous != [] + - subject_filter_absent_idempotent is not changed + - subject_filter_absent_idempotent.previous == [] + - absent_missing_param is failed + - 'absent_missing_param.msg == "state is absent but all of the following are missing: filter"' + +- name: cleanup subject + cisco.aci.aci_contract_subject: + <<: *aci_subject_present + state: absent + when: subject_present is changed + +- name: cleanup contract + cisco.aci.aci_contract: + <<: *aci_contract_present + state: absent + when: contract_present is changed + +- name: cleanup filter + cisco.aci.aci_filter: + <<: *aci_filter_present + state: absent + when: filter_present is changed + +- name: cleanup filter + cisco.aci.aci_filter: + <<: *aci_filter_present_2 + state: absent + when: filter_present_2 is changed + +- name: cleanup tenant + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent + when: tenant_present is changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/fc.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/fc.yml new file mode 100644 index 00000000..abc01ac2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/fc.yml @@ -0,0 +1,176 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# CLEAN ENVIRONMENT +- name: Remove FC domain + cisco.aci.aci_domain: &domain_absent + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: fc_dom + domain_type: fc + state: absent + + +# ADD DOMAIN +- name: Add FC domain (check_mode) + cisco.aci.aci_domain: &domain_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: fc_dom + domain_type: fc + state: present + check_mode: yes + register: cm_add_domain + +- name: Add FC domain (normal mode) + cisco.aci.aci_domain: *domain_present + register: nm_add_domain + +- name: Verify FC add_domain + assert: + that: + - cm_add_domain is changed + - nm_add_domain is changed + - cm_add_domain.sent.fcDomP.attributes.name == nm_add_domain.sent.fcDomP.attributes.name == 'fc_dom' + - cm_add_domain.proposed.fcDomP.attributes.name == nm_add_domain.proposed.fcDomP.attributes.name == 'fc_dom' + - cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == [] + - nm_add_domain.current.0.fcDomP.attributes.name == 'fc_dom' + - nm_add_domain.current.0.fcDomP.attributes.dn == 'uni/fc-fc_dom' + +- name: Add FC domain again (check_mode) + cisco.aci.aci_domain: *domain_present + check_mode: yes + register: cm_add_domain_again + +- name: Add FC domain again (normal mode) + cisco.aci.aci_domain: *domain_present + register: nm_add_domain_again + +- name: Verify FC add_domain_again + assert: + that: + - cm_add_domain_again is not changed + - nm_add_domain_again is not changed + + +# QUERY ALL DOMAINS +- name: Query all FC domains (check_mode) + cisco.aci.aci_domain: &domain_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain_type: fc + state: query + check_mode: yes + register: cm_query_all_domains + +- name: Query all FC domains (normal mode) + cisco.aci.aci_domain: *domain_query + register: nm_query_all_domains + +- name: Verify FC query_all_domains + assert: + that: + - cm_query_all_domains is not changed + - nm_query_all_domains is not changed + - cm_query_all_domains == nm_query_all_domains + - nm_query_all_domains.current|length >= 1 + + +# QUERY A DOMAIN +- name: Query our FC domain (check_mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: fc_dom + check_mode: yes + register: cm_query_domain + +- name: Query our FC domain (normal mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: fc_dom + register: nm_query_domain + +- name: Verify FC query_domain + assert: + that: + - cm_query_domain is not changed + - nm_query_domain is not changed + - cm_query_domain == nm_query_domain + - nm_query_domain.current.0.fcDomP.attributes.dn == 'uni/fc-fc_dom' + - nm_query_domain.current.0.fcDomP.attributes.name == 'fc_dom' + + +# REMOVE DOMAIN +- name: Remove FC domain (check_mode) + cisco.aci.aci_domain: *domain_absent + check_mode: yes + register: cm_remove_domain + +- name: Remove FC domain (normal mode) + cisco.aci.aci_domain: *domain_absent + register: nm_remove_domain + +- name: Verify FC remove_domain + assert: + that: + - cm_remove_domain is changed + - nm_remove_domain is changed + - cm_remove_domain.current.0.fcDomP.attributes.name == cm_remove_domain.previous.0.fcDomP.attributes.name == nm_remove_domain.previous.0.fcDomP.attributes.name == 'fc_dom' + - cm_remove_domain.current.0.fcDomP.attributes.dn == cm_remove_domain.previous.0.fcDomP.attributes.dn == nm_remove_domain.previous.0.fcDomP.attributes.dn == 'uni/fc-fc_dom' + - nm_remove_domain.current == [] + +- name: Remove FC domain again (check_mode) + cisco.aci.aci_domain: *domain_absent + check_mode: yes + register: cm_remove_domain_again + +- name: Remove FC domain again (normal mode) + cisco.aci.aci_domain: *domain_absent + register: nm_remove_domain_again + +- name: Verify FC remove_domain_again + assert: + that: + - cm_remove_domain_again is not changed + - nm_remove_domain_again is not changed + + +# QUERY NON-EXISTING DOMAIN +- name: Query non-existing FC domain (check_mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: fc_dom + check_mode: yes + register: cm_query_non_domain + +- name: Query non-existing FC domain (normal mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: fc_dom + register: nm_query_non_domain + +- name: Verify FC query_non_domain + assert: + that: + - cm_query_non_domain is not changed + - nm_query_non_domain is not changed + - cm_query_non_domain == nm_query_non_domain + - nm_query_non_domain.current == [] diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/l2dom.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/l2dom.yml new file mode 100644 index 00000000..29b46896 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/l2dom.yml @@ -0,0 +1,176 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# CLEAN ENVIRONMENT +- name: Remove L2 domain + cisco.aci.aci_domain: &domain_absent + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: l2_dom + domain_type: l2dom + state: absent + + +# ADD DOMAIN +- name: Add L2 domain (check_mode) + cisco.aci.aci_domain: &domain_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: l2_dom + domain_type: l2dom + state: present + check_mode: yes + register: cm_add_domain + +- name: Add L2 domain (normal mode) + cisco.aci.aci_domain: *domain_present + register: nm_add_domain + +- name: Verify L2 add_domain + assert: + that: + - cm_add_domain is changed + - nm_add_domain is changed + - cm_add_domain.sent.l2extDomP.attributes.name == nm_add_domain.sent.l2extDomP.attributes.name == 'l2_dom' + - cm_add_domain.proposed.l2extDomP.attributes.name == nm_add_domain.proposed.l2extDomP.attributes.name == 'l2_dom' + - cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == [] + - nm_add_domain.current.0.l2extDomP.attributes.name == 'l2_dom' + - nm_add_domain.current.0.l2extDomP.attributes.dn == 'uni/l2dom-l2_dom' + +- name: Add L2 domain again (check_mode) + cisco.aci.aci_domain: *domain_present + check_mode: yes + register: cm_add_domain_again + +- name: Add L2 domain again (normal mode) + cisco.aci.aci_domain: *domain_present + register: nm_add_domain_again + +- name: Verify L2 add_domain_again + assert: + that: + - cm_add_domain_again is not changed + - nm_add_domain_again is not changed + + +# QUERY ALL DOMAINS +- name: Query all L2 domains (check_mode) + cisco.aci.aci_domain: &domain_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain_type: l2dom + state: query + check_mode: yes + register: cm_query_all_domains + +- name: Query all L2 domains (normal mode) + cisco.aci.aci_domain: *domain_query + register: nm_query_all_domains + +- name: Verify L2 query_all_domains + assert: + that: + - cm_query_all_domains is not changed + - nm_query_all_domains is not changed + - cm_query_all_domains == nm_query_all_domains + - nm_query_all_domains.current|length >= 1 + + +# QUERY A DOMAIN +- name: Query our L2 domain (check_mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: l2_dom + check_mode: yes + register: cm_query_domain + +- name: Query our L2 domain (normal mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: l2_dom + register: nm_query_domain + +- name: Verify L2 query_domain + assert: + that: + - cm_query_domain is not changed + - nm_query_domain is not changed + - cm_query_domain == nm_query_domain + - nm_query_domain.current.0.l2extDomP.attributes.dn == 'uni/l2dom-l2_dom' + - nm_query_domain.current.0.l2extDomP.attributes.name == 'l2_dom' + + +# REMOVE DOMAIN +- name: Remove L2 domain (check_mode) + cisco.aci.aci_domain: *domain_absent + check_mode: yes + register: cm_remove_domain + +- name: Remove L2 domain (normal mode) + cisco.aci.aci_domain: *domain_absent + register: nm_remove_domain + +- name: Verify L2 remove_domain + assert: + that: + - cm_remove_domain is changed + - nm_remove_domain is changed + - cm_remove_domain.current.0.l2extDomP.attributes.dn == cm_remove_domain.previous.0.l2extDomP.attributes.dn == nm_remove_domain.previous.0.l2extDomP.attributes.dn == 'uni/l2dom-l2_dom' + - cm_remove_domain.current.0.l2extDomP.attributes.name == cm_remove_domain.previous.0.l2extDomP.attributes.name == nm_remove_domain.previous.0.l2extDomP.attributes.name == 'l2_dom' + - nm_remove_domain.current == [] + +- name: Remove L2 domain again (check_mode) + cisco.aci.aci_domain: *domain_absent + check_mode: yes + register: cm_remove_domain_again + +- name: Remove L2 domain again (normal mode) + cisco.aci.aci_domain: *domain_absent + register: nm_remove_domain_again + +- name: Verify L2 remove_domain_again + assert: + that: + - cm_remove_domain_again is not changed + - nm_remove_domain_again is not changed + + +# QUERY NON-EXISTING DOMAIN +- name: Query non-existing L2 domain (check_mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: l2_dom + check_mode: yes + register: cm_query_non_domain + +- name: Query non-existing L2 domain (normal mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: l2_dom + register: nm_query_non_domain + +- name: Verify L2 query_non_domain + assert: + that: + - cm_query_non_domain is not changed + - nm_query_non_domain is not changed + - cm_query_non_domain == nm_query_non_domain + - nm_query_non_domain.current == [] diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/l3dom.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/l3dom.yml new file mode 100644 index 00000000..16bc72d4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/l3dom.yml @@ -0,0 +1,176 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# CLEAN ENVIRONMENT +- name: Remove L3 domain + cisco.aci.aci_domain: &domain_absent + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: l3_dom + domain_type: l3dom + state: absent + + +# ADD DOMAIN +- name: Add L3 domain (check_mode) + cisco.aci.aci_domain: &domain_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: l3_dom + domain_type: l3dom + state: present + check_mode: yes + register: cm_add_domain + +- name: Add L3 domain (normal mode) + cisco.aci.aci_domain: *domain_present + register: nm_add_domain + +- name: Verify L3 add_domain + assert: + that: + - cm_add_domain is changed + - nm_add_domain is changed + - cm_add_domain.sent.l3extDomP.attributes.name == nm_add_domain.sent.l3extDomP.attributes.name == 'l3_dom' + - cm_add_domain.proposed.l3extDomP.attributes.name == nm_add_domain.proposed.l3extDomP.attributes.name == 'l3_dom' + - cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == [] + - nm_add_domain.current.0.l3extDomP.attributes.name == 'l3_dom' + - nm_add_domain.current.0.l3extDomP.attributes.dn == 'uni/l3dom-l3_dom' + +- name: Add L3 domain again (check_mode) + cisco.aci.aci_domain: *domain_present + check_mode: yes + register: cm_add_domain_again + +- name: Add L3 domain again (normal mode) + cisco.aci.aci_domain: *domain_present + register: nm_add_domain_again + +- name: Verify L3 add_domain_again + assert: + that: + - cm_add_domain_again is not changed + - nm_add_domain_again is not changed + + +# QUERY ALL DOMAINS +- name: Query all L3 domains (check_mode) + cisco.aci.aci_domain: &domain_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain_type: l3dom + state: query + check_mode: yes + register: cm_query_all_domains + +- name: Query all L3 domains (normal mode) + cisco.aci.aci_domain: *domain_query + register: nm_query_all_domains + +- name: Verify query_all_domains + assert: + that: + - cm_query_all_domains is not changed + - nm_query_all_domains is not changed + - cm_query_all_domains == nm_query_all_domains + - nm_query_all_domains.current|length >= 1 + + +# QUERY A DOMAIN +- name: Query our L3 domain (check_mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: l3_dom + check_mode: yes + register: cm_query_domain + +- name: Query our L3 domain (normal mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: l3_dom + register: nm_query_domain + +- name: Verify L3 query_domain + assert: + that: + - cm_query_domain is not changed + - nm_query_domain is not changed + - cm_query_domain == nm_query_domain + - nm_query_domain.current.0.l3extDomP.attributes.dn == 'uni/l3dom-l3_dom' + - nm_query_domain.current.0.l3extDomP.attributes.name == 'l3_dom' + + +# REMOVE DOMAIN +- name: Remove L3 domain (check_mode) + cisco.aci.aci_domain: *domain_absent + check_mode: yes + register: cm_remove_domain + +- name: Remove L3 domain (normal mode) + cisco.aci.aci_domain: *domain_absent + register: nm_remove_domain + +- name: Verify L3 remove_domain + assert: + that: + - cm_remove_domain is changed + - nm_remove_domain is changed + - cm_remove_domain.current.0.l3extDomP.attributes.name == cm_remove_domain.previous.0.l3extDomP.attributes.name == nm_remove_domain.previous.0.l3extDomP.attributes.name == 'l3_dom' + - cm_remove_domain.current.0.l3extDomP.attributes.dn == cm_remove_domain.previous.0.l3extDomP.attributes.dn == nm_remove_domain.previous.0.l3extDomP.attributes.dn == 'uni/l3dom-l3_dom' + - nm_remove_domain.current == [] + +- name: Remove L3 domain again (check_mode) + cisco.aci.aci_domain: *domain_absent + check_mode: yes + register: cm_remove_domain_again + +- name: Remove L3 domain again (normal mode) + cisco.aci.aci_domain: *domain_absent + register: nm_remove_domain_again + +- name: Verify L3 remove_domain_again + assert: + that: + - cm_remove_domain_again is not changed + - nm_remove_domain_again is not changed + + +# QUERY NON-EXISTING DOMAIN +- name: Query non-existing L3 domain (check_mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: l3_dom + check_mode: yes + register: cm_query_non_domain + +- name: Query non-existing L3 domain (normal mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: l3_dom + register: nm_query_non_domain + +- name: Verify L3 query_non_domain + assert: + that: + - cm_query_non_domain is not changed + - nm_query_non_domain is not changed + - cm_query_non_domain == nm_query_non_domain + - nm_query_non_domain.current == [] diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/main.yml new file mode 100644 index 00000000..be6c9712 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/main.yml @@ -0,0 +1,24 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- include_tasks: phys.yml + when: phys is not defined or phys + +- include_tasks: l2dom.yml + when: l2dom is not defined or l2dom + +- include_tasks: l3dom.yml + when: l3dom is not defined or l3dom + +- include_tasks: fc.yml + when: fc is not defined or fc + +- include_tasks: vmm-vmware.yml + when: vmm_vmware is not defined or vmm_vmware diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/phys.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/phys.yml new file mode 100644 index 00000000..7478858c --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/phys.yml @@ -0,0 +1,176 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# CLEAN ENVIRONMENT +- name: Remove physical domain + cisco.aci.aci_domain: &domain_absent + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: phys_dom + domain_type: phys + state: absent + + +# ADD DOMAIN +- name: Add physical domain (check_mode) + cisco.aci.aci_domain: &domain_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: phys_dom + domain_type: phys + state: present + check_mode: yes + register: cm_add_domain + +- name: Add physical domain (normal mode) + cisco.aci.aci_domain: *domain_present + register: nm_add_domain + +- name: Verify physical add_domain + assert: + that: + - cm_add_domain is changed + - nm_add_domain is changed + - cm_add_domain.sent.physDomP.attributes.name == nm_add_domain.sent.physDomP.attributes.name == 'phys_dom' + - cm_add_domain.proposed.physDomP.attributes.name == nm_add_domain.proposed.physDomP.attributes.name == 'phys_dom' + - cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == [] + - nm_add_domain.current.0.physDomP.attributes.name == 'phys_dom' + - nm_add_domain.current.0.physDomP.attributes.dn == 'uni/phys-phys_dom' + +- name: Add physical domain again (check_mode) + cisco.aci.aci_domain: *domain_present + check_mode: yes + register: cm_add_domain_again + +- name: Add physical domain again (normal mode) + cisco.aci.aci_domain: *domain_present + register: nm_add_domain_again + +- name: Verify physical add_domain_again + assert: + that: + - cm_add_domain_again is not changed + - nm_add_domain_again is not changed + + +# QUERY ALL DOMAINS +- name: Query all physical domains (check_mode) + cisco.aci.aci_domain: &domain_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain_type: phys + state: query + check_mode: yes + register: cm_query_all_domains + +- name: Query all physical domains (normal mode) + cisco.aci.aci_domain: *domain_query + register: nm_query_all_domains + +- name: Verify physical query_all_domains + assert: + that: + - cm_query_all_domains is not changed + - nm_query_all_domains is not changed + - cm_query_all_domains == nm_query_all_domains + - nm_query_all_domains.current|length >= 1 + + +# QUERY A DOMAIN +- name: Query our physical domain (check_mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: phys_dom + check_mode: yes + register: cm_query_domain + +- name: Query our physical domain (normal mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: phys_dom + register: nm_query_domain + +- name: Verify physical query_domain + assert: + that: + - cm_query_domain is not changed + - nm_query_domain is not changed + - cm_query_domain == nm_query_domain + - nm_query_domain.current.0.physDomP.attributes.dn == 'uni/phys-phys_dom' + - nm_query_domain.current.0.physDomP.attributes.name == 'phys_dom' + + +# REMOVE DOMAIN +- name: Remove physical domain (check_mode) + cisco.aci.aci_domain: *domain_absent + check_mode: yes + register: cm_remove_domain + +- name: Remove physical domain (normal mode) + cisco.aci.aci_domain: *domain_absent + register: nm_remove_domain + +- name: Verify physical remove_domain + assert: + that: + - cm_remove_domain is changed + - nm_remove_domain is changed + - cm_remove_domain.current.0.physDomP.attributes.name == cm_remove_domain.previous.0.physDomP.attributes.name == nm_remove_domain.previous.0.physDomP.attributes.name == 'phys_dom' + - cm_remove_domain.current.0.physDomP.attributes.dn == cm_remove_domain.previous.0.physDomP.attributes.dn == nm_remove_domain.previous.0.physDomP.attributes.dn == 'uni/phys-phys_dom' + - nm_remove_domain.current == [] + +- name: Remove physical domain again (check_mode) + cisco.aci.aci_domain: *domain_absent + check_mode: yes + register: cm_remove_domain_again + +- name: Remove physical domain again (normal mode) + cisco.aci.aci_domain: *domain_absent + register: nm_remove_domain_again + +- name: Verify physical remove_domain_again + assert: + that: + - cm_remove_domain_again is not changed + - nm_remove_domain_again is not changed + + +# QUERY NON-EXISTING DOMAIN +- name: Query non-existing physical domain (check_mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: phys_dom + check_mode: yes + register: cm_query_non_domain + +- name: Query non-existing physical domain (normal mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: phys_dom + register: nm_query_non_domain + +- name: Verify physical query_non_domain + assert: + that: + - cm_query_non_domain is not changed + - nm_query_non_domain is not changed + - cm_query_non_domain == nm_query_non_domain + - nm_query_non_domain.current == [] diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/vmm-vmware.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/vmm-vmware.yml new file mode 100644 index 00000000..dde2a909 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain/tasks/vmm-vmware.yml @@ -0,0 +1,184 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# CLEAN ENVIRONMENT +- name: Remove VMM domain + cisco.aci.aci_domain: &domain_absent + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: vmm_dom + domain_type: vmm + vm_provider: vmware + state: absent + + +# ADD DOMAIN +- name: Add VMM domain (check_mode) + cisco.aci.aci_domain: &domain_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: vmm_dom + domain_type: vmm + vm_provider: vmware + state: present + check_mode: yes + register: cm_add_domain + +- name: Add VMM domain (normal mode) + cisco.aci.aci_domain: *domain_present + register: nm_add_domain + +- name: Verify VMM add_domain + assert: + that: + - cm_add_domain is changed + - nm_add_domain is changed + - cm_add_domain.sent.vmmDomP.attributes.name == nm_add_domain.sent.vmmDomP.attributes.name == 'vmm_dom' + - cm_add_domain.proposed.vmmDomP.attributes.name == nm_add_domain.proposed.vmmDomP.attributes.name == 'vmm_dom' + - cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == [] + - nm_add_domain.current.0.vmmDomP.attributes.dn == 'uni/vmmp-VMware/dom-vmm_dom' + - nm_add_domain.current.0.vmmDomP.attributes.name == 'vmm_dom' + +- name: Add VMM domain again (check_mode) + cisco.aci.aci_domain: *domain_present + check_mode: yes + register: cm_add_domain_again + +- name: Add VMM domain again (normal mode) + cisco.aci.aci_domain: *domain_present + register: nm_add_domain_again + +- name: Verify VMM add_domain_again + assert: + that: + - cm_add_domain_again is not changed + - nm_add_domain_again is not changed + + +# QUERY ALL DOMAINS +- name: Query all VMM domains (check_mode) + cisco.aci.aci_domain: &domain_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain_type: vmm + vm_provider: vmware + state: query + check_mode: yes + register: cm_query_all_domains + +- name: Query all VMM domains (normal mode) + cisco.aci.aci_domain: *domain_query + register: nm_query_all_domains + +- name: Verify query_all_domains + assert: + that: + - cm_query_all_domains is not changed + - nm_query_all_domains is not changed + - cm_query_all_domains == nm_query_all_domains + - nm_query_all_domains.current|length >= 1 + + +# QUERY A DOMAIN +- name: Query our VMM domain (check_mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: vmm_dom + vm_provider: vmware + check_mode: yes + register: cm_query_domain + +- name: Query our VMM domain (normal mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: vmm_dom + vm_provider: vmware + register: nm_query_domain + +- name: Verify VMM query_domain + assert: + that: + - cm_query_domain is not changed + - nm_query_domain is not changed + - cm_query_domain == nm_query_domain + - nm_query_domain.current.0.vmmDomP.attributes.dn == 'uni/vmmp-VMware/dom-vmm_dom' + - nm_query_domain.current.0.vmmDomP.attributes.name == 'vmm_dom' + + +# REMOVE DOMAIN +- name: Remove VMM domain (check_mode) + cisco.aci.aci_domain: *domain_absent + check_mode: yes + register: cm_remove_domain + +- name: Remove VMM domain (normal mode) + cisco.aci.aci_domain: *domain_absent + register: nm_remove_domain + +- name: Verify VMM remove_domain + assert: + that: + - cm_remove_domain is changed + - nm_remove_domain is changed + - cm_remove_domain.current == cm_remove_domain.previous == nm_remove_domain.previous + - nm_remove_domain.previous.0.vmmDomP.attributes.dn == 'uni/vmmp-VMware/dom-vmm_dom' + - nm_remove_domain.previous.0.vmmDomP.attributes.name == 'vmm_dom' + - nm_remove_domain.current == [] + +- name: Remove VMM domain again (check_mode) + cisco.aci.aci_domain: *domain_absent + check_mode: yes + register: cm_remove_domain_again + +- name: Remove VMM domain again (normal mode) + cisco.aci.aci_domain: *domain_absent + register: nm_remove_domain_again + +- name: Verify VMM remove_domain_again + assert: + that: + - cm_remove_domain_again is not changed + - nm_remove_domain_again is not changed + + +# QUERY NON-EXISTING DOMAIN +- name: Query non-existing VMM domain (check_mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: vmm_dom + vm_provider: vmware + check_mode: yes + register: cm_query_non_domain + +- name: Query non-existing VMM domain (normal mode) + cisco.aci.aci_domain: + <<: *domain_query + domain: vmm_dom + vm_provider: vmware + register: nm_query_non_domain + +- name: Verify VMM query_non_domain + assert: + that: + - cm_query_non_domain is not changed + - nm_query_non_domain is not changed + - cm_query_non_domain == nm_query_non_domain + - nm_query_non_domain.current == [] diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain_to_encap_pool/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain_to_encap_pool/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain_to_encap_pool/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain_to_encap_pool/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain_to_encap_pool/tasks/main.yml new file mode 100644 index 00000000..39187401 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain_to_encap_pool/tasks/main.yml @@ -0,0 +1,635 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Shreyas Srish (@shrsr) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + +# Clean Environment +- name: Remove domains + aci_domain: + <<: *aci_info + domain: '{{ item.name }}' + domain_type: '{{ item.type }}' + state: absent + loop: + - { name: phys_dom, type: phys } + - { name: fc_dom, type: fc } + - { name: l2_dom, type: l2dom } + - { name: l3_dom, type: l3dom } + +- name: Remove domains + aci_domain: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + vm_provider: '{{ item.name }}' + state: absent + loop: + - { name: vmware } + - { name: microsoft } + - { name: cloudfoundry } + - { name: openshift } + - { name: openstack } + - { name: redhat } + - { name: kubernetes } + +# Add +- name: Add physical domain + aci_domain: + <<: *aci_info + domain: phys_dom + domain_type: phys + state: present + register: phys_domain + +- name: Add l2 domain + aci_domain: + <<: *aci_info + domain: l2_dom + domain_type: l2dom + state: present + register: l2_domain + +- name: Add l3 domain + aci_domain: + <<: *aci_info + domain: l3_dom + domain_type: l3dom + state: present + register: l3_domain + +- name: Add FC domain + aci_domain: + <<: *aci_info + domain: fc_dom + domain_type: fc + state: present + register: fc_domain + +- name: Add vmm domain + aci_domain: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + vm_provider: vmware + state: present + register: vmm_domain + +- name: Add domain to encap pool binding (phys, pool_type=vlan) + aci_domain_to_encap_pool: + <<: *aci_info + domain: phys_dom + domain_type: phys + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + state: present + register: phys_vlan + +- name: Add domain to encap pool binding (fc, pool_type=vsan) + aci_domain_to_encap_pool: + <<: *aci_info + domain: fc_dom + domain_type: fc + pool: test_pool + pool_type: vsan + pool_allocation_mode: dynamic + state: present + register: fc_vsan + +- name: Add domain to encap pool binding (domain_type=l2dom) + aci_domain_to_encap_pool: + <<: *aci_info + domain: l2_dom + domain_type: l2dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: static + state: present + register: l2dom + +- name: Add domain to encap pool binding (domain_type=l3dom) + aci_domain_to_encap_pool: + <<: *aci_info + domain: l3_dom + domain_type: l3dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: static + state: present + register: l3dom + +- name: Add domain to encap pool binding (domain_type=vmm, vm_provider=cloudfoundry) + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: cloudfoundry + state: present + register: vmm_cloudfoundry + +- name: Add domain to encap pool binding (domain_type=vmm, vm_provider=kubernetes) + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: kubernetes + state: present + register: vmm_kubernetes + +- name: Add domain to encap pool binding (domain_type=vmm, vm_provider=microsoft) + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: microsoft + state: present + register: vmm_microsoft + +- name: Add domain to encap pool binding (domain_type=vmm, vm_provider=openshift) + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: openshift + state: present + register: vmm_openshift + +- name: Add domain to encap pool binding (domain_type=vmm, vm_provider=openstack) + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: openstack + state: present + register: vmm_openstack + +- name: Add domain to encap pool binding (domain_type=vmm, vm_provider=redhat) + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: redhat + state: present + register: vmm_redhat + +- name: Add domain to encap pool binding (domain_type=vmm, vm_provider=vmware) + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: vmware + state: present + register: vmm_vmware + +- name: Add domain to encap pool binding (domain_type=vmm, vm_provider=vmware) again + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: vmware + state: present + register: vmm_vmware_idemp + +- name: Add domain to encap pool binding (phys with vm_provider) + aci_domain_to_encap_pool: + <<: *aci_info + domain: phys_dom + domain_type: phys + pool: test_pool + pool_type: vlan + vm_provider: vmware + pool_allocation_mode: dynamic + state: present + ignore_errors: yes + register: phys_vlan_with_vmm + +- name: Add domain to encap pool binding (phys, pool_type=vlan, no allocation mode) + aci_domain_to_encap_pool: + <<: *aci_info + domain: phys_dom + domain_type: phys + pool: test_pool + pool_type: vlan + state: present + ignore_errors: yes + register: phys_vlan_no_alloc + +- name: Add domain to encap pool binding (domain_type=vmm, vm_provider=vmware, pool_type=vxlan) + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool: test_pool + pool_type: vxlan + pool_allocation_mode: dynamic + vm_provider: vmware + state: present + ignore_errors: yes + register: vmm_vmware_vxlan + +- name: Add domain to encap pool binding (phys, pool_type=vlan) missing domain_type + aci_domain_to_encap_pool: + <<: *aci_info + domain: phys_dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + state: present + ignore_errors: yes + register: phys_missing_domain_type + +- name: Add domain to encap pool binding (phys, pool_type=vlan) missing pool_type + aci_domain_to_encap_pool: + <<: *aci_info + domain: phys_dom + domain_type: phys + pool: test_pool + pool_allocation_mode: dynamic + state: present + ignore_errors: yes + register: phys_missing_pool_type + +- name: Verify add operation + assert: + that: + - phys_vlan is changed + - fc_vsan is changed + - l2dom is changed + - l3dom is changed + - vmm_cloudfoundry is changed + - vmm_kubernetes is changed + - vmm_openshift is changed + - vmm_openstack is changed + - vmm_redhat is changed + - vmm_vmware is changed + - vmm_vmware_idemp is not changed + - phys_vlan.current.0.physDomP.children.0.infraRsVlanNs.attributes.tDn =='uni/infra/vlanns-[test_pool]-dynamic' + - fc_vsan.current.0.fcDomP.children.0.fcRsVsanNs.attributes.tDn == 'uni/infra/vsanns-[test_pool]-dynamic' + - l2dom.current.0.l2extDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-static' + - l3dom.current.0.l3extDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-static' + - vmm_cloudfoundry.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - vmm_kubernetes.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - vmm_microsoft.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - vmm_openshift.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - vmm_openstack.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - vmm_redhat.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - vmm_vmware.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - phys_vlan_with_vmm.msg is match("Domain type '[0-9a-zA-Z]*' cannot have a 'vm_provider'") + - vmm_vmware_vxlan.msg is match("vxlan pools do not support setting the allocation_mode; please remove this parameter from the task") + - phys_vlan_no_alloc.msg is match("ACI requires the 'pool_allocation_mode' for 'pool_type' of 'vlan' and 'vsan' when 'pool' is provided") + - phys_missing_pool_type.msg is match("missing required arguments{{':'}} pool_type") + - phys_missing_domain_type.msg is match("missing required arguments{{':'}} domain_type") + +# QUERY +- name: Query all + aci_domain_to_encap_pool: + <<: *aci_info + domain_type: phys + pool_type: vlan + state: query + register: query_all + +- name: Query phys + aci_domain_to_encap_pool: + <<: *aci_info + domain: phys_dom + domain_type: phys + pool_type: vlan + state: query + register: query_phys + +- name: Query fc + aci_domain_to_encap_pool: + <<: *aci_info + domain: fc_dom + domain_type: fc + pool_type: vsan + state: query + register: query_fc + +- name: Query l2dom + aci_domain_to_encap_pool: + <<: *aci_info + domain: l2_dom + domain_type: l2dom + pool_type: vlan + state: query + register: query_l2dom + +- name: Query l3dom + aci_domain_to_encap_pool: + <<: *aci_info + domain: l3_dom + domain_type: l3dom + pool_type: vlan + state: query + register: query_l3dom + +- name: Query vmm + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool_type: vlan + vm_provider: microsoft + state: query + register: query_vmm_microsoft + +- name: Query vmm + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool_type: vlan + vm_provider: cloudfoundry + state: query + register: query_vmm_cloudfoundry + +- name: Query vmm + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool_type: vlan + vm_provider: kubernetes + state: query + register: query_vmm_kubernetes + +- name: Query vmm + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool_type: vlan + vm_provider: openshift + state: query + register: query_vmm_openshift + +- name: Query vmm + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool_type: vlan + vm_provider: openstack + state: query + register: query_vmm_openstack + +- name: Query vmm + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool_type: vlan + vm_provider: redhat + state: query + register: query_vmm_redhat + +- name: Query vmm + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool_type: vlan + vm_provider: vmware + state: query + register: query_vmm_vmware + +- name: Query vmm + aci_domain_to_encap_pool: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + pool_type: vlan + vm_provider: vmware + state: query + register: query_vmm_vmware + +- name: Verify Query + assert: + that: + - query_all | length >=1 + - query_phys.current.0.physDomP.children.0.infraRsVlanNs.attributes.tDn =='uni/infra/vlanns-[test_pool]-dynamic' + - query_fc.current.0.fcDomP.children.0.fcRsVsanNs.attributes.tDn == 'uni/infra/vsanns-[test_pool]-dynamic' + - query_l2dom.current.0.l2extDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-static' + - query_l3dom.current.0.l3extDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-static' + - query_vmm_cloudfoundry.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - query_vmm_kubernetes.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - query_vmm_microsoft.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - query_vmm_openshift.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - query_vmm_openstack.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - query_vmm_redhat.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - query_vmm_vmware.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + +# REMOVE +- name: Remove phys + aci_domain_to_encap_pool: + <<: *aci_info + domain_type: phys + domain: phys_dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + state: absent + register: remove_phys + +- name: Remove fc + aci_domain_to_encap_pool: + <<: *aci_info + domain_type: fc + domain: fc_dom + pool: test_pool + pool_type: vsan + pool_allocation_mode: dynamic + state: absent + register: remove_fc + +- name: Remove l2dom + aci_domain_to_encap_pool: + <<: *aci_info + domain_type: l2dom + domain: l2_dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: static + state: absent + register: remove_l2dom + +- name: Remove l3dom + aci_domain_to_encap_pool: + <<: *aci_info + domain_type: l3dom + domain: l3_dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: static + state: absent + register: remove_l3dom + +- name: Remove vmm cloudfoundry + aci_domain_to_encap_pool: + <<: *aci_info + domain_type: vmm + domain: vmm_dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: cloudfoundry + state: absent + register: remove_vmm_cloudfoundry + +- name: Remove vmm redhat kubernetes + aci_domain_to_encap_pool: + <<: *aci_info + domain_type: vmm + domain: vmm_dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: kubernetes + state: absent + register: remove_vmm_kubernetes + +- name: Remove vmm microsoft + aci_domain_to_encap_pool: + <<: *aci_info + domain_type: vmm + domain: vmm_dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: microsoft + state: absent + register: remove_vmm_microsoft + +- name: Remove vmm redhat + aci_domain_to_encap_pool: + <<: *aci_info + domain_type: vmm + domain: vmm_dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: redhat + state: absent + register: remove_vmm_redhat + +- name: Remove vmm openstack + aci_domain_to_encap_pool: + <<: *aci_info + domain_type: vmm + domain: vmm_dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: openstack + state: absent + register: remove_vmm_openstack + +- name: Remove vmm openshift + aci_domain_to_encap_pool: + <<: *aci_info + domain_type: vmm + domain: vmm_dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: openshift + state: absent + register: remove_vmm_openshift + +- name: Remove vmm vmware + aci_domain_to_encap_pool: + <<: *aci_info + domain_type: vmm + domain: vmm_dom + pool: test_pool + pool_type: vlan + pool_allocation_mode: dynamic + vm_provider: vmware + state: absent + register: remove_vmm_vmware + +- name: Verify Remove + assert: + that: + - remove_phys.previous.0.physDomP.children.0.infraRsVlanNs.attributes.tDn =='uni/infra/vlanns-[test_pool]-dynamic' + - remove_fc.previous.0.fcDomP.children.0.fcRsVsanNs.attributes.tDn == 'uni/infra/vsanns-[test_pool]-dynamic' + - remove_l2dom.previous.0.l2extDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-static' + - remove_l3dom.previous.0.l3extDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-static' + - remove_vmm_cloudfoundry.previous.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - remove_vmm_kubernetes.previous.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - remove_vmm_microsoft.previous.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - remove_vmm_openshift.previous.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - remove_vmm_openstack.previous.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - remove_vmm_redhat.previous.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - remove_vmm_vmware.previous.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + +# Clean Environment Again +- name: Remove domains + aci_domain: + <<: *aci_info + domain: '{{ item.name }}' + domain_type: '{{ item.type }}' + state: absent + loop: + - { name: phys_dom, type: phys } + - { name: fc_dom, type: fc } + - { name: l2_dom, type: l2dom } + - { name: l3_dom, type: l3dom } + +- name: Remove domains + aci_domain: + <<: *aci_info + domain: vmm_dom + domain_type: vmm + vm_provider: '{{ item.name }}' + state: absent + loop: + - { name: vmware } + - { name: microsoft } + - { name: cloudfoundry } + - { name: openshift } + - { name: openstack } + - { name: redhat } + - { name: kubernetes }
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain_to_vlan_pool/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain_to_vlan_pool/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain_to_vlan_pool/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain_to_vlan_pool/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain_to_vlan_pool/tasks/main.yml new file mode 100644 index 00000000..2fb30c47 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_domain_to_vlan_pool/tasks/main.yml @@ -0,0 +1,222 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# CLEAN ENVIRONMENT +- name: Remove domain to VLAN pool binding + cisco.aci.aci_domain_to_vlan_pool: &binding_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: phys_dom + domain_type: phys + pool: test_pool + pool_allocation_mode: dynamic + state: absent + +- name: Remove physical domain + cisco.aci.aci_domain: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: phys_dom + domain_type: phys + state: absent + +- name: Create VLAN pool + cisco.aci.aci_vlan_pool: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + pool: test_pool + pool_allocation_mode: dynamic + description: Test VLAN pool + state: present + + +# ADD BINDING +- name: Add domain to VLAN pool binding (check_mode) + cisco.aci.aci_domain_to_vlan_pool: &binding_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: phys_dom + domain_type: phys + pool: test_pool + pool_allocation_mode: dynamic + state: present + check_mode: yes + register: cm_add_binding + +- name: Add domain to VLAN pool binding (normal mode) + cisco.aci.aci_domain_to_vlan_pool: *binding_present + register: nm_add_binding + +- name: Verify add_binding + assert: + that: + - cm_add_binding is changed + - nm_add_binding is changed + - cm_add_binding.sent.physDomP.attributes.name == nm_add_binding.sent.physDomP.attributes.name == 'phys_dom' + - cm_add_binding.sent.physDomP.children.0.infraRsVlanNs.attributes.tDn == nm_add_binding.sent.physDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - cm_add_binding.proposed.physDomP.attributes.name == nm_add_binding.proposed.physDomP.attributes.name == 'phys_dom' + - cm_add_binding.proposed.physDomP.children.0.infraRsVlanNs.attributes.tDn == nm_add_binding.proposed.physDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - cm_add_binding.current == cm_add_binding.previous == nm_add_binding.previous == [] + - nm_add_binding.current.0.physDomP.attributes.name == 'phys_dom' + - nm_add_binding.current.0.physDomP.attributes.dn == 'uni/phys-phys_dom' + - nm_add_binding.current.0.physDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + +- name: Add domain to VLAN pool binding again (check_mode) + cisco.aci.aci_domain_to_vlan_pool: *binding_present + check_mode: yes + register: cm_add_binding_again + +- name: Add domain to VLAN pool binding again (normal mode) + cisco.aci.aci_domain_to_vlan_pool: *binding_present + register: nm_add_binding_again + +- name: Verify add_binding_again + assert: + that: + - cm_add_binding_again is not changed + - nm_add_binding_again is not changed + + +# QUERY ALL BINDINGS +- name: Query all domain to VLAN pool bindings (check_mode) + cisco.aci.aci_domain_to_vlan_pool: &binding_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain_type: phys + pool_allocation_mode: dynamic + state: query + check_mode: yes + register: cm_query_all_bindings + +- name: Query all domain to VLAN pool bindings (normal mode) + cisco.aci.aci_domain_to_vlan_pool: *binding_query + register: nm_query_all_bindings + +- name: Verify query_all_bindings + assert: + that: + - cm_query_all_bindings is not changed + - nm_query_all_bindings is not changed + - cm_query_all_bindings == nm_query_all_bindings + - nm_query_all_bindings.current|length >= 1 + + +# QUERY A BINDING +- name: Query our domain to VLAN pool binding (check_mode) + cisco.aci.aci_domain_to_vlan_pool: + <<: *binding_query + domain: phys_dom + pool: test_pool + pool_allocation_mode: dynamic + check_mode: yes + register: cm_query_binding + +- name: Query our domain to VLAN pool binding (normal mode) + cisco.aci.aci_domain_to_vlan_pool: + <<: *binding_query + domain: phys_dom + pool: test_pool + pool_allocation_mode: dynamic + register: nm_query_binding + +- name: Verify query_binding + assert: + that: + - cm_query_binding is not changed + - nm_query_binding is not changed + - cm_query_binding == nm_query_binding + - nm_query_binding.current.0.physDomP.attributes.dn == 'uni/phys-phys_dom' + - nm_query_binding.current.0.physDomP.attributes.name == 'phys_dom' + - nm_query_binding.current.0.physDomP.children.0.infraRsVlanNs.attributes.tCl == 'fvnsVlanInstP' + - nm_query_binding.current.0.physDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + + +# REMOVE BINDING +- name: Remove domain to VLAN pool binding (check_mode) + cisco.aci.aci_domain_to_vlan_pool: *binding_absent + check_mode: yes + register: cm_remove_binding + +- name: Remove domain to VLAN pool binding (normal mode) + cisco.aci.aci_domain_to_vlan_pool: *binding_absent + register: nm_remove_binding + +- name: Verify remove_binding + assert: + that: + - cm_remove_binding is changed + - nm_remove_binding is changed + - cm_remove_binding.current.0.physDomP.attributes.dn == cm_remove_binding.previous.0.physDomP.attributes.dn == nm_remove_binding.previous.0.physDomP.attributes.dn == 'uni/phys-phys_dom' + - cm_remove_binding.current.0.physDomP.attributes.name == cm_remove_binding.previous.0.physDomP.attributes.name == nm_remove_binding.previous.0.physDomP.attributes.name == 'phys_dom' + - cm_remove_binding.current.0.physDomP.children.0.infraRsVlanNs.attributes.tDn == cm_remove_binding.previous.0.physDomP.children.0.infraRsVlanNs.attributes.tDn == nm_remove_binding.previous.0.physDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic' + - nm_remove_binding.current == [] + +- name: Remove domain to VLAN pool binding again (check_mode) + cisco.aci.aci_domain_to_vlan_pool: *binding_absent + check_mode: yes + register: cm_remove_binding_again + +- name: Remove domain to VLAN pool binding again (normal mode) + cisco.aci.aci_domain_to_vlan_pool: *binding_absent + register: nm_remove_binding_again + +- name: Verify remove_binding_again + assert: + that: + - cm_remove_binding_again is not changed + - nm_remove_binding_again is not changed + + +# QUERY NON-EXISTING BINDING +- name: Query non-existing domain to VLAN pool binding (check_mode) + cisco.aci.aci_domain_to_vlan_pool: + <<: *binding_query + domain: phys_dom + pool: test_pool + pool_allocation_mode: dynamic + check_mode: yes + register: cm_query_non_binding + +- name: Query non-existing domain to VLAN pool binding (normal mode) + cisco.aci.aci_domain_to_vlan_pool: + <<: *binding_query + domain: phys_dom + pool: test_pool + pool_allocation_mode: dynamic + register: nm_query_non_binding + +- name: Verify query_non_binding + assert: + that: + - cm_query_non_binding is not changed + - nm_query_non_binding is not changed + - cm_query_non_binding == nm_query_non_binding + - nm_query_non_binding.current == [] diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/tasks/main.yml new file mode 100644 index 00000000..f33bfa00 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/tasks/main.yml @@ -0,0 +1,18 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: test that we have an aci apic host, aci username and aci password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- include_tasks: vlan.yml + when: vlan is not defined or vlan + +- include_tasks: vxlan.yml + when: vxlan is not defined or vxlan + +- include_tasks: vsan.yml + when: vsan is not defined or vsan diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/tasks/vlan.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/tasks/vlan.yml new file mode 100644 index 00000000..7bd4bfd7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/tasks/vlan.yml @@ -0,0 +1,274 @@ +--- +- name: ensure vlan pool does not exist for tests to kick off + cisco.aci.aci_encap_pool: &aci_pool_absent_static + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: absent + pool: anstest + pool_type: vlan + pool_allocation_mode: static + +- name: ensure vlan pool does not exist for tests to kick off + cisco.aci.aci_encap_pool: &aci_pool_absent_dynamic + <<: *aci_pool_absent_static + pool_allocation_mode: dynamic + +- name: create static vlan pool - check mode works + cisco.aci.aci_encap_pool: &aci_pool_present_static + <<: *aci_pool_absent_static + state: present + descr: Ansible Test + check_mode: yes + register: create_check_mode + +- name: assertion test - present + assert: + that: + - create_check_mode is changed + - create_check_mode.sent.fvnsVlanInstP.attributes.allocMode == 'static' + - create_check_mode.sent.fvnsVlanInstP.attributes.descr == 'Ansible Test' + - create_check_mode.sent.fvnsVlanInstP.attributes.name == 'anstest' + +- name: create static vlan pool - creation works + cisco.aci.aci_encap_pool: + <<: *aci_pool_present_static + register: create_static + +- name: assertion test - present + assert: + that: + - create_static is changed + - create_static.previous == [] + - create_static.sent == create_check_mode.sent + +- name: create dynamic vlan pool - creation works + cisco.aci.aci_encap_pool: &aci_pool_present_dynamic + <<: *aci_pool_absent_dynamic + state: present + descr: Ansible Test + register: create_dynamic + +- name: assertion test - present + assert: + that: + - create_dynamic is changed + - create_dynamic.previous == [] + - create_dynamic.sent.fvnsVlanInstP.attributes.allocMode == 'dynamic' + - create_dynamic.sent.fvnsVlanInstP.attributes.descr == 'Ansible Test' + - create_dynamic.sent.fvnsVlanInstP.attributes.name == 'anstest' + +- name: create static vlan pool again - idempotency works + cisco.aci.aci_encap_pool: + <<: *aci_pool_present_static + register: idempotent_static + +- name: assertion test - present + assert: + that: + - idempotent_static is not changed + - idempotent_static.previous.0.fvnsVlanInstP.attributes.allocMode == 'static' + - idempotent_static.previous.0.fvnsVlanInstP.attributes.descr == 'Ansible Test' + - idempotent_static.previous.0.fvnsVlanInstP.attributes.dn == 'uni/infra/vlanns-[anstest]-static' + - idempotent_static.previous.0.fvnsVlanInstP.attributes.name == 'anstest' + - idempotent_static.sent == {} + +- name: create dynamic vlan pool again - idempotency works + cisco.aci.aci_encap_pool: + <<: *aci_pool_present_dynamic + register: idempotent_dynamic + +- name: assertion test - present + assert: + that: + - idempotent_dynamic is not changed + - idempotent_dynamic.previous.0.fvnsVlanInstP.attributes.allocMode == 'dynamic' + - idempotent_dynamic.previous.0.fvnsVlanInstP.attributes.descr == 'Ansible Test' + - idempotent_dynamic.previous.0.fvnsVlanInstP.attributes.dn == 'uni/infra/vlanns-[anstest]-dynamic' + - idempotent_dynamic.previous.0.fvnsVlanInstP.attributes.name == 'anstest' + - idempotent_dynamic.sent == {} + +- name: update static vlan pool - update works + cisco.aci.aci_encap_pool: + <<: *aci_pool_present_static + descr: Ansible Test Change + register: update_static + +- name: assertion test - present + assert: + that: + - update_static is changed + - update_static.sent.fvnsVlanInstP.attributes.descr == 'Ansible Test Change' + +- name: update dynamic vlan pool - update works + cisco.aci.aci_encap_pool: + <<: *aci_pool_present_dynamic + descr: Ansible Test Change + register: update_dynamic + +- name: assertion test - present + assert: + that: + - update_dynamic is changed + - update_dynamic.sent.fvnsVlanInstP.attributes.descr == 'Ansible Test Change' + +- name: missing param - failure message works + cisco.aci.aci_encap_pool: + <<: *aci_pool_present_dynamic + pool_allocation_mode: "{{ fake_var | default(omit) }}" + ignore_errors: yes + register: vlan_alloc_fail + +- name: assertion test - present + assert: + that: + - vlan_alloc_fail is failed + - "vlan_alloc_fail.msg == 'ACI requires parameter \\'pool_allocation_mode\\' for \\'pool_type\\' of \\'vlan\\' and \\'vsan\\' when parameter \\'pool\\' is provided'" + +- name: missing param - failure message works + cisco.aci.aci_encap_pool: + <<: *aci_pool_present_dynamic + pool: "{{ fake_var | default(omit) }}" + ignore_errors: yes + register: vlan_pool_fail + +- name: assertion test - present + assert: + that: + - vlan_pool_fail is failed + - 'vlan_pool_fail.msg == "state is present but all of the following are missing: pool"' + +- name: missing param - failure message works + cisco.aci.aci_encap_pool: + <<: *aci_pool_present_dynamic + pool_type: "{{ fake_var | default(omit) }}" + ignore_errors: yes + register: vlan_pool_type_fail + +- name: assertion test - present + assert: + that: + - vlan_pool_type_fail is failed + - 'vlan_pool_type_fail.msg == "missing required arguments: pool_type"' + +- name: get all vlan pools - get class works + cisco.aci.aci_encap_pool: + <<: *aci_pool_absent_static + state: query + pool: "{{ fake_var | default(omit) }}" + pool_allocation_mode: "{{ fake_var | default(omit) }}" + register: get_all_pools + +- name: assertion test - query + assert: + that: + - get_all_pools is not changed + - get_all_pools.method == "GET" + - get_all_pools.current | length > 1 + +- name: get created static vlan pool - get mo works + cisco.aci.aci_encap_pool: + <<: *aci_pool_absent_static + state: query + register: get_static_pool + +- name: assertion test - query + assert: + that: + - get_static_pool is not changed + - get_static_pool.method == "GET" + - get_static_pool.current | length == 1 + - get_static_pool.current.0.fvnsVlanInstP.attributes.allocMode == "static" + - get_static_pool.current.0.fvnsVlanInstP.attributes.name == "anstest" + +- name: get created dynamic vlan pool - get mo works + cisco.aci.aci_encap_pool: + <<: *aci_pool_absent_dynamic + state: query + register: get_dynamic_pool + +- name: assertion test - query + assert: + that: + - get_dynamic_pool is not changed + - get_dynamic_pool.method == "GET" + - get_dynamic_pool.current | length == 1 + - get_dynamic_pool.current.0.fvnsVlanInstP.attributes.allocMode == "dynamic" + - get_dynamic_pool.current.0.fvnsVlanInstP.attributes.name == "anstest" + +- name: get created dynamic vlan pool - get mo works + cisco.aci.aci_encap_pool: + <<: *aci_pool_absent_dynamic + state: query + pool_type: "{{ fake_var | default(omit) }}" + ignore_errors: yes + register: vlan_query_pool_type_fail + +- name: assertion test - query + assert: + that: + - vlan_query_pool_type_fail is failed + - 'vlan_query_pool_type_fail.msg == "missing required arguments: pool_type"' + +- name: delete static vlan pool - deletion works + cisco.aci.aci_encap_pool: + <<: *aci_pool_absent_static + register: delete_static + +- name: assertion test - absent + assert: + that: + - delete_static is changed + - delete_static.method == "DELETE" + - delete_static.previous.0.fvnsVlanInstP.attributes.allocMode == "static" + - delete_static.previous.0.fvnsVlanInstP.attributes.name == "anstest" + +- name: delete dynamic vlan pool - check mode works + cisco.aci.aci_encap_pool: + <<: *aci_pool_absent_dynamic + check_mode: yes + register: delete_check_mode + +- name: assertion test - absent + assert: + that: + - delete_check_mode is changed + +- name: delete dynamic vlan pool - deletion works + cisco.aci.aci_encap_pool: + <<: *aci_pool_absent_dynamic + register: delete_dynamic + +- name: assertion test - absent + assert: + that: + - delete_dynamic is changed + - delete_dynamic.method == "DELETE" + - delete_dynamic.previous.0.fvnsVlanInstP.attributes.allocMode == "dynamic" + - delete_dynamic.previous.0.fvnsVlanInstP.attributes.name == "anstest" + +- name: delete static vlan pool again - idempotency works + cisco.aci.aci_encap_pool: + <<: *aci_pool_absent_static + register: idempotent_delete_static + +- name: assertion test - absent + assert: + that: + - idempotent_delete_static is not changed + - idempotent_delete_static.previous == [] + +- name: delete dynamic vlan pool again - idempotency works + cisco.aci.aci_encap_pool: + <<: *aci_pool_absent_dynamic + register: idempotent_delete_dynamic + +- name: assertion test - absent + assert: + that: + - idempotent_delete_dynamic is not changed + - idempotent_delete_dynamic.previous == [] diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/tasks/vsan.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/tasks/vsan.yml new file mode 100644 index 00000000..84681873 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/tasks/vsan.yml @@ -0,0 +1,16 @@ +--- +- name: ensure vlan pool does not exist for tests to kick off + cisco.aci.aci_encap_pool: &aci_pool_absent_static + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + pool: anstest + pool_type: vsan + pool_allocation_mode: static + state: absent + +# FIXME: Add Test to this integration
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/tasks/vxlan.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/tasks/vxlan.yml new file mode 100644 index 00000000..10bd039b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool/tasks/vxlan.yml @@ -0,0 +1,178 @@ +--- +- name: ensure vxlan pool anstest does not exist for tests to kick off + cisco.aci.aci_encap_pool: &aci_vxlan_absent + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: absent + pool: anstest + pool_type: vxlan + +- name: ensure vxlan pool anstest_2 does not exist for tests to kick off + cisco.aci.aci_encap_pool: + <<: *aci_vxlan_absent + pool: anstest_2 + +- name: ensure vxlan pool anstest_3 does not exist for tests to kick off + cisco.aci.aci_encap_pool: + <<: *aci_vxlan_absent + pool: anstest_3 + +- name: create vxlan pool - check mode works + cisco.aci.aci_encap_pool: &aci_vxlan_present + <<: *aci_vxlan_absent + state: present + descr: Ansible Test + check_mode: yes + register: create_vxlan_check_mode + +- name: assertion test - present + assert: + that: + - create_vxlan_check_mode is changed + - create_vxlan_check_mode.sent.fvnsVxlanInstP.attributes.descr == 'Ansible Test' + - create_vxlan_check_mode.sent.fvnsVxlanInstP.attributes.name == 'anstest' + +- name: create vxlan pool - creation works + cisco.aci.aci_encap_pool: + <<: *aci_vxlan_present + register: create_vxlan + +- name: assertion test - present + assert: + that: + - create_vxlan is changed + - create_vxlan.previous == [] + - create_vxlan.sent == create_vxlan_check_mode.sent + +- name: create vxlan pool again - idempotency works + cisco.aci.aci_encap_pool: + <<: *aci_vxlan_present + register: idempotent_vxlan + +- name: assertion test - present + assert: + that: + - idempotent_vxlan is not changed + - idempotent_vxlan.previous.0.fvnsVxlanInstP.attributes.name == 'anstest' + - idempotent_vxlan.sent == {} + +- name: update vxlan pool - update works + cisco.aci.aci_encap_pool: + <<: *aci_vxlan_present + descr: Ansible Test Change + register: update_vxlan + +- name: assertion test - present + assert: + that: + - update_vxlan is changed + - update_vxlan.sent.fvnsVxlanInstP.attributes.descr == 'Ansible Test Change' + +- name: create vxlan pool - used for query + cisco.aci.aci_encap_pool: + <<: *aci_vxlan_present + name: anstest_2 + register: create_vxlan_2 + +- name: assertion test - present + assert: + that: + - create_vxlan_2 is changed + +- name: create vxlan pool with pool allocation mode - failure message works + cisco.aci.aci_encap_pool: + <<: *aci_vxlan_present + name: anstest_3 + pool_allocation_mode: dynamic + ignore_errors: yes + register: create_vxlan_alloc_mode + +- name: assertion test - present + assert: + that: + - create_vxlan_alloc_mode is failed + - "create_vxlan_alloc_mode.msg == 'vxlan pools do not support setting the \\'pool_allocation_mode\\'; please remove this parameter from the task'" + +- name: get vxlan pool - get object works + cisco.aci.aci_encap_pool: &aci_vxlan_query + <<: *aci_vxlan_present + state: query + register: query_vxlan + +- name: assertion test - query + assert: + that: + - query_vxlan is not changed + - query_vxlan.current | length == 1 + - '"infra/vxlanns-anstest.json" in query_vxlan.url' + +- name: get created static vlan pool - get class works + cisco.aci.aci_encap_pool: + <<: *aci_vxlan_query + pool: "{{ fake_var | default(omit) }}" + register: query_vxlan_all + +- name: assertion test - query + assert: + that: + - query_vxlan_all is not changed + - query_vxlan_all.current | length > 1 + - '"class/fvnsVxlanInstP.json" in query_vxlan_all.url' + +- name: delete vxlan pool - check mode works + cisco.aci.aci_encap_pool: + <<: *aci_vxlan_absent + check_mode: yes + register: delete_vxlan_check_mode + +- name: assertion test - absent + assert: + that: + - delete_vxlan_check_mode is changed + - delete_vxlan_check_mode.previous != [] + +- name: delete vxlan pool - deletion works + cisco.aci.aci_encap_pool: + <<: *aci_vxlan_absent + register: delete_vxlan + +- name: assertion test - absent + assert: + that: + - delete_vxlan is changed + - delete_vxlan.previous == delete_vxlan_check_mode.previous + - delete_vxlan.previous.0.fvnsVxlanInstP.attributes.name == "anstest" + +- name: delete vxlan pool again - idempotency works + cisco.aci.aci_encap_pool: + <<: *aci_vxlan_absent + register: delete_vxlan_idempotent + +- name: missing param - failure message works + cisco.aci.aci_encap_pool: + <<: *aci_vxlan_absent + pool: "{{ fake_var | default(omit) }}" + ignore_errors: yes + register: delete_vxlan_pool_fail + +- name: assertion test - absent + assert: + that: + - delete_vxlan_idempotent is not changed + - delete_vxlan_idempotent.previous == [] + +- name: delete vxlan pool - cleanup + cisco.aci.aci_encap_pool: + <<: *aci_vxlan_absent + pool: anstest_2 + +- name: assertion test - absent + assert: + that: + - delete_vxlan_pool_fail is failed + - 'delete_vxlan_pool_fail.msg == "state is absent but all of the following are missing: pool"' diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/tasks/main.yml new file mode 100644 index 00000000..66caef7e --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/tasks/main.yml @@ -0,0 +1,18 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: test that we have an aci apic host, aci username and aci password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- include_tasks: vlan.yml + when: "vlan is not defined or (vlan is defined and vlan == 'True')" + +- include_tasks: vxlan.yml + when: "vxlan is not defined or (vxlan is defined and vxlan == 'True')" + +- include_tasks: vsan.yml + when: "vsan is not defined or (vsan is defined and vsan == 'True')" diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/tasks/vlan.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/tasks/vlan.yml new file mode 100644 index 00000000..04a4f43b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/tasks/vlan.yml @@ -0,0 +1,387 @@ +- name: ensure vlan pool exists for tests to kick off + cisco.aci.aci_encap_pool: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: absent + pool: anstest + pool_type: vlan + allocation_mode: static + description: Ansible Test + +- name: ensure vlan pool exists for tests to kick off + cisco.aci.aci_encap_pool: &aci_pool_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: present + pool: anstest + pool_type: vlan + allocation_mode: static + description: Ansible Test + register: pool_present + +- name: create vlan pool range - check mode works + cisco.aci.aci_encap_pool_range: &aci_range_present + <<: *aci_pool_present + range_name: anstest + range_start: 20 + range_end: 40 + pool: anstest + pool_allocation_mode: static + allocation_mode: inherit + description: Ansible Test + check_mode: yes + register: range_present_check_mode + +- name: present assertions + assert: + that: + - range_present_check_mode is changed + - range_present_check_mode.sent.fvnsEncapBlk.attributes.allocMode == 'inherit' + - range_present_check_mode.sent.fvnsEncapBlk.attributes.descr == 'Ansible Test' + - range_present_check_mode.sent.fvnsEncapBlk.attributes.name == 'anstest' + - range_present_check_mode.sent.fvnsEncapBlk.attributes.from == 'vlan-20' + - range_present_check_mode.sent.fvnsEncapBlk.attributes.to == 'vlan-40' + +- name: create vlan pool range - creation works + cisco.aci.aci_encap_pool_range: + <<: *aci_range_present + register: range_present + +- name: present assertions + assert: + that: + - range_present is changed + - range_present.previous == [] + - range_present.sent == range_present_check_mode.sent + - range_present.sent == range_present.proposed + +- name: create vlan pool range - idempotency works + cisco.aci.aci_encap_pool_range: + <<: *aci_range_present + register: range_present_idempotent + +- name: present assertions + assert: + that: + - range_present_idempotent is not changed + - range_present_idempotent.previous.0.fvnsEncapBlk.attributes.name == "anstest" + +- name: update vlan pool range - update works + cisco.aci.aci_encap_pool_range: + <<: *aci_range_present + description: Ansible Test Update + allocation_mode: inherit + register: range_present_update + +- name: present assertions + assert: + that: + - range_present_update is changed + - range_present_update.previous != [] + - range_present_update.sent != range_present.sent + +- name: create vlan pool range - used for query + cisco.aci.aci_encap_pool_range: &aci_range_present_2 + <<: *aci_range_present + range_name: anstest_2 + range_start: 50 + range_end: 55 + register: range_present_2 + +- name: present assertions + assert: + that: + - range_present_2 is changed + - range_present_2.previous == [] + +- name: invalid range_start - error message works + cisco.aci.aci_encap_pool_range: + <<: *aci_range_present + range_start: 0 + ignore_errors: yes + register: range_start_low + +- name: present assertions + assert: + that: + - range_start_low is failed + - range_start_low.msg == 'vlan pools must have "range_start" and "range_end" values between 1 and 4094' + +- name: invalid range_start - error message works + cisco.aci.aci_encap_pool_range: + <<: *aci_range_present + range_start: 4096 + ignore_errors: yes + register: range_start_high + +- name: present assertions + assert: + that: + - range_start_high is failed + - range_start_high.msg == 'vlan pools must have "range_start" and "range_end" values between 1 and 4094' + +- name: invalid range_end - error message works + cisco.aci.aci_encap_pool_range: + <<: *aci_range_present + range_end: 0 + ignore_errors: yes + register: range_end_low + +- name: present assertions + assert: + that: + - range_end_low is failed + - range_end_low.msg == 'vlan pools must have "range_start" and "range_end" values between 1 and 4094' + +- name: invalid range_end - error message works + cisco.aci.aci_encap_pool_range: + <<: *aci_range_present + range_end: 4096 + ignore_errors: yes + register: range_end_high + +- name: present assertions + assert: + that: + - range_end_high is failed + - range_end_high.msg == 'vlan pools must have "range_start" and "range_end" values between 1 and 4094' + +- name: range start higher than range end - error message works + cisco.aci.aci_encap_pool_range: + <<: *aci_range_present + range_start: 1000 + ignore_errors: yes + register: range_start_end + +- name: present assertions + assert: + that: + - range_start_end is failed + - range_start_end.msg == 'The "range_start" must be less than or equal to the "range_end"' + +- name: missing required param - error message works + cisco.aci.aci_encap_pool_range: + <<: *aci_range_present + pool_type: '{{ omit }}' + ignore_errors: yes + register: range_present_pool_type + +- name: present assertions + assert: + that: + - range_present_pool_type is failed + - "range_present_pool_type.msg == 'missing required arguments: pool_type'" + +- name: missing required param - error message works + cisco.aci.aci_encap_pool_range: + <<: *aci_pool_present + ignore_errors: yes + register: range_present_missing_param + +- name: present assertions + assert: + that: + - range_present_missing_param is failed + - "range_present_missing_param.msg == 'state is present but all of the following are missing: range_end, range_name, range_start'" + +- name: missing required param - error message works + cisco.aci.aci_encap_pool_range: + <<: *aci_range_present + pool_allocation_mode: '{{ omit }}' + ignore_errors: yes + register: range_present_allocation + +- name: present assertions + assert: + that: + - range_present_allocation is failed + - range_present_allocation.msg == 'ACI requires the "pool_allocation_mode" for "pool_type" of "vlan" and "vsan" when the "pool" is provided' + +- name: query specific vlan pool range + cisco.aci.aci_encap_pool_range: &aci_range_query + <<: *aci_range_present + state: query + register: range_query + +- name: query assertions + assert: + that: + - range_query is not changed + - range_query.url.endswith("infra/vlanns-[anstest]-static/from-[vlan-20]-to-[vlan-40].json") + - range_query.current | length == 1 + - range_query.current.0.fvnsEncapBlk.attributes.name == "anstest" + +- name: query vlan pool range - from, to, and name are filtered + cisco.aci.aci_encap_pool_range: &aci_range_query_filter + <<: *aci_range_query + pool: '{{ omit }}' + register: range_query_from_to_name + +- name: query assertions + assert: + that: + - range_query_from_to_name is not changed + - range_query_from_to_name.url.endswith("class/fvnsEncapBlk.json") + - '"eq(fvnsEncapBlk.from,\"vlan-20\")" in range_query_from_to_name.filter_string' + - '"eq(fvnsEncapBlk.name,\"anstest\")" in range_query_from_to_name.filter_string' + - '"eq(fvnsEncapBlk.to,\"vlan-40\")" in range_query_from_to_name.filter_string' + - range_query_from_to_name.current.0.fvnsEncapBlk.attributes.name == "anstest" + - range_query_from_to_name.current.0.fvnsEncapBlk.attributes.from == "vlan-20" + - range_query_from_to_name.current.0.fvnsEncapBlk.attributes.to == "vlan-40" + +- name: query vlan pool range - from and name are filtered + cisco.aci.aci_encap_pool_range: + <<: *aci_range_query_filter + range_end: '{{ omit }}' + register: range_query_from_name + +- name: query assertions + assert: + that: + - range_query_from_name is not changed + - range_query_from_name.url.endswith("class/fvnsEncapBlk.json") + - '"eq(fvnsEncapBlk.from,\"vlan-20\")" in range_query_from_name.filter_string' + - '"eq(fvnsEncapBlk.name,\"anstest\")" in range_query_from_name.filter_string' + - range_query_from_name.current.0.fvnsEncapBlk.attributes.name == "anstest" + - range_query_from_name.current.0.fvnsEncapBlk.attributes.from == "vlan-20" + +- name: query vlan pool range - to and name are filtered + cisco.aci.aci_encap_pool_range: + <<: *aci_range_query_filter + range_start: '{{ omit }}' + register: range_query_to_name + +- name: query assertions + assert: + that: + - range_query_to_name is not changed + - range_query_to_name.url.endswith('class/fvnsEncapBlk.json') + - '"eq(fvnsEncapBlk.name,\"anstest\")" in range_query_to_name.filter_string' + - '"eq(fvnsEncapBlk.to,\"vlan-40\")" in range_query_to_name.filter_string' + - range_query_to_name.current.0.fvnsEncapBlk.attributes.name == "anstest" + - range_query_to_name.current.0.fvnsEncapBlk.attributes.to == "vlan-40" + +- name: query vlan pool range - name is filtered + cisco.aci.aci_encap_pool_range: + <<: *aci_range_query_filter + range_start: '{{ omit }}' + range_end: '{{ omit }}' + register: range_query_name + +- name: query assertions + assert: + that: + - range_query_name is not changed + - range_query_name.url.endswith("class/fvnsEncapBlk.json") + - '"eq(fvnsEncapBlk.name,\"anstest\")" in range_query_name.filter_string' + - range_query_name.current.0.fvnsEncapBlk.attributes.name == "anstest" + +- name: query vlan pool range - from and to are filtered + cisco.aci.aci_encap_pool_range: + <<: *aci_range_query_filter + range_name: '{{ omit }}' + register: range_query_from_to + +- name: query assertions + assert: + that: + - range_query_from_to is not changed + - range_query_from_to.url.endswith("class/fvnsEncapBlk.json") + - '"eq(fvnsEncapBlk.from,\"vlan-20\")" in range_query_from_to.filter_string' + - '"eq(fvnsEncapBlk.to,\"vlan-40\")" in range_query_from_to.filter_string' + - range_query_from_to.current.0.fvnsEncapBlk.attributes.from == "vlan-20" + - range_query_from_to.current.0.fvnsEncapBlk.attributes.to == "vlan-40" + +- name: query all ranges in a vlan pool + cisco.aci.aci_encap_pool_range: + <<: *aci_pool_present + state: query + pool_allocation_mode: static + register: range_query_pool + +- name: query assertions + assert: + that: + - range_query_pool.current | length == 1 + - range_query_pool.current.0.fvnsVlanInstP.attributes.name == "anstest" + - range_query_pool.current.0.fvnsVlanInstP.children | length > 1 + - range_query_pool.url.endswith("infra/vlanns-[anstest]-static.json") + +- name: query all ranges + cisco.aci.aci_encap_pool_range: + <<: *aci_pool_present + state: query + pool: '{{ omit }}' + register: range_query_all + +- name: query assertions + assert: + that: + - range_query_all is not changed + - range_query_all.current | length > 1 + - range_query_all.current.0.fvnsEncapBlk is defined + - range_query_all.url.endswith("class/fvnsEncapBlk.json") + +- name: delete vlan pool range - deletion works + cisco.aci.aci_encap_pool_range: + <<: *aci_range_present + state: absent + register: delete_range + +- name: absent assertions + assert: + that: + - delete_range is changed + - delete_range.proposed == {} + - delete_range.previous.0.fvnsEncapBlk.attributes.name == "anstest" + +- name: delete vlan pool range - check mode works + cisco.aci.aci_encap_pool_range: &aci_range_absent + <<: *aci_range_present_2 + state: absent + check_mode: yes + register: delete_check_mode + +- name: absent assertions + assert: + that: + - delete_check_mode is changed + - delete_check_mode.previous != [] + +- name: delete vlan pool range - deletion works + cisco.aci.aci_encap_pool_range: + <<: *aci_range_absent + register: delete_range_2 + +- name: absent assertions + assert: + that: + - delete_range_2 is changed + - delete_range_2.previous == delete_check_mode.previous + +- name: delete vlan pool range again - idempotency works + cisco.aci.aci_encap_pool_range: + <<: *aci_range_absent + register: delete_idempotent + +- name: absent assertions + assert: + that: + - delete_idempotent is not changed + - delete_idempotent.previous == [] + +- name: cleanup vlan pool + cisco.aci.aci_encap_pool: + <<: *aci_pool_present + state: absent + when: pool_present is changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/tasks/vsan.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/tasks/vsan.yml new file mode 100644 index 00000000..7bdc332c --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/tasks/vsan.yml @@ -0,0 +1,20 @@ +- name: ensure vsan pool exists for tests to kick off + cisco.aci.aci_encap_pool: &aci_pool_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + pool: anstest + pool_type: vsan + allocation_mode: static + description: Ansible Test + state: present + +- name: cleanup vsan pool + cisco.aci.aci_encap_pool: + <<: *aci_pool_present + state: absent + when: pool_present is changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/tasks/vxlan.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/tasks/vxlan.yml new file mode 100644 index 00000000..e5e30191 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_encap_pool_range/tasks/vxlan.yml @@ -0,0 +1,19 @@ +- name: ensure vxlan pool exists for tests to kick off + cisco.aci.aci_encap_pool: &aci_pool_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + pool: anstest + pool_type: vxlan + description: Ansible Test + state: present + +- name: cleanup vxlan pool + cisco.aci.aci_encap_pool: + <<: *aci_pool_present + state: absent + when: pool_present is changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg/tasks/main.yml new file mode 100644 index 00000000..9291c18f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg/tasks/main.yml @@ -0,0 +1,187 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) +# Copyright: (c) 2020, Shreyas Srish (@shrsr) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: present + tenant: anstest + register: tenant_present + +- name: ensure monitoring policy exists + cisco.aci.aci_epg_monitoring_policy: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + monitoring_policy: check + tenant: anstest + +- name: ensure bd exists for tests to kick off + cisco.aci.aci_bd: &aci_bd_present + <<: *aci_tenant_present + bd: anstest + register: bd_present + +- name: ensure ap exists for tests to kick off + cisco.aci.aci_ap: &aci_ap_present + <<: *aci_tenant_present + ap: anstest + register: ap_present + +- name: create epg - check mode works + cisco.aci.aci_epg: &aci_epg_present + <<: *aci_ap_present + epg: anstest + bd: anstest + description: Ansible Test + monitoring_policy: check + check_mode: yes + register: epg_present_check_mode + +- name: create epg - creation works + cisco.aci.aci_epg: + <<: *aci_epg_present + register: epg_present + +- name: create epg - idempotency works + cisco.aci.aci_epg: + <<: *aci_epg_present + register: epg_present_idempotent + +- name: update epg - update works + cisco.aci.aci_epg: + <<: *aci_epg_present + description: Ansible Test Update + register: epg_present_update + +- name: create epg - missing param + cisco.aci.aci_epg: + <<: *aci_epg_present + ap: "{{ fakevar | default(omit) }}" + ignore_errors: yes + register: epg_present_missing_param + +- name: create epg - used for query + cisco.aci.aci_epg: + <<: *aci_epg_present + epg: anstest2 + +- name: present assertions + assert: + that: + - epg_present_check_mode is changed + - epg_present_check_mode.previous == [] + - epg_present_check_mode.sent.fvAEPg.attributes != {} + - epg_present_check_mode.sent.fvAEPg.children.0.fvRsBd.attributes.tnFvBDName == "anstest" + - epg_present_check_mode.sent.fvAEPg.children.1.fvRsAEPgMonPol.attributes.tnMonEPGPolName == 'check' + - epg_present is changed + - epg_present.sent == epg_present_check_mode.sent + - epg_present_idempotent is not changed + - epg_present_idempotent.sent == {} + - epg_present_update is changed + - epg_present_update.sent.fvAEPg.attributes.descr == 'Ansible Test Update' + - epg_present_missing_param is failed + - 'epg_present_missing_param.msg == "state is present but all of the following are missing: ap"' + +- name: get specific epg + cisco.aci.aci_epg: + <<: *aci_epg_present + state: query + register: epg_query + +- name: get all epgs + cisco.aci.aci_epg: + <<: *aci_tenant_present + state: query + tenant: "{{ fakevar | default(omit) }}" + register: epg_query_all + +- name: query assertions + assert: + that: + - epg_query is not changed + - epg_query.current | length == 1 + - epg_query.current.0.fvAEPg.attributes.name == "anstest" + - epg_query.current.0.fvAEPg.children.1.fvRsAEPgMonPol.attributes.tnMonEPGPolName == 'check' + - '"tn-anstest/ap-anstest/epg-anstest.json" in epg_query.url' + - epg_query_all is not changed + - epg_query_all.current | length > 1 + - '"?rsp-subtree=full&rsp-subtree-class=fvRsAEPgMonPol,fvRsBd" in epg_query_all.filter_string' + - '"class/fvAEPg.json" in epg_query_all.url' + +- name: delete epg - check mode works + cisco.aci.aci_epg: &aci_epg_absent + <<: *aci_epg_present + state: absent + check_mode: yes + register: delete_epg_check_mode + +- name: delete epg - delete works + cisco.aci.aci_epg: + <<: *aci_epg_absent + register: delete_epg + +- name: delete epg - idempotency works + cisco.aci.aci_epg: + <<: *aci_epg_absent + register: delete_epg_idempotent + +- name: delete epg - cleanup extra epg + cisco.aci.aci_epg: + <<: *aci_epg_absent + epg: anstest2 + +- name: delete epg - missing param fails + cisco.aci.aci_epg: + <<: *aci_epg_absent + tenant: "{{ fakevar | default(omit) }}" + ignore_errors: yes + register: delete_epg_missing_param + +- name: query assertions + assert: + that: + - delete_epg_check_mode is changed + - delete_epg_check_mode.previous != [] + - delete_epg is changed + - delete_epg.previous == delete_epg_check_mode.previous + - delete_epg_idempotent is not changed + - delete_epg_idempotent.previous == [] + - delete_epg_missing_param is failed + - 'delete_epg_missing_param.msg == "state is absent but all of the following are missing: tenant"' + +- name: cleanup bd + cisco.aci.aci_bd: + <<: *aci_bd_present + state: absent + when: bd_present.previous == [] + +- name: cleanup ap + cisco.aci.aci_ap: + <<: *aci_ap_present + state: absent + when: ap_present.previous == [] + +- name: cleanup tenant + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent + when: tenant_present.previous == [] diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_contract/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_contract/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_contract/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_contract/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_contract/tasks/main.yml new file mode 100644 index 00000000..8af8c4b1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_contract/tasks/main.yml @@ -0,0 +1,254 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Ensure contract binding does not exist prior to testing + cisco.aci.aci_epg_to_contract: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: anstest + ap: anstest + epg: anstest + contract_type: provider + contract: "anstest_http" + state: absent + +- name: ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + tenant: anstest + state: present + register: tenant_present + +- name: ensure contracts exist for tests to kick off + cisco.aci.aci_contract: + <<: *aci_tenant_present + contract: "{{ item }}" + with_items: ["anstest_http", "anstest_https", "anstest_db"] + +- name: ensure ap exists + cisco.aci.aci_ap: &aci_ap_present + <<: *aci_tenant_present + ap: anstest + register: ap_present + +- name: ensure epg exists + cisco.aci.aci_epg: &aci_epg_present + <<: *aci_ap_present + epg: anstest + register: epg_present + +- name: bind contract to epg - check mode works + cisco.aci.aci_epg_to_contract: &aci_epg_provide_present + <<: *aci_epg_present + contract_type: provider + contract: anstest_http + check_mode: yes + register: provide_present_check_mode + +- name: bind contract to epg - provide works + cisco.aci.aci_epg_to_contract: + <<: *aci_epg_provide_present + register: provide_present + +- name: bind contract to epg - consume works + cisco.aci.aci_epg_to_contract: &aci_epg_consume_present + <<: *aci_epg_provide_present + contract_type: consumer + contract: anstest_db + register: consume_present + +- name: bind contract to epg - add additional contract + cisco.aci.aci_epg_to_contract: &aci_epg_provide_present2 + <<: *aci_epg_provide_present + contract: anstest_https + provider_match: at_most_one + register: provide_present2 + +- name: bind contract to epg - idempotency works + cisco.aci.aci_epg_to_contract: + <<: *aci_epg_provide_present + register: idempotent_present + +- name: missing param - failure message works + cisco.aci.aci_epg_to_contract: + <<: *aci_tenant_present + contract_type: provider + ignore_errors: yes + register: missing_param_present + +- name: missing required param - failure message works + cisco.aci.aci_epg_to_contract: + <<: *aci_tenant_present + ignore_errors: yes + register: missing_required_present + +- name: incompatible param - failure message works + cisco.aci.aci_epg_to_contract: + <<: *aci_epg_consume_present + provider_match: all + ignore_errors: yes + register: incompatible_present + +- name: present assertions + assert: + that: + - provide_present_check_mode is changed + - provide_present_check_mode.sent.fvRsProv.attributes.tnVzBrCPName == 'anstest_http' + - provide_present is changed + - provide_present.sent == provide_present_check_mode.sent + - provide_present.previous == [] + - consume_present is changed + - consume_present.previous == [] + - consume_present.sent.fvRsCons.attributes.tnVzBrCPName == 'anstest_db' + - provide_present2 is changed + - provide_present2.previous == [] + - missing_param_present is failed + - 'missing_param_present.msg == "state is present but all of the following are missing: ap, contract, epg"' + - missing_required_present is failed + - 'missing_required_present.msg == "missing required arguments: contract_type"' + - incompatible_present is failed + - incompatible_present.msg == "the 'provider_match' is only configurable for Provided Contracts" + +- name: get binding + cisco.aci.aci_epg_to_contract: + <<: *aci_epg_provide_present2 + state: query + register: query_provide_contract + +- name: get binding + cisco.aci.aci_epg_to_contract: + <<: *aci_epg_consume_present + state: query + register: query_consume_contract + +- name: get all bindings + cisco.aci.aci_epg_to_contract: + <<: *aci_tenant_present + state: query + tenant: "{{ fakevar | default(omit) }}" + contract_type: provider + register: query_all + +- name: missing required param - failure message works + cisco.aci.aci_epg_to_contract: + <<: *aci_tenant_present + state: query + ignore_errors: yes + register: missing_required_query + +- name: query assertions + assert: + that: + - query_provide_contract is not changed + - query_provide_contract.current != [] + - '"uni/tn-anstest/ap-anstest/epg-anstest/rsprov-anstest_https.json" in query_provide_contract.url' + - query_consume_contract is not changed + - query_consume_contract.current != [] + - '"uni/tn-anstest/ap-anstest/epg-anstest/rscons-anstest_db.json" in query_consume_contract.url' + - query_all is not changed + - '"class/fvRsProv.json" in query_all.url' + - missing_required_query is failed + - 'missing_required_query.msg == "missing required arguments: contract_type"' + +- name: delete consume binding - check mode works + cisco.aci.aci_epg_to_contract: &aci_epg_consume_absent + <<: *aci_epg_consume_present + state: absent + check_mode: yes + register: consume_absent_check_mode + +- name: delete consume binding - deletion works + cisco.aci.aci_epg_to_contract: + <<: *aci_epg_consume_absent + register: consume_absent + +- name: delete provide binding - deletion works + cisco.aci.aci_epg_to_contract: + <<: *aci_epg_provide_present + state: absent + register: provide_absent + +- name: delete provide binding - deletion works + cisco.aci.aci_epg_to_contract: + <<: *aci_epg_provide_present2 + state: absent + register: provide_absent2 + +- name: delete consume binding - idempotency works + cisco.aci.aci_epg_to_contract: + <<: *aci_epg_consume_absent + register: consume_absent_idempotent + +- name: missing param - failure message works + cisco.aci.aci_epg_to_contract: + <<: *aci_epg_consume_absent + contract: "{{ fakevar | default(omit) }}" + ignore_errors: yes + register: missing_param_absent + +- name: missing required param - failure message works + cisco.aci.aci_epg_to_contract: + <<: *aci_epg_consume_absent + contract_type: "{{ fakevar | default(omit) }}" + ignore_errors: yes + register: missing_required_absent + +- name: absent assertions + assert: + that: + - consume_absent_check_mode is changed + - consume_absent_check_mode.previous.0.fvRsCons is defined + - consume_absent is changed + - consume_absent.previous == consume_absent_check_mode.previous + - provide_absent is changed + - provide_absent.previous.0.fvRsProv is defined + - provide_absent2 is changed + - consume_absent_idempotent is not changed + - consume_absent_idempotent.previous == [] + - missing_param_absent is failed + - 'missing_param_absent.msg == "state is absent but all of the following are missing: contract"' + - missing_required_absent is failed + - 'missing_required_absent.msg == "missing required arguments: contract_type"' + +- name: cleanup contracts + cisco.aci.aci_contract: + <<: *aci_tenant_present + state: absent + contract: "{{ item }}" + with_items: ["anstest_http", "anstest_https", "anstest_db"] + +- name: cleanup epg + cisco.aci.aci_epg: + <<: *aci_epg_present + state: absent + when: epg_present is changed + +- name: cleanup ap + cisco.aci.aci_ap: + <<: *aci_ap_present + state: absent + when: ap_present is changed + +- name: cleanup tenant + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent + when: tenant_present is changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_contract_master/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_contract_master/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_contract_master/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_contract_master/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_contract_master/tasks/main.yml new file mode 100644 index 00000000..1f49f488 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_contract_master/tasks/main.yml @@ -0,0 +1,202 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Cindy Zhao (@cizhao) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +# CLEAN ENVIRONMENT +- name: Set vars + set_fact: + aci_info: &aci_info + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +- name: Ensure tenant doesn't exist + aci_tenant: + <<: *aci_info + state: absent + tenant: anstest + register: tenant_absent + +- name: Ensure tenant exists for tests to kick off + aci_tenant: &aci_tenant_present + <<: *aci_info + state: present + tenant: anstest + register: tenant_present + +- name: ensure contracts exist for tests to kick off + cisco.aci.aci_contract: + <<: *aci_tenant_present + contract: "{{ item }}" + with_items: ["anstest_http", "anstest_https", "anstest_db"] + +- name: ensure ap exists for tests to kick off + cisco.aci.aci_ap: &aci_ap_present + <<: *aci_tenant_present + ap: "{{ item }}" + register: ap_present + loop: + - anstest + - anstest_2 + +- name: create epg + cisco.aci.aci_epg: &aci_epg_present + <<: *aci_tenant_present + ap: anstest + epg: anstest + register: epg_present + +- name: bind provider contract to epg + cisco.aci.aci_epg_to_contract: + <<: *aci_epg_present + contract_type: provider + contract: anstest_http + +- name: bind consumer contract to epg + cisco.aci.aci_epg_to_contract: &aci_epg_consume_present + <<: *aci_epg_present + contract_type: consumer + contract: anstest_https + +- name: bind consumer contract to epg + cisco.aci.aci_epg_to_contract: + <<: *aci_epg_consume_present + contract_type: consumer + contract: anstest_db + +- name: create epgs + cisco.aci.aci_epg: + <<: *aci_tenant_present + ap: "{{ item.ap }}" + epg: "{{ item.epg }}" + register: inherited_epg_present + loop: + - {ap: 'anstest', epg: 'anstest_2'} + - {ap: 'anstest_2', epg: 'anstest_4'} + - {ap: 'anstest_2', epg: 'anstest_3'} + +- name: bind provider contract to epg_4 + cisco.aci.aci_epg_to_contract: + <<: *aci_tenant_present + ap: anstest_2 + epg: anstest_4 + contract_type: provider + contract: anstest_http + +- name: add contract master + cisco.aci.aci_epg_to_contract_master: + <<: *aci_tenant_present + ap: "{{ item.ap }}" + epg: "{{ item.epg }}" + contract_master_ap: anstest + contract_master_epg: anstest + state: present + register: add_contract_master + loop: + - {ap: 'anstest', epg: 'anstest_2'} + - {ap: 'anstest_2', epg: 'anstest_3'} + +- name: Verify add_contract_master + assert: + that: + - item is changed + - item.current.0.fvRsSecInherited.attributes.tDn == "uni/tn-anstest/ap-anstest/epg-anstest" + loop: "{{ add_contract_master.results }}" + +- name: add another contract master + cisco.aci.aci_epg_to_contract_master: + <<: *aci_tenant_present + ap: "{{ item.ap }}" + epg: "{{ item.epg }}" + contract_master_ap: anstest_2 + contract_master_epg: anstest_4 + state: present + register: add_another_contract_master + loop: + - {ap: 'anstest', epg: 'anstest_2'} + - {ap: 'anstest_2', epg: 'anstest_3'} +- name: Verify add_another_contract_master + assert: + that: + - item is changed + - item.current.0.fvRsSecInherited.attributes.tDn == "uni/tn-anstest/ap-anstest_2/epg-anstest_4" + loop: "{{ add_another_contract_master.results }}" + +# Query all contract master ! does not work +- name: Query all contract master + cisco.aci.aci_epg_to_contract_master: + <<: *aci_tenant_present + ap: anstest + epg: anstest_2 + state: query + register: query_all + +# Query specific contract master +- name: Query specific contract master + cisco.aci.aci_epg_to_contract_master: + <<: *aci_tenant_present + ap: anstest + epg: anstest_2 + contract_master_ap: anstest_2 + contract_master_epg: anstest_4 + state: query + register: query_specific_contract_master + +- name: Verify query_specific_contract_master + assert: + that: + - query_specific_contract_master is not changed + - query_specific_contract_master.current.0.fvRsSecInherited.attributes.dn == "uni/tn-anstest/ap-anstest/epg-anstest_2/rssecInherited-[uni/tn-anstest/ap-anstest_2/epg-anstest_4]" + - query_specific_contract_master.current.0.fvRsSecInherited.attributes.tDn == "uni/tn-anstest/ap-anstest_2/epg-anstest_4" + +# Query specific contract master +- name: Query another specific contract master + cisco.aci.aci_epg_to_contract_master: + <<: *aci_tenant_present + ap: anstest + epg: anstest_2 + contract_master_ap: anstest + contract_master_epg: anstest + state: query + register: query_another_specific_contract_master + +- name: Verify query_another_specific_contract_master + assert: + that: + - query_another_specific_contract_master is not changed + - query_another_specific_contract_master.current.0.fvRsSecInherited.attributes.dn == "uni/tn-anstest/ap-anstest/epg-anstest_2/rssecInherited-[uni/tn-anstest/ap-anstest/epg-anstest]" + - query_another_specific_contract_master.current.0.fvRsSecInherited.attributes.tDn == "uni/tn-anstest/ap-anstest/epg-anstest" + +- name: Remove specific contract master + cisco.aci.aci_epg_to_contract_master: + <<: *aci_tenant_present + ap: anstest + epg: anstest_2 + contract_master_ap: anstest + contract_master_epg: anstest + state: absent + register: remove_contract_master + +- name: Verify remove_contract_master + assert: + that: + - remove_contract_master is changed + - remove_contract_master.current == [] + +# Clean up environment +- name: Ensure tenant doesn't exist + aci_tenant: + <<: *aci_info + state: absent + tenant: anstest + register: tenant_absent
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_domain/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_domain/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_domain/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_domain/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_domain/tasks/main.yml new file mode 100644 index 00000000..f199675a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_epg_to_domain/tasks/main.yml @@ -0,0 +1,279 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + tenant: anstest + state: present + register: tenant_present + +- name: ensure ap exists for tests to kick off + cisco.aci.aci_ap: &aci_ap_present + <<: *aci_tenant_present + ap: anstest + register: ap_present + +- name: delete epg to make sure setup is clean + cisco.aci.aci_epg: + <<: *aci_ap_present + epg: anstest + state: absent + +- name: ensure epg exists for tests to kick off + cisco.aci.aci_epg: &aci_epg_present + <<: *aci_ap_present + epg: anstest + register: epg_present + +- name: ensure phys domain exists for tests to kick off + cisco.aci.aci_rest: &aci_rest_phys_domain + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + method: post + path: api/mo/uni/phys-anstest.json + content: {"physDomP": {"attributes": {}}} + register: phys_domain_post + +- name: ensure vmm domain exists for tests to kick off + cisco.aci.aci_rest: &aci_rest_vmm_domain + <<: *aci_rest_phys_domain + path: api/mo/uni/vmmp-VMware/dom-anstest.json + content: {"vmmDomP": {"attributes": {}}} + register: vmm_domain_post + +- name: ensure l2dom domain exists for tests to kick off + cisco.aci.aci_rest: &aci_rest_l2dom_domain + <<: *aci_rest_phys_domain + path: api/mo/uni/l2dom-anstest.json + content: {"l2extDomP": {"attributes": {}}} + register: l2dom_domain_post + +- name: bind phys domain to epg - check mode works + cisco.aci.aci_epg_to_domain: &aci_epg_to_domain_present + <<: *aci_epg_present + domain: anstest + domain_type: phys + check_mode: yes + register: phys_check_mode_present + +- name: bind phys domain to epg - creation works + cisco.aci.aci_epg_to_domain: + <<: *aci_epg_to_domain_present + register: phys_present + +- name: bind phys domain to epg - idempotency works + cisco.aci.aci_epg_to_domain: + <<: *aci_epg_to_domain_present + register: phys_idempotent + +- name: bind phys domain to epg - update works + cisco.aci.aci_epg_to_domain: + <<: *aci_epg_to_domain_present + deploy_immediacy: immediate + register: phys_update + +- name: bind l2dom domain to epg + cisco.aci.aci_epg_to_domain: &aci_epg_to_domain_l2dom_present + <<: *aci_epg_present + domain: anstest + domain_type: l2dom + register: l2_present + +- name: bind vmm domain to epg - creation works + cisco.aci.aci_epg_to_domain: &aci_epg_to_domain_vmm_present + <<: *aci_epg_to_domain_present + domain_type: vmm + vm_provider: vmware + resolution_immediacy: pre-provision + promiscuous: accept + register: vmm_present_promiscuous_accept + +- name: bind vmm domain to epg - creation works + cisco.aci.aci_epg_to_domain: + <<: *aci_epg_to_domain_present + domain_type: vmm + vm_provider: vmware + resolution_immediacy: pre-provision + promiscuous: reject + register: vmm_present_promiscuous_reject + +- name: bind vmm domain to epg - missing params + cisco.aci.aci_epg_to_domain: + <<: *aci_epg_to_domain_vmm_present + vm_provider: "{{ fake_var | default(omit) }}" + ignore_errors: yes + register: present_missing_params + +- name: bind vmm domain to epg - valid vlan + cisco.aci.aci_epg_to_domain: + <<: *aci_epg_to_domain_present + encap: 1 + register: valid_vlan + +- name: bind vmm domain to epg - valid vlan (primary_encap) + cisco.aci.aci_epg_to_domain: + <<: *aci_epg_to_domain_present + primary_encap: 25 + register: valid_vlan_primary_encap + +- name: bind vmm domain to epg - invalid vlan + cisco.aci.aci_epg_to_domain: + <<: *aci_epg_to_domain_present + encap: 4097 + ignore_errors: yes + register: invalid_vlan + +- name: bind vmm domain to epg - invalid vlan (primary_encap) + cisco.aci.aci_epg_to_domain: + <<: *aci_epg_to_domain_present + primary_encap: 4097 + ignore_errors: yes + register: invalid_vlan_primary_encap + +- name: bind vmm domain to epg - incompatible params + cisco.aci.aci_epg_to_domain: + <<: *aci_epg_to_domain_present + vm_provider: vmware + ignore_errors: yes + register: incompatible_params + +- name: present assertions + assert: + that: + - phys_check_mode_present is changed + - phys_present is changed + - phys_present.previous == [] + - phys_present.sent.fvRsDomAtt.attributes.switchingMode == 'native' + - '"[uni/phys-anstest].json" in phys_present.url' + - phys_idempotent is not changed + - phys_idempotent.sent == {} + - phys_update is changed + - phys_update.sent.fvRsDomAtt.attributes.instrImedcy == 'immediate' + - vmm_present_promiscuous_accept is changed + - vmm_present_promiscuous_reject is changed + - vmm_present_promiscuous_accept.current.0.fvRsDomAtt.children.0.vmmSecP.attributes.allowPromiscuous == 'accept' + - vmm_present_promiscuous_reject.current.0.fvRsDomAtt.children.0.vmmSecP.attributes.allowPromiscuous == 'reject' + - vmm_present_promiscuous_accept.sent.fvRsDomAtt.attributes.resImedcy == 'pre-provision' + - vmm_present_promiscuous_accept.sent.fvRsDomAtt.attributes.switchingMode == 'native' + - '"[uni/vmmp-VMware/dom-anstest].json" in vmm_present_promiscuous_accept.url' + - present_missing_params is failed + - 'present_missing_params.msg == "domain_type is vmm but all of the following are missing: vm_provider"' + - invalid_vlan is failed + - invalid_vlan.msg == "Valid VLAN assignments are from 1 to 4096" + - incompatible_params is failed + - incompatible_params.msg == "Domain type 'phys' cannot have a 'vm_provider'" + - invalid_vlan_primary_encap.msg == "Valid VLAN assignments are from 1 to 4096" + - '"[uni/l2dom-anstest].json" in l2_present.url' + - valid_vlan_primary_encap.current.0.fvRsDomAtt.attributes.dn == "uni/tn-anstest/ap-anstest/epg-anstest/rsdomAtt-[uni/phys-anstest]" + +- name: get domain epg binding + cisco.aci.aci_epg_to_domain: &aci_epg_domain_query + <<: *aci_tenant_present + state: query + tenant: "{{ fake_var | default(omit) }}" + register: binding_query + +- name: query assertions + assert: + that: + - binding_query is not changed + - binding_query.current | length > 1 + - '"class/fvRsDomAtt.json" in binding_query.url' + +- name: delete domain epg binding - check mode + cisco.aci.aci_epg_to_domain: &aci_epg_to_domain_absent + <<: *aci_epg_to_domain_present + state: absent + check_mode: yes + register: epg_domain_check_mode_absent + +- name: delete phys domain epg binding - delete works + cisco.aci.aci_epg_to_domain: + <<: *aci_epg_to_domain_absent + register: epg_domain_absent + +- name: delete vmm domain epg binding - delete works + cisco.aci.aci_epg_to_domain: + <<: *aci_epg_to_domain_vmm_present + state: absent + register: epg_vmm_domain_absent + +- name: delete domain epg binding - idempotency works + cisco.aci.aci_epg_to_domain: + <<: *aci_epg_to_domain_absent + register: idempotency_absent + +- name: delete domain epg binding - missing param + cisco.aci.aci_epg_to_domain: + <<: *aci_tenant_present + state: absent + ignore_errors: true + register: absent_missing_param + +- name: absent assertions + assert: + that: + - epg_domain_check_mode_absent is changed + - epg_domain_check_mode_absent.previous != [] + - epg_domain_absent is changed + - epg_domain_absent.previous == epg_domain_check_mode_absent.previous + - epg_vmm_domain_absent is changed + - idempotency_absent is not changed + - idempotency_absent.previous == [] + - absent_missing_param is failed + - 'absent_missing_param.msg == "state is absent but all of the following are missing: ap, domain, domain_type, epg"' + +- name: remove vmm domain - cleanup + cisco.aci.aci_rest: + <<: *aci_rest_vmm_domain + method: delete + when: vmm_domain_post is changed + +- name: remove phys domain - cleanup + cisco.aci.aci_rest: + <<: *aci_rest_phys_domain + method: delete + when: phys_domain_post is changed + +- name: remove l2 domain - cleanup + cisco.aci.aci_rest: + <<: *aci_rest_l2dom_domain + method: delete + when: l2dom_domain_post is changed + +- name: remove epg - cleanup + cisco.aci.aci_epg: + <<: *aci_epg_present + state: absent + when: epg_present is changed + +- name: remove ap - cleanup + cisco.aci.aci_ap: + <<: *aci_ap_present + state: absent + when: ap_present is changed + +- name: remove tenant - cleanup + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent + when: tenant_present is changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_fabric_node/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_fabric_node/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_fabric_node/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_fabric_node/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_fabric_node/tasks/main.yml new file mode 100644 index 00000000..ad255bb1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_fabric_node/tasks/main.yml @@ -0,0 +1,217 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + + +# CLEAN ENVIRONMENT +- name: Remove fabric node + cisco.aci.aci_fabric_node: &aci_fabric_node_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + serial: ansible_test + node_id: 105 + state: absent + + +# ADD FABRIC NODE +- name: Add fabric node (check_mode) + cisco.aci.aci_fabric_node: &aci_fabric_node_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + serial: ansible_test + node_id: 105 + switch: test + state: present + check_mode: yes + register: cm_add_fabric_node + +- name: Add fabric node (normal mode) + cisco.aci.aci_fabric_node: *aci_fabric_node_present + register: nm_add_fabric_node + +- name: Add fabric node again (check_mode) + cisco.aci.aci_fabric_node: *aci_fabric_node_present + check_mode: yes + register: cm_add_fabric_node_again + +- name: Add fabric node again (normal mode) + cisco.aci.aci_fabric_node: *aci_fabric_node_present + register: nm_add_fabric_node_again + +- name: Verify add_fabric_node + assert: + that: + - cm_add_fabric_node is changed + - nm_add_fabric_node is changed + # FIXME: Module is not idempotent + - cm_add_fabric_node_again is not changed + - nm_add_fabric_node_again is not changed + + +# CHANGE FABRIC NODE +- name: Change description of fabric node (check_mode) + cisco.aci.aci_fabric_node: + <<: *aci_fabric_node_present + description: Ansible test fabric node + check_mode: yes + register: cm_add_fabric_node_descr + +- name: Change description of fabric node (normal mode) + cisco.aci.aci_fabric_node: + <<: *aci_fabric_node_present + description: Ansible test fabric node + register: nm_add_fabric_node_descr + +- name: Change description of fabric nodeagain (check_mode) + cisco.aci.aci_fabric_node: + <<: *aci_fabric_node_present + description: Ansible test fabric node + check_mode: yes + register: cm_add_fabric_node_descr_again + +- name: Change description of fabric node again (normal mode) + cisco.aci.aci_fabric_node: + <<: *aci_fabric_node_present + description: Ansible test fabric node + register: nm_add_fabric_node_descr_again + +- name: Verify add_fabric_node_descr + assert: + that: + - cm_add_fabric_node_descr is changed + - nm_add_fabric_node_descr is changed + # FIXME: Module is not idempotent + - cm_add_fabric_node_descr_again is not changed + - nm_add_fabric_node_descr_again is not changed + + +# ADD FABRIC NODE AGAIN +- name: Add fabric node again with no description (check_mode) + cisco.aci.aci_fabric_node: *aci_fabric_node_present + check_mode: yes + register: cm_add_fabric_node_again_no_descr + +- name: Add fabric node again with no description (normal mode) + cisco.aci.aci_fabric_node: *aci_fabric_node_present + register: nm_add_fabric_node_again_no_descr + +- name: Verify add_fabric_node_again_no_descr + assert: + that: + # FIXME: Module is not idempotent + - cm_add_fabric_node_again_no_descr is not changed + - nm_add_fabric_node_again_no_descr is not changed + + +# QUERY ALL FABRIC NODES +- name: Query fabric nodes (check_mode) + cisco.aci.aci_fabric_node: &aci_fabric_node_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_fabric_nodes + +- name: Query all fabric nodes (normal mode) + cisco.aci.aci_fabric_node: *aci_fabric_node_query + register: nm_query_all_fabric_nodes + +- name: Verify query_all_fabric_nodes + assert: + that: + - cm_query_all_fabric_nodes is not changed + - nm_query_all_fabric_nodes is not changed + - cm_query_all_fabric_nodes == nm_query_all_fabric_nodes + + +# QUERY A FABRIC NODE +- name: Query our fabric_node + cisco.aci.aci_fabric_node: + <<: *aci_fabric_node_query + serial: ansible_test # might need node_id too + check_mode: yes + register: cm_query_fabric_node + +- name: Query our fabric_node + cisco.aci.aci_fabric_node: + <<: *aci_fabric_node_query + serial: ansible_test + register: nm_query_fabric_node + +- name: Verify query_fabric_node + assert: + that: + - cm_query_fabric_node is not changed + - nm_query_fabric_node is not changed + - cm_query_fabric_node == nm_query_fabric_node + + +# REMOVE FABRIC NODE +- name: Remove fabric_node (check_mode) + cisco.aci.aci_fabric_node: *aci_fabric_node_absent + check_mode: yes + register: cm_remove_fabric_node + +- name: Remove fabric_node (normal mode) + cisco.aci.aci_fabric_node: *aci_fabric_node_absent + register: nm_remove_fabric_node + +- name: Remove fabric_node again (check_mode) + cisco.aci.aci_fabric_node: *aci_fabric_node_absent + check_mode: yes + register: cm_remove_fabric_node_again + +- name: Remove fabric_node again (normal mode) + cisco.aci.aci_fabric_node: *aci_fabric_node_absent + register: nm_remove_fabric_node_again + +- name: Verify remove_fabric_node + assert: + that: + - cm_remove_fabric_node is changed + - nm_remove_fabric_node is changed + - cm_remove_fabric_node_again is not changed + - nm_remove_fabric_node_again is not changed + + +# QUERY NON-EXISTING LEAF PROFILE +- name: Query non-existing fabric_node (check_mode) + cisco.aci.aci_fabric_node: + <<: *aci_fabric_node_query + serial: ansible_test + check_mode: yes + register: cm_query_non_fabric_node + +- name: Query non-existing fabric_node (normal mode) + cisco.aci.aci_fabric_node: + <<: *aci_fabric_node_query + serial: ansible_test + register: nm_query_non_fabric_node + +- name: Verify query_non_fabric_node + assert: + that: + - cm_query_non_fabric_node is not changed + - nm_query_non_fabric_node is not changed + - cm_query_non_fabric_node == nm_query_non_fabric_node diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_filter/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_filter/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_filter/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_filter/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_filter/tasks/main.yml new file mode 100644 index 00000000..b5ceb56c --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_filter/tasks/main.yml @@ -0,0 +1,223 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +# CLEAN ENVIRONMENT +- name: Add tenant + cisco.aci.aci_tenant: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: ansible_test + state: present + +- name: Remove filter + cisco.aci.aci_filter: &filter_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: ansible_test + filter: filter_test + state: absent + +# ADD FILTER +- name: Add filter (check_mode) + cisco.aci.aci_filter: &filter_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: ansible_test + filter: filter_test + state: present + check_mode: yes + register: cm_add_filter + +- name: Add filter again (check_mode) + cisco.aci.aci_filter: *filter_present + check_mode: yes + register: cm_add_filter_again + +- name: Add filter (normal mode) + cisco.aci.aci_filter: *filter_present + register: nm_add_filter + +- name: Add filter again (normal mode) + cisco.aci.aci_filter: *filter_present + register: nm_add_filter_again + +- name: Verify add_filter + assert: + that: + - cm_add_filter is changed + - cm_add_filter_again is changed + - nm_add_filter is changed + - nm_add_filter_again is not changed + +# CHANGE FILTER +- name: Change description of filter (check_mode) + cisco.aci.aci_filter: + <<: *filter_present + description: Ansible test filter + check_mode: yes + register: cm_add_filter_descr + +- name: Change description of filter again (check_mode) + cisco.aci.aci_filter: + <<: *filter_present + description: Ansible test filter + check_mode: yes + register: cm_add_filter_descr_again + +- name: Change description of filter (normal mode) + cisco.aci.aci_filter: + <<: *filter_present + description: Ansible test filter + register: nm_add_filter_descr + +- name: Change description of filter again (normal mode) + cisco.aci.aci_filter: + <<: *filter_present + description: Ansible test filter + register: nm_add_filter_descr_again + +- name: Verify add_filter_descr + assert: + that: + - cm_add_filter_descr is changed + - cm_add_filter_descr_again is changed + - nm_add_filter_descr is changed + - nm_add_filter_descr_again is not changed + +# ADD FILTER AGAIN +- name: Add filter again with no description (check_mode) + cisco.aci.aci_filter: *filter_present + check_mode: yes + register: cm_add_filter_again_no_descr + +- name: Add filter again with no description (normal mode) + cisco.aci.aci_filter: *filter_present + register: nm_add_filter_again_no_descr + +- name: Verify add_filter_again_no_descr + assert: + that: + - cm_add_filter_again_no_descr is not changed + - nm_add_filter_again_no_descr is not changed + +# QUERY ALL FILTERS +- name: Query all filters (check_mode) + cisco.aci.aci_filter: &filter_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_filters + +- name: Query all filters (normal mode) + cisco.aci.aci_filter: *filter_query + register: nm_query_all_filters + +- name: Verify query_all_filters + assert: + that: + - cm_query_all_filters is not changed + - nm_query_all_filters is not changed + # NOTE: Order of filters is not stable between calls + #- cm_query_all_filters == nm_query_all_filters + +# QUERY A FILTER +- name: Query our filter + cisco.aci.aci_filter: + <<: *filter_query + tenant: ansible_test + filter: filter_test + check_mode: yes + register: cm_query_filter + +- name: Query our filter + cisco.aci.aci_filter: + <<: *filter_query + tenant: ansible_test + filter: filter_test + register: nm_query_filter + +- name: Verify query_filter + assert: + that: + - cm_query_filter is not changed + - nm_query_filter is not changed + - cm_query_filter == nm_query_filter + +# REMOVE FILTER +- name: Remove filter (check_mode) + cisco.aci.aci_filter: *filter_absent + check_mode: yes + register: cm_remove_filter + +- name: Remove filter again (check_mode) + cisco.aci.aci_filter: *filter_absent + check_mode: yes + register: cm_remove_filter_again + +- name: Remove filter (normal mode) + cisco.aci.aci_filter: *filter_absent + register: nm_remove_filter + +- name: Remove filter again (normal mode) + cisco.aci.aci_filter: *filter_absent + register: nm_remove_filter_again + +- name: Verify remove_filter + assert: + that: + - cm_remove_filter is changed + - cm_remove_filter_again is changed + - nm_remove_filter is changed + - nm_remove_filter_again is not changed + +# QUERY NON-EXISTING FILTER +# FIXME: Should this fail or return empty values ? +- name: Query non-existing filter (check_mode) + cisco.aci.aci_filter: + <<: *filter_query + tenant: ansible_test + filter: filter_test + check_mode: yes + register: cm_query_non_filter + +- name: Query non-existing filter (normal mode) + cisco.aci.aci_filter: + <<: *filter_query + tenant: ansible_test + filter: filter_test + register: nm_query_non_filter + +- name: Verify query_non_filter + assert: + that: + - cm_query_non_filter is not changed + - nm_query_non_filter is not changed + - cm_query_non_filter == nm_query_non_filter diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_filter_entry/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_filter_entry/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_filter_entry/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_filter_entry/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_filter_entry/tasks/main.yml new file mode 100644 index 00000000..b69ae6c4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_filter_entry/tasks/main.yml @@ -0,0 +1,299 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: anstest + state: absent + +- name: ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + tenant: anstest + state: present + register: tenant_present + +- name: ensure filter exists for tests to kick off + cisco.aci.aci_filter: &aci_filter_present + <<: *aci_tenant_present + filter: anstest + register: filter_present + +- name: create filter entry - check mode works + cisco.aci.aci_filter_entry: &aci_entry_present + <<: *aci_filter_present + entry: anstest + description: Ansible Test + ether_type: ip + ip_protocol: tcp + dst_port_start: 80 + dst_port_end: 88 + check_mode: yes + register: entry_present_check_mode + +- name: create filter entry - creation works + cisco.aci.aci_filter_entry: + <<: *aci_entry_present + register: entry_present + +- name: create filter entry - idempotency works + cisco.aci.aci_filter_entry: + <<: *aci_entry_present + register: entry_present_idempotent + +- name: update filter entry - update works + cisco.aci.aci_filter_entry: + <<: *aci_entry_present + description: Ansible Test Update + dst_port_start: 80 + dst_port_end: 90 + register: entry_present_update + +- name: create filter entry - test different types + cisco.aci.aci_filter_entry: + <<: *aci_filter_present + entry: anstest2 + ether_type: arp + arp_flag: arp_reply + register: entry_present_2 + +- name: create filter entry - test different types + cisco.aci.aci_filter_entry: + <<: *aci_filter_present + entry: anstest3 + ether_type: ip + ip_protocol: icmp + icmp_msg_type: echo + register: entry_present_3 + +- name: create filter entry - test different types + cisco.aci.aci_filter_entry: + <<: *aci_filter_present + entry: anstest4 + ether_type: ip + ip_protocol: udp + dst_port: 1000 + register: entry_present_4 + +- name: missing param - failure message works + cisco.aci.aci_filter_entry: + <<: *aci_filter_present + ignore_errors: yes + register: present_missing_param + +- name: incompatable params - failure message works + cisco.aci.aci_filter_entry: + <<: *aci_entry_present + dst_port: 99 + ignore_errors: yes + register: present_incompatible_params + +- name: present assertions + assert: + that: + - entry_present_check_mode is changed + - entry_present_check_mode.previous == [] + - entry_present_check_mode.sent.vzEntry.attributes.dFromPort == 'http' + - entry_present_check_mode.sent.vzEntry.attributes.dToPort == '88' + - entry_present_check_mode.sent.vzEntry.attributes.descr == 'Ansible Test' + - entry_present_check_mode.sent.vzEntry.attributes.etherT == 'ip' + - entry_present_check_mode.sent.vzEntry.attributes.name == 'anstest' + - entry_present_check_mode.sent.vzEntry.attributes.prot == 'tcp' + - entry_present is changed + - entry_present.previous == [] + - entry_present.sent == entry_present_check_mode.sent + - entry_present_idempotent is not changed + - entry_present_idempotent.previous != [] + - entry_present_idempotent.sent == {} + - entry_present_update is changed + - entry_present_update.previous != [] + - entry_present_update.sent != entry_present_update.proposed + - entry_present_2 is changed + - entry_present_2.sent.vzEntry.attributes.arpOpc == 'reply' + - entry_present_2.sent.vzEntry.attributes.etherT == 'arp' + - entry_present_2.sent.vzEntry.attributes.name == 'anstest2' + - entry_present_3 is changed + - entry_present_3.sent.vzEntry.attributes.etherT == 'ip' + - entry_present_3.sent.vzEntry.attributes.icmpv4T == 'echo' + - entry_present_3.sent.vzEntry.attributes.name == 'anstest3' + - entry_present_3.sent.vzEntry.attributes.prot == 'icmp' + - entry_present_4 is changed + - entry_present_4.sent.vzEntry.attributes.dFromPort == '1000' + - entry_present_4.sent.vzEntry.attributes.dToPort == '1000' + - entry_present_4.sent.vzEntry.attributes.etherT == 'ip' + - entry_present_4.sent.vzEntry.attributes.name == 'anstest4' + - entry_present_4.sent.vzEntry.attributes.prot == 'udp' + - present_missing_param is failed + - 'present_missing_param.msg == "state is present but all of the following are missing: entry"' + - present_incompatible_params is failed + - present_incompatible_params.msg.startswith("Parameter") + +- name: query tenant filter entry + cisco.aci.aci_filter_entry: &aci_query_entry + <<: *aci_entry_present + state: query + register: query_tenant_filter_entry + +- name: query filter entry + cisco.aci.aci_filter_entry: + <<: *aci_query_entry + tenant: "{{ fakevar | default(omit) }}" + register: query_filter_entry + +- name: query tenant entry + cisco.aci.aci_filter_entry: + <<: *aci_query_entry + filter: "{{ fakevar | default(omit) }}" + register: query_tenant_entry + +- name: query tenant filter + cisco.aci.aci_filter_entry: + <<: *aci_query_entry + entry: "{{ fakevar | default(omit) }}" + register: query_tenant_filter + +- name: query entry + cisco.aci.aci_filter_entry: &aci_query_entry_2 + <<: *aci_query_entry + tenant: "{{ fakevar | default(omit) }}" + filter: "{{ fakevar | default(omit) }}" + register: query_entry + +- name: query filter + cisco.aci.aci_filter_entry: + <<: *aci_query_entry + tenant: "{{ fakevar | default(omit) }}" + entry: "{{ fakevar | default(omit) }}" + register: query_filter + +- name: query tenant + cisco.aci.aci_filter_entry: + <<: *aci_query_entry + filter: "{{ fakevar | default(omit) }}" + entry: "{{ fakevar | default(omit) }}" + register: query_tenant + +- name: query all + cisco.aci.aci_filter_entry: + <<: *aci_query_entry_2 + entry: "{{ fakevar | default(omit) }}" + register: query_all + +- name: query assertions + assert: + that: + - query_tenant_filter_entry is not changed + - query_tenant_filter_entry.current | length == 1 + - query_tenant_filter_entry.current.0.vzEntry.attributes.name == "anstest" + - '"tn-anstest/flt-anstest/e-anstest.json" in query_tenant_filter_entry.url' + - query_filter_entry is not changed + - query_filter_entry.current.0.vzFilter.attributes.name == "anstest" + - query_filter_entry.current.0.vzFilter.children | length == 1 + - '"query-target-filter=eq(vzFilter.name,\"anstest\")" in query_filter_entry.filter_string' + - '"rsp-subtree-filter=eq(vzEntry.name,\"anstest\")" in query_filter_entry.filter_string' + - '"class/vzFilter.json" in query_filter_entry.url' + - query_tenant_entry is not changed + - query_tenant_entry.current | length == 1 + - query_tenant_entry.current.0.fvTenant.attributes.name == "anstest" + - '"rsp-subtree-filter=eq(vzEntry.name,\"anstest\")" in query_tenant_entry.filter_string' + - '"rsp-subtree-class=vzEntry" in query_tenant_entry.filter_string' + - '"tn-anstest.json" in query_tenant_entry.url' + - query_tenant_filter is not changed + - query_tenant_filter.current | length == 1 + - query_tenant_filter.current.0.vzFilter.attributes.name == "anstest" + - query_tenant_filter.current.0.vzFilter.children | length == 4 + - '"rsp-subtree-class=vzEntry" in query_tenant_filter.filter_string' + - '"tn-anstest/flt-anstest.json" in query_tenant_filter.url' + - query_entry is not changed + - query_entry.current.0.vzEntry.attributes.name == "anstest" + - '"query-target-filter=eq(vzEntry.name,\"anstest\")" in query_entry.filter_string' + - '"class/vzEntry.json" in query_entry.url' + - query_filter is not changed + - query_filter.current.0.vzFilter.attributes.name == "anstest" + - '"query-target-filter=eq(vzFilter.name,\"anstest\")" in query_filter.filter_string' + - '"rsp-subtree-class=vzEntry" in query_filter.filter_string' + - '"class/vzFilter.json" in query_filter.url' + - query_tenant is not changed + - query_tenant.current | length == 1 + - query_tenant.current.0.fvTenant.attributes.name == "anstest" + - '"rsp-subtree-class=vzEntry,vzFilter" in query_tenant.filter_string' + - '"tn-anstest.json" in query_tenant.url' + - query_all is not changed + - query_all.current | length > 1 + - query_all.current.0.vzEntry is defined + - '"class/vzEntry.json" in query_all.url' + +- name: delete entry - check mode works + cisco.aci.aci_filter_entry: &aci_entry_absent + <<: *aci_entry_present + state: absent + check_mode: yes + register: entry_absent_check_mode + +- name: delete entry - deletion works + cisco.aci.aci_filter_entry: + <<: *aci_entry_absent + register: entry_absent + +- name: delete entry - idempotency works + cisco.aci.aci_filter_entry: + <<: *aci_entry_absent + register: entry_absent_idempotent + +- name: missing param - failure message works + cisco.aci.aci_filter_entry: + <<: *aci_tenant_present + state: absent + ignore_errors: yes + register: absent_missing_param + +- name: cleanup remaining entries + cisco.aci.aci_filter_entry: + <<: *aci_entry_absent + entry: "{{ item }}" + with_items: ["anstest2", "anstest3", "anstest4"] + +- name: absent assertions + assert: + that: + - entry_absent_check_mode is changed + - entry_absent_check_mode.previous != [] + - entry_absent is changed + - entry_absent.previous == entry_absent_check_mode.previous + - entry_absent.proposed == {} + - entry_absent_idempotent is not changed + - entry_absent_idempotent.previous == [] + - absent_missing_param is failed + - 'absent_missing_param.msg == "state is absent but all of the following are missing: entry, filter"' + +- name: cleanup filter + cisco.aci.aci_filter: + <<: *aci_filter_present + state: absent + when: filter_present is changed + +- name: cleanup tenant + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent + when: tenant_present is changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_firmware_source/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_firmware_source/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_firmware_source/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_firmware_source/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_firmware_source/tasks/main.yml new file mode 100644 index 00000000..3195a3ff --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_firmware_source/tasks/main.yml @@ -0,0 +1,207 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> +# Copyright: (c) 2020, Cindy Zhao (@cizhao) <cizhao@cisco.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +# CLEAN ENVIRONMENT +- name: Remove firmware source + cisco.aci.aci_firmware_source: &source_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + source: '{{ item }}' + state: absent + loop: + - ansible_test_1 + - ansible_test_2 + +# ADD SOURCE +- name: Add source (check_mode) + aci_firmware_source: &source_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + source: ansible_test_1 + url: foobar.cisco.com/download/cisco/aci/aci-msft-pkg-3.1.1i.zip + url_protocol: http + state: present + check_mode: yes + register: cm_add_source + +- name: Add source (normal mode) + aci_firmware_source: + <<: *source_present + register: nm_add_source + +- name: Verify add_source + assert: + that: + - cm_add_source is changed + - nm_add_source is changed + - cm_add_source.sent.firmwareOSource.attributes.name == nm_add_source.sent.firmwareOSource.attributes.name == 'ansible_test_1' + - cm_add_source.sent.firmwareOSource.attributes.proto == nm_add_source.sent.firmwareOSource.attributes.proto == 'http' + - cm_add_source.sent.firmwareOSource.attributes.url == nm_add_source.sent.firmwareOSource.attributes.url == 'foobar.cisco.com/download/cisco/aci/aci-msft-pkg-3.1.1i.zip' + - cm_add_source.proposed.firmwareOSource.attributes.name == nm_add_source.proposed.firmwareOSource.attributes.name == 'ansible_test_1' + - cm_add_source.proposed.firmwareOSource.attributes.proto == nm_add_source.proposed.firmwareOSource.attributes.proto == 'http' + - cm_add_source.proposed.firmwareOSource.attributes.url == nm_add_source.proposed.firmwareOSource.attributes.url == 'foobar.cisco.com/download/cisco/aci/aci-msft-pkg-3.1.1i.zip' + - cm_add_source.current == cm_add_source.previous == nm_add_source.previous == [] + - nm_add_source.current.0.firmwareOSource.attributes.name == 'ansible_test_1' + - nm_add_source.current.0.firmwareOSource.attributes.proto == 'http' + - nm_add_source.current.0.firmwareOSource.attributes.url == 'foobar.cisco.com/download/cisco/aci/aci-msft-pkg-3.1.1i.zip' + +- name: Add source again (check_mode) + aci_firmware_source: *source_present + check_mode: yes + register: cm_add_source_again + +- name: Add source again (normal mode) + aci_firmware_source: + <<: *source_present + register: nm_add_source_again + +- name: Verify add_source_again + assert: + that: + - cm_add_source_again is not changed + +- name: Add another source (normal mode) + aci_firmware_source: + <<: *source_present + source: ansible_test_2 + register: nm_add_source + +# QUERY ALL SOURCES +- name: Query all sources (check_mode) + cisco.aci.aci_firmware_source: &source_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_sources + +- name: Query all sources (normal mode) + cisco.aci.aci_firmware_source: *source_query + register: nm_query_all_sources + +- name: Verify query_all_sources + assert: + that: + - cm_query_all_sources is not changed + - nm_query_all_sources is not changed + - cm_query_all_sources == nm_query_all_sources + - nm_query_all_sources.current.0.firmwareRepoP.children | length == 2 + +# QUERY A SOURCE +- name: Query our source (check_mode) + aci_firmware_source: + <<: *source_query + source: ansible_test_1 + check_mode: yes + register: cm_query_source + +- name: Query our source (normal mode) + aci_firmware_source: + <<: *source_query + source: ansible_test_1 + register: nm_query_source + +- name: Verify query_source + assert: + that: + - cm_query_source is not changed + - nm_query_source is not changed + - cm_query_source == nm_query_source + - nm_query_source.current.0.firmwareOSource.attributes.dn == 'uni/fabric/fwrepop/osrc-ansible_test_1' + - nm_query_source.current.0.firmwareOSource.attributes.name == 'ansible_test_1' + +# REMOVE SOURCE +- name: Remove source (check_mode) + aci_firmware_source: &source_remove + <<: *source_query + source: ansible_test_1 + state: absent + check_mode: yes + register: cm_remove_source + +- name: Remove source (normal mode) + aci_firmware_source: *source_remove + register: nm_remove_source + +- name: Verify remove_source + assert: + that: + - cm_remove_source is changed + - nm_remove_source is changed + - cm_remove_source.current.0.firmwareOSource.attributes.dn == cm_remove_source.previous.0.firmwareOSource.attributes.dn == nm_remove_source.previous.0.firmwareOSource.attributes.dn == 'uni/fabric/fwrepop/osrc-ansible_test_1' + - nm_remove_source.current == [] + +- name: Remove source again (check_mode) + aci_firmware_source: *source_remove + check_mode: yes + register: cm_remove_source_again + +- name: Remove source again (normal mode) + aci_firmware_source: *source_remove + register: nm_remove_source_again + +- name: Verify remove_source_again + assert: + that: + - cm_remove_source_again is not changed + - nm_remove_source_again is not changed + +# QUERY NON-EXISTING SOURCE +- name: Query non-existing source (check_mode) + aci_firmware_source: + <<: *source_query + source: non_existing_source + check_mode: yes + register: cm_query_non_source + +- name: Query non-existing source (normal mode) + aci_firmware_source: + <<: *source_query + source: non_existing_source + register: nm_query_non_source + +- name: Verify query_non_source + assert: + that: + - cm_query_non_source is not changed + - nm_query_non_source is not changed + - cm_query_non_source == nm_query_non_source + - nm_query_non_source.current == [] + +# PROVOKE ERRORS +- name: Error when required parameter is missing + cisco.aci.aci_firmware_source: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: present + ignore_errors: yes + register: error_on_missing_required_param + +- name: Verify error_on_missing_required_param + assert: + that: + - error_on_missing_required_param is failed + - 'error_on_missing_required_param.msg == "state is present but all of the following are missing: source, url"' diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_cdp/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_cdp/aliases new file mode 100644 index 00000000..cf765b70 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_cdp/aliases @@ -0,0 +1 @@ +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_cdp/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_cdp/tasks/main.yml new file mode 100644 index 00000000..92103acb --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_cdp/tasks/main.yml @@ -0,0 +1,106 @@ +# Test code for the ACI modules +# Copyright: (c) 2019, Tim Knipper (tknipper11) <tim.knipper@gmail.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +# CLEAN ENVIRONMENT +- name: Remove CDP Test Policy + cisco.aci.aci_interface_policy_cdp: + name: Ansible_CDP_Test_Policy + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(false) }}' + output_level: debug + state: absent + register: cdp_delete + + + +- name: Create CDP Test Policy + cisco.aci.aci_interface_policy_cdp: + name: Ansible_CDP_Test_Policy + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(false) }}' +# output_level: debug + state: present + register: cdp_create +- debug: + var: cdp_create + +- assert: + that: + - cdp_create is changed + + +- name: test for idempotency + cisco.aci.aci_interface_policy_cdp: + name: Ansible_CDP_Test_Policy + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(false) }}' +# output_level: debug + state: present + register: cdp_idem + +- name: Assert that idempotency is not changed + assert: + that: + - cdp_idem is not changed + + + +- name: Create CDP Disable Test Policy + cisco.aci.aci_interface_policy_cdp: + name: Ansible_CDP_Test_Policy + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(false) }}' +# output_level: debug + state: present + admin_state: no + register: cdp_disable +- debug: + var: cdp_disable + +- name: Assert that CDP is Disabled + assert: + that: + - cdp_disable.current.0.cdpIfPol.attributes.adminSt == 'disabled' + + +- name: Query CDP Policy + cisco.aci.aci_interface_policy_cdp: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(false) }}' +# output_level: debug + state: query + register: cdp_query +- debug: + var: cdp_query + +- name: CDP Query Assertion + assert: + that: + - cdp_query is not changed
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_breakout_port_group/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_breakout_port_group/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_breakout_port_group/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_breakout_port_group/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_breakout_port_group/tasks/main.yml new file mode 100644 index 00000000..b0d94c18 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_breakout_port_group/tasks/main.yml @@ -0,0 +1,162 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Cindy Zhao (@cizhao) <cizhao@cisco.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +# CLEAN ENVIRONMENT +- name: Set vars + set_fact: + aci_info: &aci_info + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +- name: Making sure leaf breakout port group doesn't exist at beginning of test + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + <<: *aci_info + breakout_port_group: '{{ item }}' + state: absent + loop: + - 'ansible_breakout_port' + - 'ansible_breakout_port_2' + +- name: Create leaf breakout port group (check_mode) + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + <<: *aci_info + breakout_port_group: ansible_breakout_port + description: description for ansible_breakout_port + breakout_map: 10g-4x + state: present + check_mode: yes + register: cm_create_brkout_port + +- name: Create leaf breakout port group (normal_mode) + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + <<: *aci_info + breakout_port_group: ansible_breakout_port + description: description for ansible_breakout_port + breakout_map: 10g-4x + state: present + register: nm_create_brkout_port + +- name: Verify cm_create_brkout_port and nm_create_brkout_port + assert: + that: + - cm_create_brkout_port is changed + - cm_create_brkout_port.previous == [] + - cm_create_brkout_port.proposed.infraBrkoutPortGrp.attributes.name == "ansible_breakout_port" + - cm_create_brkout_port.proposed.infraBrkoutPortGrp.attributes.dn == "uni/infra/funcprof/brkoutportgrp-ansible_breakout_port" + - cm_create_brkout_port.proposed.infraBrkoutPortGrp.attributes.descr == "description for ansible_breakout_port" + - cm_create_brkout_port.proposed.infraBrkoutPortGrp.attributes.brkoutMap == "10g-4x" + - nm_create_brkout_port is changed + - nm_create_brkout_port.previous == [] + - nm_create_brkout_port.current.0.infraBrkoutPortGrp.attributes.name == "ansible_breakout_port" + - nm_create_brkout_port.current.0.infraBrkoutPortGrp.attributes.dn == "uni/infra/funcprof/brkoutportgrp-ansible_breakout_port" + - nm_create_brkout_port.current.0.infraBrkoutPortGrp.attributes.descr == "description for ansible_breakout_port" + - nm_create_brkout_port.current.0.infraBrkoutPortGrp.attributes.brkoutMap == "10g-4x" + +- name: Create leaf breakout port group again (normal_mode) + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + <<: *aci_info + breakout_port_group: ansible_breakout_port + description: description for ansible_breakout_port + breakout_map: 10g-4x + state: present + register: nm_create_brkout_port_again + +- name: Verify nm_create_brkout_port_again + assert: + that: + - nm_create_brkout_port_again is not changed + +- name: Create another leaf breakout port group + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + <<: *aci_info + breakout_port_group: ansible_breakout_port_2 + state: present + register: nm_create_another_brkout_port + +- name: Verify nm_create_another_brkout_port + assert: + that: + - nm_create_another_brkout_port is changed + - nm_create_another_brkout_port.previous == [] + - nm_create_another_brkout_port.proposed.infraBrkoutPortGrp.attributes.name == "ansible_breakout_port_2" + - nm_create_another_brkout_port.proposed.infraBrkoutPortGrp.attributes.dn == "uni/infra/funcprof/brkoutportgrp-ansible_breakout_port_2" + - nm_create_another_brkout_port.current.0.infraBrkoutPortGrp.attributes.dn == "uni/infra/funcprof/brkoutportgrp-ansible_breakout_port_2" + - nm_create_another_brkout_port.current.0.infraBrkoutPortGrp.attributes.brkoutMap == "none" + - nm_create_another_brkout_port.current.0.infraBrkoutPortGrp.attributes.name == "ansible_breakout_port_2" + +- name: Query all breakout ports + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + <<: *aci_info + state: query + register: query_all + +- name: Verify query_all + assert: + that: + query_all.current | length >= 2 + +- name: Query specific breakout port + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + <<: *aci_info + breakout_port_group: ansible_breakout_port + state: query + register: query_one + +- name: Verify query_one + assert: + that: + - query_one is not changed + - query_one.current.0.infraBrkoutPortGrp.attributes.name == "ansible_breakout_port" + +- name: Update breakout port + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + <<: *aci_info + breakout_port_group: ansible_breakout_port + breakout_map: none + description: "" + state: present + register: change_ansible_breakout_port + +- name: Verify change_ansible_breakout_port + assert: + that: + - change_ansible_breakout_port is changed + - change_ansible_breakout_port.current.0.infraBrkoutPortGrp.attributes.brkoutMap == "none" + - change_ansible_breakout_port.current.0.infraBrkoutPortGrp.attributes.descr == "" + +- name: Delete breakout port + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + <<: *aci_info + breakout_port_group: ansible_breakout_port + state: absent + register: rm_breakout_port + +- name: Verify rm_breakout_port + assert: + that: + - rm_breakout_port is changed + - rm_breakout_port.current == [] + +- name: Query removed breakout port + cisco.aci.aci_interface_policy_leaf_breakout_port_group: + <<: *aci_info + breakout_port_group: ansible_breakout_port + state: query + register: query_removed_breakout_port + +- name: Verify query_removed_breakout_port + assert: + that: + - query_removed_breakout_port.current == []
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_policy_group/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_policy_group/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_policy_group/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_policy_group/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_policy_group/tasks/main.yml new file mode 100644 index 00000000..3557c986 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_policy_group/tasks/main.yml @@ -0,0 +1,453 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Making sure interface_policy_leaf_policy_group doesn't exist at beginning of test (PC) + cisco.aci.aci_interface_policy_leaf_policy_group: &aci_interface_policy_leaf_policy_group_link_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + policy_group: policygroupname_link + lag_type: link + state: absent + +- name: Making sure interface_policy_leaf_policy_group doesn't exist at beginning of test (VPC) + cisco.aci.aci_interface_policy_leaf_policy_group: &aci_interface_policy_leaf_policy_group_node_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + policy_group: policygroupname_node + lag_type: node + state: absent + +- name: Making sure interface_policy_leaf_policy_group doesn't exist at beginning of test (Leaf Access Port) + cisco.aci.aci_interface_policy_leaf_policy_group: &aci_interface_policy_leaf_policy_group_leaf_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + policy_group: policygroupname_leaf + lag_type: leaf + state: absent + + +# ==== TESTING Port Channel (PC), lag_type: link ==== + +- name: Adding a interface policy leaf policy group (PC) - check mode works + cisco.aci.aci_interface_policy_leaf_policy_group: &aci_interface_policy_leaf_policy_group_link_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + policy_group: policygroupname_link + lag_type: link + link_level_policy: linklevelpolicy + fibre_channel_interface_policy: fiberchannelpolicy + state: present + check_mode: yes + register: intf_policy_leaf_polgrp_check_mode_present + +- name: Adding a interface policy leaf policy group (PC) - creation works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_link_present + register: intf_policy_leaf_polgrp_present + +- name: Adding a interface policy leaf policy group (PC) - idempotency works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_link_present + register: intf_policy_leaf_polgrp_idempotent + +- name: Adding a interface policy leaf policy group description (PC) - update works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_link_present + description: policygroup description + register: intf_policy_leaf_polgrp_update + +# TODO: also test for errors +- name: present assertions + assert: + that: + - intf_policy_leaf_polgrp_check_mode_present is changed + - intf_policy_leaf_polgrp_present is changed + - intf_policy_leaf_polgrp_present.previous == [] + - intf_policy_leaf_polgrp_present.sent.infraAccBndlGrp.attributes.lagT == 'link' + - intf_policy_leaf_polgrp_present.sent.infraAccBndlGrp.attributes.name == 'policygroupname_link' + - intf_policy_leaf_polgrp_present.sent.infraAccBndlGrp.children.0.infraRsFcIfPol.attributes.tnFcIfPolName == 'fiberchannelpolicy' + - intf_policy_leaf_polgrp_present.sent.infraAccBndlGrp.children.1.infraRsHIfPol.attributes.tnFabricHIfPolName == 'linklevelpolicy' + - intf_policy_leaf_polgrp_idempotent is not changed + - intf_policy_leaf_polgrp_idempotent.sent == {} + - intf_policy_leaf_polgrp_update is changed + - intf_policy_leaf_polgrp_update.sent.infraAccBndlGrp.attributes.descr == 'policygroup description' + +- name: Query interface policy leaf policy group (PC) + cisco.aci.aci_interface_policy_leaf_policy_group: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + policy_group: policygroupname_link + lag_type: link + state: query + register: binding_query + +- name: present assertions + assert: + that: + - binding_query is not changed + - binding_query.current | length >= 1 + - '"/api/mo/uni/infra/funcprof/accbundle-policygroupname_link.json" in binding_query.url' + +- name: Remove interface policy leaf policy group (PC) - check mode + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_link_absent + check_mode: yes + register: intf_policy_leaf_polgrp_check_mode_absent + +- name: Remove interface policy leaf policy group (PC) - delete works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_link_absent + register: intf_policy_leaf_polgrp_absent + +- name: Remove interface policy leaf policy group (PC) - idempotency works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_link_absent + register: intf_policy_leaf_polgrp_absent_idempotent + +- name: Remove interface policy leaf policy group (PC) - check mode + cisco.aci.aci_interface_policy_leaf_policy_group: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + policy_group: policygroupname_link + #lag_type: link + state: absent + ignore_errors: yes + register: intf_policy_leaf_polgrp_absent_missing_param + +- name: absent assertions + assert: + that: + - intf_policy_leaf_polgrp_check_mode_absent is changed + - intf_policy_leaf_polgrp_check_mode_absent.previous != [] + - intf_policy_leaf_polgrp_absent is changed + - intf_policy_leaf_polgrp_absent.previous == intf_policy_leaf_polgrp_absent.previous + - intf_policy_leaf_polgrp_absent_idempotent is not changed + - intf_policy_leaf_polgrp_absent_idempotent.previous == [] + - intf_policy_leaf_polgrp_absent_missing_param is failed + - 'intf_policy_leaf_polgrp_absent_missing_param.msg == "missing required arguments: lag_type"' + +# ==== END TESTING Port Channel (PC), lag_type: link ==== + + +# ==== START TESTING Virtual Port Channel (VPC), lag_type: node ==== + +- name: Making sure interface_policy_leaf_policy_group doesn't exist at beginning of test (VPC) + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_node_absent + +- name: Adding a interface policy leaf policy group (VPC) - check mode works + cisco.aci.aci_interface_policy_leaf_policy_group: &aci_interface_policy_leaf_policy_group_node_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + policy_group: policygroupname_node + lag_type: node + link_level_policy: linklevelpolicy + fibre_channel_interface_policy: fiberchannelpolicy + state: present + check_mode: yes + register: intf_policy_leaf_polgrp_check_mode_present + +- name: Adding a interface policy leaf policy group (VPC) - creation works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_node_present + register: intf_policy_leaf_polgrp_present + +- name: Adding a interface policy leaf policy group (VPC) - idempotency works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_node_present + register: intf_policy_leaf_polgrp_idempotent + +- name: Adding a interface policy leaf policy group description (VPC) - update works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_node_present + description: policygroup description + register: intf_policy_leaf_polgrp_update + +# TODO: also test for errors +- name: present assertions + assert: + that: + - intf_policy_leaf_polgrp_check_mode_present is changed + - intf_policy_leaf_polgrp_present is changed + - intf_policy_leaf_polgrp_present.previous == [] + - intf_policy_leaf_polgrp_present.sent.infraAccBndlGrp.attributes.lagT == 'node' + - intf_policy_leaf_polgrp_present.sent.infraAccBndlGrp.attributes.name == 'policygroupname_node' + - intf_policy_leaf_polgrp_present.sent.infraAccBndlGrp.children.0.infraRsFcIfPol.attributes.tnFcIfPolName == 'fiberchannelpolicy' + - intf_policy_leaf_polgrp_present.sent.infraAccBndlGrp.children.1.infraRsHIfPol.attributes.tnFabricHIfPolName == 'linklevelpolicy' + - intf_policy_leaf_polgrp_present.sent.infraAccBndlGrp.attributes.name == 'policygroupname_node' + - intf_policy_leaf_polgrp_idempotent is not changed + - intf_policy_leaf_polgrp_idempotent.sent == {} + - intf_policy_leaf_polgrp_update is changed + - intf_policy_leaf_polgrp_update.sent.infraAccBndlGrp.attributes.descr == 'policygroup description' + +- name: Query interface policy leaf policy group (VPC) + cisco.aci.aci_interface_policy_leaf_policy_group: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + policy_group: policygroupname_node + lag_type: node + state: query + register: binding_query + +- name: present assertions + assert: + that: + - binding_query is not changed + - binding_query.current | length >= 1 + - '"/api/mo/uni/infra/funcprof/accbundle-policygroupname_node.json" in binding_query.url' + +# Add lag_type link to see what we get back +- name: Adding a interface policy leaf policy group (PC) - creation works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_link_present + register: intf_policy_leaf_polgrp_present + +- name: Query interface policy leaf policy group (VPC) + cisco.aci.aci_interface_policy_leaf_policy_group: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + lag_type: node + state: query + register: binding_query_node_all + +- name: present assertions + assert: + that: + - binding_query_node_all is not changed + - binding_query_node_all.current | length >= 1 + - binding_query_node_all.current | selectattr("infraAccBndlGrp.attributes.lagT", "equalto", "link") | list == [] + - '"/api/class/infraAccBndlGrp.json" in binding_query_node_all.url' + +- name: Remove interface policy leaf policy group (VPC) - check mode + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_node_absent + check_mode: yes + register: intf_policy_leaf_polgrp_check_mode_absent + +- name: Remove interface policy leaf policy group (VPC) - delete works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_node_absent + register: intf_policy_leaf_polgrp_absent + +- name: Remove interface policy leaf policy group (VPC) - idempotency works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_node_absent + register: intf_policy_leaf_polgrp_absent_idempotent + +- name: Remove interface policy leaf policy group (VPC) - check mode + cisco.aci.aci_interface_policy_leaf_policy_group: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + policy_group: policygroupname_node + #lag_type: node + state: absent + ignore_errors: yes + register: intf_policy_leaf_polgrp_absent_missing_param + +- name: absent assertions + assert: + that: + - intf_policy_leaf_polgrp_check_mode_absent is changed + - intf_policy_leaf_polgrp_check_mode_absent.previous != [] + - intf_policy_leaf_polgrp_absent is changed + - intf_policy_leaf_polgrp_absent.previous == intf_policy_leaf_polgrp_absent.previous + - intf_policy_leaf_polgrp_absent_idempotent is not changed + - intf_policy_leaf_polgrp_absent_idempotent.previous == [] + - intf_policy_leaf_polgrp_absent_missing_param is failed + - 'intf_policy_leaf_polgrp_absent_missing_param.msg == "missing required arguments: lag_type"' + +# ==== END TESTING Virtual Port Channel (VPC), lag_type: node ==== + + +# ==== START TESTING Virtual Port Channel (VPC), lag_type: leaf ==== + +- name: Making sure interface_policy_leaf_policy_group doesn't exist at beginning of test (Leaf Access Port) + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_leaf_absent + +- name: Adding a interface policy leaf policy group (Leaf Access Port) - check mode works + cisco.aci.aci_interface_policy_leaf_policy_group: &aci_interface_policy_leaf_policy_group_leaf_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + policy_group: policygroupname_leaf + lag_type: leaf + link_level_policy: linklevelpolicy + fibre_channel_interface_policy: fiberchannelpolicy + state: present + check_mode: yes + register: intf_policy_leaf_polgrp_check_mode_present + +- name: Adding a interface policy leaf policy group (Leaf Access Port) - creation works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_leaf_present + register: intf_policy_leaf_polgrp_present + +- name: Adding a interface policy leaf policy group (Leaf Access Port) - idempotency works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_leaf_present + register: intf_policy_leaf_polgrp_idempotent + +- name: Adding a interface policy leaf policy group description (Leaf Access Port) - update works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_leaf_present + description: policygroup description + register: intf_policy_leaf_polgrp_update + +- name: Adding a interface policy leaf policy group (Leaf Access Port) - null parameter works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_leaf_present + port_channel_policy: null + ignore_errors: yes + register: intf_policy_leaf_polgrp_parameter + +- name: Adding a interface policy leaf policy group (Leaf Access Port) - port_channel_policy not supported error + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_leaf_present + port_channel_policy: "default" + ignore_errors: yes + register: intf_policy_leaf_polgrp_pc_policy_error + +# TODO: also test for errors +- name: present assertions + assert: + that: + - intf_policy_leaf_polgrp_check_mode_present is changed + - intf_policy_leaf_polgrp_present is changed + - intf_policy_leaf_polgrp_present.previous == [] + - intf_policy_leaf_polgrp_present.sent.infraAccPortGrp.attributes.name == 'policygroupname_leaf' + - intf_policy_leaf_polgrp_present.sent.infraAccPortGrp.children.0.infraRsFcIfPol.attributes.tnFcIfPolName == 'fiberchannelpolicy' + - intf_policy_leaf_polgrp_present.sent.infraAccPortGrp.children.1.infraRsHIfPol.attributes.tnFabricHIfPolName == 'linklevelpolicy' + - intf_policy_leaf_polgrp_idempotent is not changed + - intf_policy_leaf_polgrp_idempotent.sent == {} + - intf_policy_leaf_polgrp_update is changed + - intf_policy_leaf_polgrp_update.sent.infraAccPortGrp.attributes.descr == 'policygroup description' + - intf_policy_leaf_polgrp_parameter is not changed + - intf_policy_leaf_polgrp_pc_policy_error.msg == 'port_channel_policy is not a valid parameter for leaf (leaf access port policy group), if used assign null to it (port_channel_policy{{":"}} null).' + +- name: Query interface policy leaf policy group (Leaf Access Port) + cisco.aci.aci_interface_policy_leaf_policy_group: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + policy_group: policygroupname_leaf + lag_type: leaf + state: query + register: binding_query + +- name: present assertions + assert: + that: + - binding_query is not changed + - binding_query.current | length >= 1 + - '"/api/mo/uni/infra/funcprof/accportgrp-policygroupname_leaf.json" in binding_query.url' + +- name: Remove interface policy leaf policy group (Leaf Access Port) - check mode + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_leaf_absent + check_mode: yes + register: intf_policy_leaf_polgrp_check_mode_absent + +- name: Remove interface policy leaf policy group (Leaf Access Port) - delete works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_leaf_absent + register: intf_policy_leaf_polgrp_absent + +- name: Remove interface policy leaf policy group (Leaf Access Port) - idempotency works + cisco.aci.aci_interface_policy_leaf_policy_group: + <<: *aci_interface_policy_leaf_policy_group_leaf_absent + register: intf_policy_leaf_polgrp_absent_idempotent + +- name: Remove interface policy leaf policy group (Leaf Access Port) - check mode + cisco.aci.aci_interface_policy_leaf_policy_group: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + policy_group: policygroupname_leaf + #lag_type: leaf + state: absent + ignore_errors: yes + register: intf_policy_leaf_polgrp_absent_missing_param + +- name: absent assertions + assert: + that: + - intf_policy_leaf_polgrp_check_mode_absent is changed + - intf_policy_leaf_polgrp_check_mode_absent.previous != [] + - intf_policy_leaf_polgrp_absent is changed + - intf_policy_leaf_polgrp_absent.previous == intf_policy_leaf_polgrp_absent.previous + - intf_policy_leaf_polgrp_absent_idempotent is not changed + - intf_policy_leaf_polgrp_absent_idempotent.previous == [] + - intf_policy_leaf_polgrp_absent_missing_param is failed + - 'intf_policy_leaf_polgrp_absent_missing_param.msg == "missing required arguments: lag_type"' + +# ==== END TESTING Virtual Port Channel (VPC), lag_type: leaf ==== diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_profile/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_profile/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_profile/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_profile/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_profile/tasks/main.yml new file mode 100644 index 00000000..9ee75c30 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_leaf_profile/tasks/main.yml @@ -0,0 +1,266 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com> +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +# CLEAN ENVIRONMENT +- name: Remove leaf profile + cisco.aci.aci_interface_policy_leaf_profile: &interface_policy_leaf_profile_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + leaf_interface_profile: ansible_test + state: absent + +- name: Remove leaf_interface_profile fex + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_absent + type: fex + leaf_interface_profile: ansible_test_fex + +# ADD LEAF INTERFACE PROFILE +- name: Add leaf interface profile (check_mode) + cisco.aci.aci_interface_policy_leaf_profile: &interface_policy_leaf_profile_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + interface_profile: ansible_test + state: present + check_mode: yes + register: cm_add_leaf_interface_profile + +- name: Add leaf interface profile (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: *interface_policy_leaf_profile_present + register: nm_add_leaf_interface_profile + +- name: Add leaf interface profile again (check_mode) + cisco.aci.aci_interface_policy_leaf_profile: *interface_policy_leaf_profile_present + check_mode: yes + register: cm_add_leaf_interface_profile_again + +- name: Add leaf interface profile again (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: *interface_policy_leaf_profile_present + register: nm_add_leaf_interface_profile_again + +- name: Add leaf interface profile fex (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_present + type: fex + leaf_interface_profile: ansible_test_fex + register: nm_add_leaf_interface_profile_fex + +- name: Verify add_leaf_interface_profile + assert: + that: + - cm_add_leaf_interface_profile is changed + - nm_add_leaf_interface_profile is changed + - cm_add_leaf_interface_profile_again is not changed + - nm_add_leaf_interface_profile_again is not changed + - nm_add_leaf_interface_profile_fex is changed + +# CHANGE LEAF INTERFACE PROFILE +- name: Change description of leaf interface profile (check_mode) + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_present + description: Ansible test leaf interface profile + check_mode: yes + register: cm_add_leaf_interface_profile_descr + +- name: Change description of leaf interface profile (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_present + description: Ansible test leaf interface profile + register: nm_add_leaf_interface_profile_descr + +- name: Change description of leaf interface profile again (check_mode) + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_present + description: Ansible test leaf interface profile + check_mode: yes + register: cm_add_leaf_interface_profile_descr_again + +- name: Change description of leaf interface profile again (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_present + description: Ansible test leaf interface profile + register: nm_add_leaf_interface_profile_descr_again + +- name: Change description of leaf interface profile fex (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_present + type: fex + description: Ansible test leaf interface profile fex + leaf_interface_profile: ansible_test_fex + register: nm_add_leaf_interface_profile_descr_fex + +- name: Verify add_leaf_interface_profile_descr + assert: + that: + - cm_add_leaf_interface_profile_descr is changed + - nm_add_leaf_interface_profile_descr is changed + - nm_add_leaf_interface_profile_descr_fex is changed + - cm_add_leaf_interface_profile_descr_again is not changed + - nm_add_leaf_interface_profile_descr_again is not changed + +# ADD LEAF INTERFACE PROFILE AGAIN +- name: Add leaf interface profile again with no description (check_mode) + cisco.aci.aci_interface_policy_leaf_profile: *interface_policy_leaf_profile_present + check_mode: yes + register: cm_add_leaf_interface_profile_again_no_descr + +- name: Add leaf interface profile again with no description (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: *interface_policy_leaf_profile_present + register: nm_add_leaf_interface_profile_again_no_descr + +- name: Add leaf interface profile again with no description fex (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_present + type: fex + leaf_interface_profile: ansible_test_fex + register: nm_add_leaf_interface_profile_again_no_descr_fex + +- name: Verify add_leaf_interface_profile_again_no_descr + assert: + that: + - cm_add_leaf_interface_profile_again_no_descr is not changed + - nm_add_leaf_interface_profile_again_no_descr is not changed + - nm_add_leaf_interface_profile_again_no_descr_fex is not changed + +# QUERY ALL LEAF INTERFACE PROFILES +- name: Query all interface profiles (check_mode) + cisco.aci.aci_interface_policy_leaf_profile: &interface_policy_leaf_profile_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_leaf_interface_profiles + +- name: Query all leaf_interface_profiles (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: *interface_policy_leaf_profile_query + register: nm_query_all_leaf_interface_profiles + +- name: Query all fex (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_query + type: fex + +- name: Verify query_all_leaf_interface_profiles + assert: + that: + - cm_query_all_leaf_interface_profiles is not changed + - nm_query_all_leaf_interface_profiles is not changed + # NOTE: Order of leaf_interface_profiles is not stable between calls + #- cm_query_all_leaf_interface_profiles == nm_query_all_leaf_interface_profiles + +# QUERY A LEAF INTERFACE PROFILE +- name: Query our leaf_interface_profile + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_query + leaf_interface_profile: ansible_test + check_mode: yes + register: cm_query_leaf_interface_profile + +- name: Query our leaf_interface_profile + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_query + leaf_interface_profile: ansible_test + register: nm_query_leaf_interface_profile + +- name: Query our leaf_interface_profile fex + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_query + type: fex + leaf_interface_profile: ansible_test_fex + register: nm_query_leaf_interface_profile_fex + +- name: Verify query_leaf_interface_profile + assert: + that: + - cm_query_leaf_interface_profile is not changed + - nm_query_leaf_interface_profile is not changed + - nm_query_leaf_interface_profile_fex is not changed + - cm_query_leaf_interface_profile == nm_query_leaf_interface_profile + +# REMOVE LEAF INTERFACE PROFILE +- name: Remove leaf_interface_profile (check_mode) + cisco.aci.aci_interface_policy_leaf_profile: *interface_policy_leaf_profile_absent + check_mode: yes + register: cm_remove_leaf_interface_profile + +- name: Remove leaf_interface_profile (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: *interface_policy_leaf_profile_absent + register: nm_remove_leaf_interface_profile + +- name: Remove leaf_interface_profile again (check_mode) + cisco.aci.aci_interface_policy_leaf_profile: *interface_policy_leaf_profile_absent + check_mode: yes + register: cm_remove_leaf_interface_profile_again + +- name: Remove leaf_interface_profile again (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: *interface_policy_leaf_profile_absent + register: nm_remove_leaf_interface_profile_again + +- name: Remove leaf_interface_profile fex (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_absent + type: fex + leaf_interface_profile: ansible_test_fex + register: nm_remove_leaf_interface_profile_again_fex + +- name: Verify remove_leaf_interface_profile + assert: + that: + - cm_remove_leaf_interface_profile is changed + - nm_remove_leaf_interface_profile is changed + - nm_remove_leaf_interface_profile_again_fex is changed + - cm_remove_leaf_interface_profile_again is not changed + - nm_remove_leaf_interface_profile_again is not changed + +# QUERY NON-EXISTING LEAF INTERFACE PROFILE +- name: Query non-existing leaf_interface_profile (check_mode) + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_query + leaf_interface_profile: ansible_test + check_mode: yes + register: cm_query_non_leaf_interface_profile + +- name: Query non-existing leaf_interface_profile (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_query + leaf_interface_profile: ansible_test + register: nm_query_non_leaf_interface_profile + +- name: Query non-existing leaf_interface_profile fex (normal mode) + cisco.aci.aci_interface_policy_leaf_profile: + <<: *interface_policy_leaf_profile_query + type: fex + leaf_interface_profile: ansible_test_fex + register: nm_query_non_leaf_interface_profile_fex + +# TODO: Implement more tests +- name: Verify query_non_leaf_interface_profile + assert: + that: + - cm_query_non_leaf_interface_profile is not changed + - nm_query_non_leaf_interface_profile is not changed + - nm_query_non_leaf_interface_profile_fex is not changed + - cm_query_non_leaf_interface_profile == nm_query_non_leaf_interface_profile
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_link_level/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_link_level/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_link_level/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_link_level/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_link_level/tasks/main.yml new file mode 100644 index 00000000..96b7e855 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_link_level/tasks/main.yml @@ -0,0 +1,285 @@ +# Test code for the ACI modules +# Copyright: (c) 2019, Vasily Prokopov (@vasilyprokopov) +# Copyright: (c) 2020, Shreyas Srish (@shrsr) + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +# CLEAN ENVIRONMENT +- name: Remove Link Level Policy + aci_interface_policy_link_level: &interface_policy_link_level_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + link_level_policy: ansible_test + state: absent + +# ADD LINK LEVEL POLICY +- name: Add Link Level Policy (check mode) + aci_interface_policy_link_level: &interface_policy_link_level_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + link_level_policy: ansible_test + state: present + check_mode: yes + register: cm_add_policy + +- name: Add Link Level Policy (normal mode) + aci_interface_policy_link_level: *interface_policy_link_level_present + register: nm_add_policy + +- name: Verify Add Link Level Policy + assert: + that: + - cm_add_policy is changed + - nm_add_policy is changed + - nm_add_policy.previous == cm_add_policy.previous == cm_add_policy.current == [] + - nm_add_policy.current.0.fabricHIfPol.attributes.name == 'ansible_test' + - nm_add_policy.current.0.fabricHIfPol.attributes.dn == 'uni/infra/hintfpol-ansible_test' + - nm_add_policy.proposed.fabricHIfPol.attributes.name == cm_add_policy.proposed.fabricHIfPol.attributes.name == nm_add_policy.sent.fabricHIfPol.attributes.name == 'ansible_test' + +- name: Add Link Level Policy again (check mode) + aci_interface_policy_link_level: *interface_policy_link_level_present + check_mode: yes + register: cm_add_policy_again + +- name: Add Link Level Policy again (normal mode) + aci_interface_policy_link_level: *interface_policy_link_level_present + register: nm_add_policy_again + +- name: Verify Add Link Level Policy again + assert: + that: + - cm_add_policy_again is not changed + - nm_add_policy_again is not changed + - nm_add_policy_again.previous.0.fabricHIfPol.attributes.name == cm_add_policy_again.previous.0.fabricHIfPol.attributes.name == nm_add_policy_again.current.0.fabricHIfPol.attributes.name == cm_add_policy_again.current.0.fabricHIfPol.attributes.name == 'ansible_test' + - nm_add_policy.proposed.fabricHIfPol.attributes.name == cm_add_policy.proposed.fabricHIfPol.attributes.name == nm_add_policy.sent.fabricHIfPol.attributes.name == cm_add_policy.sent.fabricHIfPol.attributes.name == 'ansible_test' + - nm_add_policy_again.sent == cm_add_policy_again.sent == {} + +# CHANGE LINK LEVEL POLICY +- name: Change description of Link Level Policy (check mode) + aci_interface_policy_link_level: + <<: *interface_policy_link_level_present + description: Ansible test Link Level Policy + check_mode: yes + register: cm_add_policy_descr + +- name: Change description of Link Level Policy (normal mode) + aci_interface_policy_link_level: + <<: *interface_policy_link_level_present + description: Ansible test Link Level Policy + register: nm_add_policy_descr + +- name: Verify add_policy_descr + assert: + that: + - cm_add_policy_descr is changed + - nm_add_policy_descr is changed + - cm_add_policy_descr.proposed.fabricHIfPol.attributes.name == nm_add_policy_descr.proposed.fabricHIfPol.attributes.name == 'ansible_test' + - nm_add_policy_descr.current.0.fabricHIfPol.attributes.dn == 'uni/infra/hintfpol-ansible_test' + +- name: Change description of Link Level Policy again (check mode) + aci_interface_policy_link_level: + <<: *interface_policy_link_level_present + description: Ansible test Link Level Policy + check_mode: yes + register: cm_add_policy_descr_again + +- name: Change description of Link Level Policy again (normal mode) + aci_interface_policy_link_level: + <<: *interface_policy_link_level_present + description: Ansible test Link Level Policy + register: nm_add_policy_descr_again + +- name: Verify add_policy_descr_again + assert: + that: + - cm_add_policy_descr_again is not changed + - nm_add_policy_descr_again is not changed + - cm_add_policy_descr_again.proposed.fabricHIfPol.attributes.name == nm_add_policy_descr_again.proposed.fabricHIfPol.attributes.name == 'ansible_test' + - cm_add_policy_descr_again.sent == nm_add_policy_descr_again.sent == {} + - cm_add_policy_descr_again.previous.0.fabricHIfPol.attributes.dn == nm_add_policy_descr_again.previous.0.fabricHIfPol.attributes.dn == cm_add_policy_descr_again.current.0.fabricHIfPol.attributes.dn == nm_add_policy_descr_again.current.0.fabricHIfPol.attributes.dn == 'uni/infra/hintfpol-ansible_test' + +# ADD LINK LEVEL POLICY AGAIN WITH NO DESCRIPTION +- name: Add Link Level Policy again with no description (check mode) + aci_interface_policy_link_level: *interface_policy_link_level_present + check_mode: yes + register: cm_add_policy_again_no_descr + +- name: Add Link Level Policy again with no description (normal mode) + aci_interface_policy_link_level: *interface_policy_link_level_present + register: nm_add_policy_again_no_descr + +- name: Verify add_policy_again_no_descr + assert: + that: + - cm_add_policy_again_no_descr is not changed + - nm_add_policy_again_no_descr is not changed + - cm_add_policy_again_no_descr.proposed.fabricHIfPol.attributes.name == nm_add_policy_again_no_descr.proposed.fabricHIfPol.attributes.name == 'ansible_test' + - cm_add_policy_again_no_descr.sent == nm_add_policy_again_no_descr.sent == {} + - cm_add_policy_again_no_descr.previous.0.fabricHIfPol.attributes.dn== nm_add_policy_again_no_descr.previous.0.fabricHIfPol.attributes.dn == cm_add_policy_again_no_descr.current.0.fabricHIfPol.attributes.dn == nm_add_policy_again_no_descr.current.0.fabricHIfPol.attributes.dn == 'uni/infra/hintfpol-ansible_test' + +# QUERY ALL LINK LEVEL POLICIES +- name: Query all Link Level Policies (check mode) + aci_interface_policy_link_level: &interface_policy_link_level_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_policies + +- name: Query all Link Level Policies (normal mode) + aci_interface_policy_link_level: *interface_policy_link_level_query + register: nm_query_all_policies + +- name: Verify query_all_policies + assert: + that: + - cm_query_all_policies is not changed + - nm_query_all_policies is not changed + - cm_query_all_policies == nm_query_all_policies + - nm_query_all_policies.current|length >= 1 + +# QUERY A LINK LEVEL POLICY +- name: Query our Link Level Policy (check mode) + aci_interface_policy_link_level: + <<: *interface_policy_link_level_query + link_level_policy: ansible_test + check_mode: yes + register: cm_query_policy + +- name: Query our Link Level Policy (normal mode) + aci_interface_policy_link_level: + <<: *interface_policy_link_level_query + link_level_policy: ansible_test + register: nm_query_policy + +- name: Verify query_policy + assert: + that: + - cm_query_policy is not changed + - nm_query_policy is not changed + - cm_query_policy == nm_query_policy + - nm_query_policy.current.0.fabricHIfPol.attributes.descr == 'Ansible test Link Level Policy' + - nm_query_policy.current.0.fabricHIfPol.attributes.dn == 'uni/infra/hintfpol-ansible_test' + - nm_query_policy.current.0.fabricHIfPol.attributes.name == 'ansible_test' + +# REMOVE LINK LEVEL POLICY +- name: Remove Link Level Policy (check mode) + aci_interface_policy_link_level: *interface_policy_link_level_absent + check_mode: yes + register: cm_remove_policy + +- name: Remove Link Level Policy (normal mode) + aci_interface_policy_link_level: *interface_policy_link_level_absent + register: nm_remove_policy + +- name: Verify remove_policy + assert: + that: + - cm_remove_policy is changed + - nm_remove_policy is changed + - cm_remove_policy.proposed == nm_remove_policy.proposed == {} + - cm_remove_policy.sent == nm_remove_policy.sent == {} + - cm_remove_policy.previous.0.fabricHIfPol.attributes.dn == nm_remove_policy.previous.0.fabricHIfPol.attributes.dn == cm_remove_policy.current.0.fabricHIfPol.attributes.dn == 'uni/infra/hintfpol-ansible_test' + - nm_remove_policy.current == [] + +- name: Remove Link Level Policy again (check mode) + aci_interface_policy_link_level: *interface_policy_link_level_absent + check_mode: yes + register: cm_remove_policy_again + +- name: Remove Link Level Policy again (normal mode) + aci_interface_policy_link_level: *interface_policy_link_level_absent + register: nm_remove_policy_again + +- name: Verify remove_policy_again + assert: + that: + - cm_remove_policy_again is not changed + - nm_remove_policy_again is not changed + - cm_remove_policy_again.proposed == nm_remove_policy_again.proposed == {} + - cm_remove_policy_again.sent == nm_remove_policy_again.sent == {} + - cm_remove_policy_again.previous == nm_remove_policy_again.previous == cm_remove_policy_again.current == nm_remove_policy_again.current == [] + +# QUERY NON-EXISTING LINK LEVEL POLICY +- name: Query non-existing Link Level Policy (check mode) + aci_interface_policy_link_level: + <<: *interface_policy_link_level_query + link_level_policy: ansible_test + check_mode: yes + register: cm_query_non_policy + +- name: Query non-existing Link Level Policy (normal mode) + aci_interface_policy_link_level: + <<: *interface_policy_link_level_query + link_level_policy: ansible_test + register: nm_query_non_policy + +- name: Verify query_non_policy + assert: + that: + - cm_query_non_policy is not changed + - nm_query_non_policy is not changed + - cm_query_non_policy == nm_query_non_policy + - cm_query_non_policy.current == nm_query_non_policy.current == [] + +# PROVOKE ERRORS - REQUIRED PARAMETER MISSING +- name: Error when required parameter is missing + aci_interface_policy_link_level: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: present + ignore_errors: yes + register: error_on_missing_required_param + +- name: Verify error_on_missing_required_param + assert: + that: + - error_on_missing_required_param is failed + - 'error_on_missing_required_param.msg == "state is present but all of the following are missing: link_level_policy"' + +# PROVOKE ERRORS - DEBOUNCE OUT OF RANGE +- name: Error when link debounce interval is out of range + aci_interface_policy_link_level: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: present + link_level_policy: ansible_test + link_debounce_interval: 5005 + ignore_errors: yes + register: error_on_debounce_out_of_range + +- name: Verify error_on_debounce_out_of_range + assert: + that: + - error_on_debounce_out_of_range is failed + - 'error_on_debounce_out_of_range.msg == "The \"link_debounce_interval\" must be a value between 0 and 5000"'
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_ospf/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_ospf/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_ospf/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_ospf/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_ospf/tasks/main.yml new file mode 100644 index 00000000..32ac266e --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_ospf/tasks/main.yml @@ -0,0 +1,231 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + + +# CLEAN ENVIRONMENT +- name: Ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + tenant: anstest + state: present + register: tenant_present + +- name: Remove OSPF interface policy + cisco.aci.aci_interface_policy_ospf: &interface_policy_ospf_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: anstest + ospf: ansible_ospf + state: absent + + +# ADD OSPF INTERFACE POLICY +- name: Add ospf interface policy (check_mode) + cisco.aci.aci_interface_policy_ospf: &interface_policy_ospf_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: anstest + ospf: ansible_ospf + state: present + check_mode: yes + register: cm_add_ospf_interface_policy + +- name: Add ospf interface policy (normal mode) + cisco.aci.aci_interface_policy_ospf: *interface_policy_ospf_present + register: nm_add_ospf_interface_policy + +- name: Add ospf interface policy again (check_mode) + cisco.aci.aci_interface_policy_ospf: *interface_policy_ospf_present + check_mode: yes + register: cm_add_ospf_interface_policy_again + +- name: Add ospf interface policy again (normal mode) + cisco.aci.aci_interface_policy_ospf: *interface_policy_ospf_present + register: nm_add_ospf_interface_policy_again + +- name: Verify add_ospf_interface_policy + assert: + that: + - cm_add_ospf_interface_policy is changed + - nm_add_ospf_interface_policy is changed + - cm_add_ospf_interface_policy_again is not changed + - nm_add_ospf_interface_policy_again is not changed + + +# CHANGE OSPF INTERFACE POLICY +- name: Change description of ospf interface policy (check_mode) + cisco.aci.aci_interface_policy_ospf: + <<: *interface_policy_ospf_present + description: Ansible test ospf interface policy + check_mode: yes + register: cm_add_ospf_descr + +- name: Change description of ospf interface policy (normal mode) + cisco.aci.aci_interface_policy_ospf: + <<: *interface_policy_ospf_present + description: Ansible test ospf interface policy + register: nm_add_ospf_descr + +- name: Change description of ospf interface policy again (check_mode) + cisco.aci.aci_interface_policy_ospf: + <<: *interface_policy_ospf_present + description: Ansible test ospf interface policy + check_mode: yes + register: cm_add_ospf_descr_again + +- name: Change description of ospf interface policy again (normal mode) + cisco.aci.aci_interface_policy_ospf: + <<: *interface_policy_ospf_present + description: Ansible test ospf interface policy + register: nm_add_ospf_descr_again + +- name: Verify add_ospf_descr + assert: + that: + - cm_add_ospf_descr is changed + - nm_add_ospf_descr is changed + - cm_add_ospf_descr_again is not changed + - nm_add_ospf_descr_again is not changed + + +# ADD OSPF INTERFACE POLICY AGAIN +- name: Add ospf interface policy again with no description (check_mode) + cisco.aci.aci_interface_policy_ospf: *interface_policy_ospf_present + check_mode: yes + register: cm_add_ospf_again_no_descr + +- name: Add ospf interface policy again with no description (normal mode) + cisco.aci.aci_interface_policy_ospf: *interface_policy_ospf_present + register: nm_add_ospf_again_no_descr + +- name: Verify add_ospf_again_no_descr + assert: + that: + - cm_add_ospf_again_no_descr is not changed + - nm_add_ospf_again_no_descr is not changed + + +# QUERY ALL OSPF INTERFACE POLICIES +- name: Query all ospf interface policies (check_mode) + cisco.aci.aci_interface_policy_ospf: &interface_policy_ospf_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: anstest + state: query + check_mode: yes + register: cm_query_all_ospfs + +- name: Query all ospfs (normal mode) + cisco.aci.aci_interface_policy_ospf: *interface_policy_ospf_query + register: nm_query_all_ospfs + +- name: Verify query_all_ospfs + assert: + that: + - cm_query_all_ospfs is not changed + - nm_query_all_ospfs is not changed + # NOTE: Order of ospfs is not stable between calls + #- cm_query_all_ospfs == nm_query_all_ospfs + + +# QUERY A OSPF INTERFACE POLICY +- name: Query our ospf + cisco.aci.aci_interface_policy_ospf: + <<: *interface_policy_ospf_query + tenant: anstest + ospf: ansible_ospf + check_mode: yes + register: cm_query_ospf + +- name: Query our ospf + cisco.aci.aci_interface_policy_ospf: + <<: *interface_policy_ospf_query + tenant: anstest + ospf: ansible_ospf + register: nm_query_ospf + +- name: Verify query_ospf + assert: + that: + - cm_query_ospf is not changed + - nm_query_ospf is not changed + - cm_query_ospf == nm_query_ospf + + +# REMOVE OSPF INTERFACE POLICY +- name: Remove ospf (check_mode) + cisco.aci.aci_interface_policy_ospf: *interface_policy_ospf_absent + check_mode: yes + register: cm_remove_ospf + +- name: Remove ospf (normal mode) + cisco.aci.aci_interface_policy_ospf: *interface_policy_ospf_absent + register: nm_remove_ospf + +- name: Remove ospf again (check_mode) + cisco.aci.aci_interface_policy_ospf: *interface_policy_ospf_absent + check_mode: yes + register: cm_remove_ospf_again + +- name: Remove ospf again (normal mode) + cisco.aci.aci_interface_policy_ospf: *interface_policy_ospf_absent + register: nm_remove_ospf_again + +- name: Verify remove_ospf + assert: + that: + - cm_remove_ospf is changed + - nm_remove_ospf is changed + - cm_remove_ospf_again is not changed + - nm_remove_ospf_again is not changed + + +# QUERY NON-EXISTING OSPF INTERFACE POLICY +- name: Query non-existing ospf (check_mode) + cisco.aci.aci_interface_policy_ospf: + <<: *interface_policy_ospf_query + ospf: ansible_ospf + check_mode: yes + register: cm_query_non_ospf + +- name: Query non-existing ospf (normal mode) + cisco.aci.aci_interface_policy_ospf: + <<: *interface_policy_ospf_query + ospf: ansible_ospf + register: nm_query_non_ospf + +# TODO: Implement more tests +- name: Verify query_non_ospf + assert: + that: + - cm_query_non_ospf is not changed + - nm_query_non_ospf is not changed + - cm_query_non_ospf == nm_query_non_ospf diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_port_security/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_port_security/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_port_security/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_port_security/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_port_security/tasks/main.yml new file mode 100644 index 00000000..92a90d32 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_policy_port_security/tasks/main.yml @@ -0,0 +1,171 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + + +# CLEAN ENVIRONMENT +- name: Remove policy port security + aci_interface_policy_port_security: + <<: *aci_info + port_security: '{{ item }}' + state: absent + loop: + - security1 + - security2 + - security3 + +# ADD +- name: Add a port security interface policy - check mode + aci_interface_policy_port_security: + <<: *aci_info + port_security: security1 + description: security 1 + max_end_points: 300 + port_security_timeout: 190 + state: present + check_mode: yes + register: port_security1_cm + +- name: Add a port security interface policy - normal mode + aci_interface_policy_port_security: + <<: *aci_info + port_security: security1 + description: security 1 + max_end_points: 300 + port_security_timeout: 190 + state: present + register: port_security1_nm + +- name: Add a port security interface policy again - normal mode + aci_interface_policy_port_security: + <<: *aci_info + port_security: security1 + description: security 1 + max_end_points: 300 + port_security_timeout: 190 + state: present + register: port_security1_nm_again + +- name: Add a port security interface policy - normal mode + aci_interface_policy_port_security: + <<: *aci_info + port_security: security2 + description: security 2 + max_end_points: 300 + port_security_timeout: 10 + state: present + ignore_errors: yes + register: port_security1_nm_error_timeout + +- name: Add a port security interface policy - normal mode + aci_interface_policy_port_security: + <<: *aci_info + port_security: security3 + description: security 3 + max_end_points: 15000 + port_security_timeout: 60 + state: present + ignore_errors: yes + register: port_security1_nm_error_max + +- name: Add a port security interface policy again for security 3- normal mode + aci_interface_policy_port_security: + <<: *aci_info + port_security: security3 + description: security 3 + max_end_points: 12000 + port_security_timeout: 60 + state: present + ignore_errors: yes + register: port_security1_nm_with_no_error + +- name: Verify present cases + assert: + that: + - port_security1_cm is changed + - port_security1_nm is changed + - port_security1_cm.proposed.l2PortSecurityPol.attributes.dn == "uni/infra/portsecurityP-security1" + - port_security1_cm.proposed.l2PortSecurityPol.attributes.name == "security1" + - port_security1_cm.proposed.l2PortSecurityPol.attributes.timeout == "190" + - port_security1_cm.proposed.l2PortSecurityPol.attributes.maximum == "300" + - port_security1_cm.proposed.l2PortSecurityPol.attributes.descr == "security 1" + - port_security1_nm.current.0.l2PortSecurityPol.attributes.dn == "uni/infra/portsecurityP-security1" + - port_security1_nm.current.0.l2PortSecurityPol.attributes.name == "security1" + - port_security1_nm.current.0.l2PortSecurityPol.attributes.timeout == "190" + - port_security1_nm.current.0.l2PortSecurityPol.attributes.maximum == "300" + - port_security1_nm.current.0.l2PortSecurityPol.attributes.descr == "security 1" + - port_security1_nm_again is not changed + - port_security1_nm_error_timeout.msg == "The port_security_timeout must be between 60 and 3600" + - port_security1_nm_error_max.msg == "The max_end_points must be between 0 and 12000" + +# QUERY +- name: Query a port security interface policy - normal mode + aci_interface_policy_port_security: + <<: *aci_info + port_security: security1 + state: query + register: port_security1_query + +- name: Query all port security interface policies - normal mode + aci_interface_policy_port_security: + <<: *aci_info + state: query + register: port_all_query + +- name: Verify query cases + assert: + that: + - port_security1_query is not changed + - port_all_query is not changed + - port_security1_query.current.0.l2PortSecurityPol.attributes.dn == "uni/infra/portsecurityP-security1" + - port_security1_query.current.0.l2PortSecurityPol.attributes.name == "security1" + - port_security1_query.current.0.l2PortSecurityPol.attributes.timeout == "190" + - port_security1_query.current.0.l2PortSecurityPol.attributes.maximum == "300" + - port_security1_query.current.0.l2PortSecurityPol.attributes.descr == "security 1" + - port_all_query.current|length >= 1 + +# REMOVE +- name: Remove a port security interface policy - normal mode + aci_interface_policy_port_security: + <<: *aci_info + port_security: security1 + state: absent + register: port_security1_remove + +- name: Verify remove cases + assert: + that: + - port_security1_remove is changed + - port_security1_remove.previous.0.l2PortSecurityPol.attributes.dn == "uni/infra/portsecurityP-security1" + - port_security1_remove.previous.0.l2PortSecurityPol.attributes.name == "security1" + - port_security1_remove.previous.0.l2PortSecurityPol.attributes.timeout == "190" + - port_security1_remove.previous.0.l2PortSecurityPol.attributes.maximum == "300" + - port_security1_remove.previous.0.l2PortSecurityPol.attributes.descr == "security 1" + +# CLEAN END +- name: Remove all policy port securities + aci_interface_policy_port_security: + <<: *aci_info + port_security: '{{ item }}' + state: absent + loop: + - security1 + - security2 + - security3
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_selector_to_switch_policy_leaf_profile/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_selector_to_switch_policy_leaf_profile/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_selector_to_switch_policy_leaf_profile/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_selector_to_switch_policy_leaf_profile/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_selector_to_switch_policy_leaf_profile/tasks/main.yml new file mode 100644 index 00000000..d72a9ffa --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_interface_selector_to_switch_policy_leaf_profile/tasks/main.yml @@ -0,0 +1,154 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: delete Switch Policy Leaf profile for kick off + cisco.aci.aci_switch_policy_leaf_profile: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + leaf_profile: swleafprftest + state: absent + +- name: delete Interface Policy Leaf profile for kick off + cisco.aci.aci_interface_policy_leaf_profile: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + leaf_interface_profile: leafintprftest + state: absent + +- name: Ensuring Switch Policy Leaf profile exists for kick off + cisco.aci.aci_switch_policy_leaf_profile: &aci_switch_policy_leaf_profile_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + leaf_profile: swleafprftest + state: present + register: leaf_profile_present + +- name: Ensuring Interface Policy Leaf profile exists for kick off + cisco.aci.aci_interface_policy_leaf_profile: &aci_interface_policy_leaf_profile_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + leaf_interface_profile: leafintprftest + state: present + register: leaf_profile_present + +- name: Bind an Interface Selector to a Switch Policy Leaf Profile - check mode works + cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile: &aci_interface_selector_to_switch_policy_leaf_profile_present + <<: *aci_switch_policy_leaf_profile_present + interface_selector: leafintprftest + check_mode: yes + register: intftoleaf_check_mode_present + +- name: Bind an Interface Selector to a Switch Policy Leaf Profile - creation works + cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile: + <<: *aci_interface_selector_to_switch_policy_leaf_profile_present + register: intftoleaf_present + +- name: Bind an Interface Selector to a Switch Policy Leaf Profile - idempotency works + cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile: + <<: *aci_interface_selector_to_switch_policy_leaf_profile_present + register: intftoleaf_idempotent + +# TODO: also test for errors +- name: present assertions + assert: + that: + - intftoleaf_check_mode_present is changed + - intftoleaf_present is changed + - intftoleaf_present.previous == [] + - intftoleaf_present.sent.infraRsAccPortP.attributes.tDn == 'uni/infra/accportprof-leafintprftest' + - intftoleaf_idempotent is not changed + - intftoleaf_idempotent.sent == {} + +- name: Query an interface selector profile associated with a switch policy leaf profile + cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile: + <<: *aci_switch_policy_leaf_profile_present + interface_selector: leafintprftest + state: query + register: binding_query + +- name: query assertions + assert: + that: + - binding_query is not changed + - binding_query.current | length >= 1 + - '"api/mo/uni/infra/nprof-swleafprftest/rsaccPortP-[uni/infra/accportprof-leafintprftest].json" in binding_query.url' + +- name: Remove binding of interface access port selector and Interface Policy Leaf Profile - check mode + cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile: &aci_interface_selector_to_switch_policy_leaf_profile_absent + <<: *aci_switch_policy_leaf_profile_present + interface_selector: leafintprftest + state: absent + check_mode: yes + register: intftoleaf_check_mode_absent + +- name: Remove binding of interface access port selector and Interface Policy Leaf Profile - delete works + cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile: + <<: *aci_interface_selector_to_switch_policy_leaf_profile_absent + register: intftoleaf_absent + +- name: Remove binding of interface access port selector and Interface Policy Leaf Profile - idempotency works + cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile: + <<: *aci_interface_selector_to_switch_policy_leaf_profile_absent + register: intftoleaf_absent_idempotent + +- name: Remove binding of interface access port selector and Interface Policy Leaf Profile - check mode + cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile: + <<: *aci_switch_policy_leaf_profile_present + state: absent + ignore_errors: yes + register: intftoleaf_absent_missing_param + +- name: absent assertions + assert: + that: + - intftoleaf_check_mode_absent is changed + - intftoleaf_check_mode_absent.previous != [] + - intftoleaf_absent is changed + - intftoleaf_absent.previous == intftoleaf_check_mode_absent.previous + - intftoleaf_absent_idempotent is not changed + - intftoleaf_absent_idempotent.previous == [] + - intftoleaf_absent_missing_param is failed + - 'intftoleaf_absent_missing_param.msg == "state is absent but all of the following are missing: interface_selector"' + +- name: Remove an interface selector associated with a Switch Policy Leaf Profile - Clean up + cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile: + <<: *aci_interface_selector_to_switch_policy_leaf_profile_absent + state: absent + +- name: delete Switch Policy Leaf profile - Clean up + cisco.aci.aci_switch_policy_leaf_profile: + <<: *aci_switch_policy_leaf_profile_present + state: absent + +- name: delete Interface Policy Leaf profile - Clean up + cisco.aci.aci_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_present + leaf_interface_profile: leafintprftest + state: absent diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l2out/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l2out/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l2out/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l2out/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l2out/tasks/main.yml new file mode 100644 index 00000000..103e7034 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l2out/tasks/main.yml @@ -0,0 +1,129 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Shreyas Srish (@shrsr) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Add a new tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + +- name: Add a new L2Out + aci_l2out: + <<: *aci_info + tenant: ansible_tenant + l2out: ansible_l2out + description: Test deployment + bd: ansible_bd + domain: l2Dom + vlan: 3200 + state: present + register: add_l2out + +- name: Verify add_l2out + assert: + that: + - add_l2out.current.0.l2extOut.attributes.dn == "uni/tn-ansible_tenant/l2out-ansible_l2out" + - add_l2out.current.0.l2extOut.attributes.name == "ansible_l2out" + +- name: Add the L2Out again + aci_l2out: + <<: *aci_info + tenant: ansible_tenant + l2out: ansible_l2out + description: Test deployment + bd: ansible_bd + domain: l2Dom + vlan: 3200 + state: present + register: add_l2out_again + +- name: Verify add_l2out_again + assert: + that: + - add_l2out_again is not changed + +- name: Add a new L2Out + aci_l2out: + <<: *aci_info + tenant: ansible_tenant + l2out: ansible_l2out_2 + description: Test deployment + bd: ansible_bd + domain: l2Dom + vlan: 3200 + state: present + register: add_l2out_2 + +- name: Verify add_l2out_2 + assert: + that: + - add_l2out_2.current.0.l2extOut.attributes.dn == "uni/tn-ansible_tenant/l2out-ansible_l2out_2" + - add_l2out_2.current.0.l2extOut.attributes.name == "ansible_l2out_2" + +- name: Query the L2Out + aci_l2out: + <<: *aci_info + tenant: ansible_tenant + l2out: ansible_l2out + state: query + register: query_l2out + +- name: Verify query_l2out + assert: + that: + - query_l2out is not changed + - query_l2out.current.0.l2extOut.attributes.dn == "uni/tn-ansible_tenant/l2out-ansible_l2out" + - query_l2out.current.0.l2extOut.attributes.name == "ansible_l2out" + +- name: Query all l2outs under a specific tenant + aci_l2out: + <<: *aci_info + tenant: ansible_tenant + state: query + register: query_l2out_all + +- name: Verify query_l2out_all + assert: + that: + - query_l2out_all is not changed + +- name: Remove the L2Out + aci_l2out: + <<: *aci_info + tenant: ansible_tenant + l2out: ansible_l2out + state: absent + register: remove_l2out + +- name: Verify remove_l2out + assert: + that: + - remove_l2out is changed + - remove_l2out.previous.0.l2extOut.attributes.dn == "uni/tn-ansible_tenant/l2out-ansible_l2out" + - remove_l2out.previous.0.l2extOut.attributes.name == "ansible_l2out"
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l2out_extepg/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l2out_extepg/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l2out_extepg/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l2out_extepg/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l2out_extepg/tasks/main.yml new file mode 100644 index 00000000..7a01fb84 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l2out_extepg/tasks/main.yml @@ -0,0 +1,156 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Shreyas Srish (@shrsr) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Add a new tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + +- name: Add New L2Out + aci_l2out: + <<: *aci_info + tenant: ansible_tenant + l2out: ansible_l2out + description: Ansible Test + bd: ansible_bd + domain: l2Dom + vlan: 3200 + state: present + register: add_l2out + +- name: Add another L2Out + aci_l2out: + <<: *aci_info + tenant: ansible_tenant + l2out: ansible_l2out_2 + description: Ansible Test + bd: ansible_bd + domain: l2Dom + vlan: 3200 + state: present + register: add_l2out_2 + +- name: Add L2 external end point group + aci_l2out_extepg: + <<: *aci_info + tenant: ansible_tenant + l2out: ansible_l2out + extepg: ansible_extepg + description: Ansible external epg + preferred_group: True + state: present + register: l2extepg + +- name: Verify l2extepg + assert: + that: + - l2extepg.current.0.l2extInstP.attributes.dn == "uni/tn-ansible_tenant/l2out-ansible_l2out/instP-ansible_extepg" + +- name: Add L2 external end point group again + aci_l2out_extepg: + <<: *aci_info + tenant: ansible_tenant + l2out: ansible_l2out + extepg: ansible_extepg + description: Ansible external epg + preferred_group: True + state: present + register: l2extepg_again + +- name: Verify l2extepg_again + assert: + that: + - l2extepg_again is not changed + - l2extepg.current.0.l2extInstP.attributes.dn == "uni/tn-ansible_tenant/l2out-ansible_l2out/instP-ansible_extepg" + +- name: Add another L2 external end point group + aci_l2out_extepg: + <<: *aci_info + tenant: ansible_tenant + l2out: ansible_l2out_2 + extepg: ansible_extepg_2 + description: Ansible external epg + qos_class: level1 + preferred_group: True + state: present + register: l2extepg_2 + +- name: Verify l2extepg_2 + assert: + that: + - l2extepg_2.current.0.l2extInstP.attributes.dn == "uni/tn-ansible_tenant/l2out-ansible_l2out_2/instP-ansible_extepg_2" + +- name: Query the L2 external end point group + aci_l2out_extepg: + <<: *aci_info + tenant: ansible_tenant + l2out: ansible_l2out + extepg: ansible_extepg + state: query + register: query_l2extepg + +- name: Query all L2 external epg in a tenant + aci_l2out_extepg: + <<: *aci_info + tenant: ansible_tenant + state: query + register: query_all_in_tenant + +- name: Verify query_all_in_tenant + assert: + that: + - query_all_in_tenant is not changed + +- name: Query all L2 external epgs + aci_l2out_extepg: + <<: *aci_info + state: query + register: query_all + +- name: Verify query_all + assert: + that: + - query_all is not changed + +- name: Remove L2 external end point group + aci_l2out_extepg: + <<: *aci_info + tenant: ansible_tenant + l2out: ansible_l2out + extepg: ansible_extepg + preferred_group: True + state: absent + register: remove_l2extepg + +- name: Verify remove_l2extepg + assert: + that: + - remove_l2extepg is changed + - remove_l2extepg.previous.0.l2extInstP.attributes.dn == "uni/tn-ansible_tenant/l2out-ansible_l2out/instP-ansible_extepg"
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_extepg_to_contract/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_extepg_to_contract/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_extepg_to_contract/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_extepg_to_contract/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_extepg_to_contract/tasks/main.yml new file mode 100644 index 00000000..4ee94126 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_extepg_to_contract/tasks/main.yml @@ -0,0 +1,145 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Shreyas Srish (@shrsr) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Add a new tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + +- name: Add a new l3out + aci_l3out: + <<: *aci_info + tenant: ansible_tenant + name: ansible_l3out + description: l3out for Ansible tenant + domain: ansible_dom + route_control: export + vrf: ansible_vrf + l3protocol: ospf + state: present + +- name: Add a new ExtEpg + aci_l3out_extepg: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + name: ansible_extEpg + description: ExtEpg for Ansible l3out + state: present + +- name: Bind External End Point Groups to Contracts + aci_l3out_extepg_to_contract: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + contract: ansible_contract + contract_type: provider + state: present + register: bind_extepg_provider_contract + +- name: Verify bind_extepg_provider_contract + assert: + that: + - bind_extepg_provider_contract.current.0.fvRsProv.attributes.dn == "uni/tn-ansible_tenant/out-ansible_l3out/instP-ansible_extEpg/rsprov-ansible_contract" + +- name: Bind second External End Point Groups to Contracts + aci_l3out_extepg_to_contract: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + contract: ansible_contract2 + contract_type: provider + state: present + register: bind_extepg_provider_contract_2 + +- name: Verify bind_extepg_provider_contract_2 + assert: + that: + - bind_extepg_provider_contract_2.current.0.fvRsProv.attributes.dn == "uni/tn-ansible_tenant/out-ansible_l3out/instP-ansible_extEpg/rsprov-ansible_contract2" + +- name: Query the External End Point Groups + aci_l3out_extepg_to_contract: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + contract: ansible_contract + contract_type: provider + state: query + register: query_extepg + +- name: Verify query_extepg + assert: + that: + - query_extepg is not changed + - query_extepg.current.0.fvRsProv.attributes.dn == "uni/tn-ansible_tenant/out-ansible_l3out/instP-ansible_extEpg/rsprov-ansible_contract" + +- name: Query all the External End Point Groups + aci_l3out_extepg_to_contract: + <<: *aci_info + contract_type: provider + state: query + register: query_all + +- name: Remove existing contract to External End Point Groups + aci_l3out_extepg_to_contract: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + contract: ansible_contract + contract_type: provider + state: absent + register: remove_contract_extepg + +- name: Verify remove_contract_extepg + assert: + that: + - remove_contract_extepg.previous.0.fvRsProv.attributes.dn == "uni/tn-ansible_tenant/out-ansible_l3out/instP-ansible_extEpg/rsprov-ansible_contract" + +- name: Bind External End Point Groups to Contracts + aci_l3out_extepg_to_contract: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + contract: ansible_contract + contract_type: consumer + provider_match: all + state: present + ignore_errors: yes + register: bind_extepg_consumer_contract + +- name: Verify bind_extepg_consumer_contract + assert: + that: + - bind_extepg_consumer_contract.msg == "the 'provider_match' is only configurable for Provided Contracts"
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_extsubnet/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_extsubnet/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_extsubnet/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_extsubnet/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_extsubnet/tasks/main.yml new file mode 100644 index 00000000..48796267 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_extsubnet/tasks/main.yml @@ -0,0 +1,210 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Cindy Zhao (@cizhao) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Add a new tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + +- name: Add a new l3out + aci_l3out: + <<: *aci_info + tenant: ansible_tenant + name: ansible_l3out + description: l3out for Ansible tenant + domain: ansible_dom + route_control: export + vrf: ansible_vrf + l3protocol: ospf + state: present + +- name: Add a new ExtEpg + aci_l3out_extepg: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + name: ansible_extEpg + description: ExtEpg for Ansible l3out + state: present + +- name: Add a subnet (check mode) + cisco.aci.aci_l3out_extsubnet: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + subnet_name: test + network: 192.0.2.0/24 + state: present + check_mode: yes + register: cm_add_subnet + +- name: Add a subnet (normal mode) + cisco.aci.aci_l3out_extsubnet: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + subnet_name: test + network: 192.0.2.0/24 + state: present + register: nm_add_subnet + +- name: Verify cm_add_subnet and nm_add_subnet + assert: + that: + - cm_add_subnet is changed + - nm_add_subnet is changed + - cm_add_subnet.proposed.l3extSubnet.attributes.ip == "192.0.2.0/24" + - cm_add_subnet.proposed.l3extSubnet.attributes.name == "test" + - cm_add_subnet.proposed.l3extSubnet.attributes.scope == "import-security" + - nm_add_subnet.current.0.l3extSubnet.attributes.ip == "192.0.2.0/24" + - nm_add_subnet.current.0.l3extSubnet.attributes.name == "test" + - nm_add_subnet.current.0.l3extSubnet.attributes.scope == "import-security" + +- name: Add subnet again + cisco.aci.aci_l3out_extsubnet: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + subnet_name: test + network: 192.0.2.0/24 + state: present + register: nm_add_subnet_again + +- name: Verify nm_add_subnet_again + assert: + that: + - nm_add_subnet_again is not changed + +- name: Change subnet (check_mode) + cisco.aci.aci_l3out_extsubnet: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + network: 192.0.2.0/24 + subnet_name: ansible_test + description: description for subnet + scope: [ shared-security, import-security ] + state: present + check_mode: yes + register: cm_change_subnet + +- name: Change subnet (check_mode) + cisco.aci.aci_l3out_extsubnet: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + network: 192.0.2.0/24 + subnet_name: ansible_test + description: description for subnet + scope: [ shared-security, import-security ] + state: present + register: nm_change_subnet + +- name: Verify cm_change_subnet and nm_change_subnet + assert: + that: + - cm_change_subnet is changed + - nm_change_subnet is changed + - cm_change_subnet.previous.0.l3extSubnet.attributes.descr == nm_change_subnet.previous.0.l3extSubnet.attributes.descr == "" + - cm_change_subnet.previous.0.l3extSubnet.attributes.name == nm_change_subnet.previous.0.l3extSubnet.attributes.name == "test" + - cm_change_subnet.previous.0.l3extSubnet.attributes.scope == nm_change_subnet.previous.0.l3extSubnet.attributes.scope == "import-security" + - cm_change_subnet.proposed.l3extSubnet.attributes.descr == "description for subnet" + - cm_change_subnet.proposed.l3extSubnet.attributes.name == "ansible_test" + - cm_change_subnet.proposed.l3extSubnet.attributes.scope == "import-security,shared-security" + - nm_change_subnet.current.0.l3extSubnet.attributes.descr == "description for subnet" + - nm_change_subnet.current.0.l3extSubnet.attributes.name == "ansible_test" + - nm_change_subnet.current.0.l3extSubnet.attributes.scope == "import-security,shared-security" + +- name: Add another subnet (normal mode) + cisco.aci.aci_l3out_extsubnet: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + network: 192.1.2.0/24 + state: present + register: nm_add_another_subnet + +- name: Verify nm_add_another_subnet + assert: + that: + - nm_add_another_subnet is changed + - nm_add_another_subnet.current.0.l3extSubnet.attributes.ip == "192.1.2.0/24" + +- name: Query all subnets + cisco.aci.aci_l3out_extsubnet: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + state: query + register: query_all + +- name: Query specific subnet + cisco.aci.aci_l3out_extsubnet: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + network: 192.0.2.0/24 + state: query + register: query_subnet + +- name: Verify query_all and query_subnet + assert: + that: + - query_all is not changed + - query_subnet is not changed + - query_all.current.0.l3extInstP.children | length == 2 + - query_subnet.current.0.l3extSubnet.attributes.name == "ansible_test" + - query_subnet.current.0.l3extSubnet.attributes.ip == "192.0.2.0/24" + +- name: Remove subnet + cisco.aci.aci_l3out_extsubnet: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + network: 192.0.2.0/24 + state: absent + register: rm_subnet + +- name: Verify rm_subnet + assert: + that: + - rm_subnet is changed + - rm_subnet.current == [] + - rm_subnet.previous.0.l3extSubnet.attributes.ip == "192.0.2.0/24" + - rm_subnet.previous.0.l3extSubnet.attributes.name == "ansible_test"
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_logical_interface_vpc_member/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_logical_interface_vpc_member/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_logical_interface_vpc_member/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_logical_interface_vpc_member/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_logical_interface_vpc_member/tasks/main.yml new file mode 100644 index 00000000..db0d7a90 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_logical_interface_vpc_member/tasks/main.yml @@ -0,0 +1,120 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Anvitha Jain (@anvitha-jain) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Add a new tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + +- name: Add a new L3Out + aci_l3out: + <<: *aci_info + tenant: ansible_tenant + name: ansible_l3out + description: L3Out for ansible_tenant tenant + domain: ansible_dom + vrf: ansible_vrf + l3protocol: ospf + route_control: export + state: present + +- name: Add a new logical node + cisco.aci.aci_rest: + <<: *aci_info + path: /api/mo/uni/tn-ansible_tenant/out-ansible_l3out.json + method: post + content: + { + "l3extLNodeP": { + "attributes": { + "dn": "uni/tn-ansible_tenant/out-ansible_l3out/lnodep-ansible_LNode", + "name": "ansible_LNode" + }, + "children": [{ + "l3extLIfP": { + "attributes": { + "name": "ansible_LInterface" + }, + "children": [{ + "l3extRsPathL3OutAtt": { + "attributes": { + "addr": "0.0.0.0", + "encap": "vlan-100", + "ifInstT": "ext-svi", + "tDn": "topology/pod-1/protpaths-101-102/pathep-[policy_group_one]" + } + } + }] + } + }] + } + } + +- name: Add a VPC member + aci_l3out_logical_interface_vpc_member: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + logical_node: ansible_LNode + logical_interface: ansible_LInterface + path_dn: topology/pod-1/protpaths-101-102/pathep-[policy_group_one] + side: A + state: present + +- name: Query a specific VPC member under ansible_l3out + aci_l3out_logical_interface_vpc_member: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + logical_node: ansible_LNode + logical_interface: ansible_LInterface + path_dn: topology/pod-1/protpaths-101-102/pathep-[policy_group_one] + side: A + state: query + register: query_result + +- name: Query all relationships + aci_l3out_logical_interface_vpc_member: + <<: *aci_info + tenant: ansible_tenant + state: query + ignore_errors: yes + register: query_result + +- name: Remove a VPC member + aci_l3out_logical_interface_vpc_member: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + logical_node: ansible_LNode + logical_interface: ansible_LInterface + path_dn: topology/pod-1/protpaths-101-102/pathep-[policy_group_one] + side: A + state: absent diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_static_routes/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_static_routes/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_static_routes/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_static_routes/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_static_routes/tasks/main.yml new file mode 100644 index 00000000..26864925 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_l3out_static_routes/tasks/main.yml @@ -0,0 +1,138 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Anvitha Jain (@anvitha-jain) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Add a new tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + +- name: Add a new L3Out + aci_l3out: + <<: *aci_info + tenant: ansible_tenant + name: ansible_l3out + description: L3Out for ansible_tenant tenant + domain: ansible_dom + vrf: ansible_vrf + l3protocol: ospf + route_control: export + state: present + delegate_to: localhost + +- name: Add a new logical node + cisco.aci.aci_rest: + <<: *aci_info + path: /api/mo/uni/tn-ansible_tenant/out-ansible_l3out.json + method: post + content: + { + "l3extLNodeP": { + "attributes": { + "dn": "uni/tn-ansible_tenant/out-ansible_l3out/lnodep-lNode", + "name": "lNode", + }, + "children": [{ + "l3extRsNodeL3OutAtt": { + "attributes": { + "rtrId": "10.1.0.1", + "rtrIdLoopBack": "yes", + "tDn": "topology/pod-1/node-101", + } + } + }] + } + } + +- name: Add static routes + aci_l3out_static_routes: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + logical_node: lNode + node_id: 101 + pod_id: 1 + prefix: 10.1.0.1/24 + state: present + delegate_to: localhost + +- name: Query system information + aci_system: + <<: *aci_info + id: 1 + state: query + register: version + +- name: Add static routes bfd + aci_l3out_static_routes: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + logical_node: lNode + node_id: 101 + pod_id: 1 + bfd: bfd + track_policy: test + prefix: 10.1.0.1/24 + state: present + delegate_to: localhost + when: version.current.0.topSystem.attributes.version is version('4.2', '>=') + +- name: Query for a specific MO under l3out + aci_l3out_static_routes: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + logical_node: lNode + node_id: 101 + pod_id: 1 + prefix: 10.1.0.1/24 + state: query + delegate_to: localhost + register: query_result + +- name: Query all relationships + aci_l3out_static_routes: + <<: *aci_info + tenant: production + state: query + ignore_errors: yes + register: query_result + +- name: Remove static routes + aci_l3out_static_routes: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + logical_node: lNode + node_id: 101 + pod_id: 1 + prefix: 10.1.0.1/24 + state: absent + delegate_to: localhost diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/error_handling.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/error_handling.yml new file mode 100644 index 00000000..c5c68a39 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/error_handling.yml @@ -0,0 +1,191 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# PROVOKE ERRORS +- name: Error on name resolution + cisco.aci.aci_rest: + host: foo.bar.cisco.com + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + path: /api/mo/uni.json + method: post + content: + fvTenant: + attributes: + name: ansible_test + ignore_errors: yes + register: error_on_name_resolution + +- name: Verify error_on_name_resolution + assert: + that: + - error_on_name_resolution is failed + - error_on_name_resolution.msg.startswith("Connection failed for https://foo.bar.cisco.com/api/aaaLogin.json. Request failed:") + - "'current' not in error_on_name_resolution" + - "'previous' not in error_on_name_resolution" + - "'sent' not in error_on_name_resolution" + - "'proposed' not in error_on_name_resolution" + - "'filter_string' not in error_on_name_resolution" + +- name: Error when required parameter is missing + cisco.aci.aci_rest: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + method: post + content: + fvTenant: + attributes: + name: ansible_test + ignore_errors: yes + register: error_on_missing_required_param + +- name: Verify error_on_missing_required_param + assert: + that: + - error_on_missing_required_param is failed + - 'error_on_missing_required_param.msg == "missing required arguments: path"' + - "'current' not in error_on_missing_required_param" + - "'previous' not in error_on_missing_required_param" + - "'sent' not in error_on_missing_required_param" + - "'proposed' not in error_on_missing_required_param" + - "'filter_string' not in error_on_missing_required_param" + +- name: Error when attributes are missing + cisco.aci.aci_rest: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + path: /api/mo/uni/tn-ansible_test.json + method: post + content: + fvTenant: + children: + ignore_errors: yes + register: error_on_missing_attributes + +- name: Verify error_on_missing_attributes + assert: + that: + - error_on_missing_attributes is failed + - error_on_missing_attributes.method == 'POST' + - "error_on_missing_attributes.msg == 'APIC Error 400: invalid data at line \\'1\\'. Attributes are missing, tag \\'attributes\\' must be specified first, before any other tag'" + - 'error_on_missing_attributes.response == "HTTP Error 400: Bad Request"' + - error_on_missing_attributes.status == 400 + - "'current' not in error_on_missing_attributes" + - "'previous' not in error_on_missing_attributes" + - "'sent' not in error_on_missing_attributes" + - "'proposed' not in error_on_missing_attributes" + - "'filter_string' not in error_on_missing_attributes" + +- name: Error when input does not validate + cisco.aci.aci_rest: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + path: /api/mo/uni.json + method: post + content: + fvTenant: + attributes: + name: ansible_test + descr: This is an [invalid] description + ignore_errors: yes + register: error_on_input_validation + +- name: Verify error_on_input_validation + assert: + that: + - error_on_input_validation is failed + - error_on_input_validation.method == 'POST' + - "error_on_input_validation.msg == 'APIC Error 801: property descr of tn-ansible_test failed validation for value \\'This is an [invalid] description\\''" + - 'error_on_input_validation.response == "HTTP Error 400: Bad Request"' + - error_on_input_validation.status == 400 + - "'current' not in error_on_input_validation" + - "'previous' not in error_on_input_validation" + - "'sent' not in error_on_input_validation" + - "'proposed' not in error_on_input_validation" + - "'filter_string' not in error_on_input_validation" + +- name: Error when invalid attributes are used + cisco.aci.aci_rest: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + path: /api/mo/uni.json + method: post + content: + fvTenant: + attributes: + name: ansible_test + description: This is an "invalid" description + ignore_errors: yes + register: error_on_invalid_attributes + +- name: Verify error_on_invalid_attributes + assert: + that: + - error_on_invalid_attributes is failed + - error_on_invalid_attributes.method == 'POST' + - "error_on_invalid_attributes.msg == 'APIC Error 400: unknown attribute \\'description\\' in element \\'fvTenant\\''" + - 'error_on_invalid_attributes.response == "HTTP Error 400: Bad Request"' + - error_on_invalid_attributes.status == 400 + - "'current' not in error_on_invalid_attributes" + - "'previous' not in error_on_invalid_attributes" + - "'sent' not in error_on_invalid_attributes" + - "'proposed' not in error_on_invalid_attributes" + - "'filter_string' not in error_on_invalid_attributes" + +- name: Error on invalid object + cisco.aci.aci_rest: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + path: /api/mo/uni.json + method: post + content: + fvFoobar: + attributes: + name: ansible_test + ignore_errors: yes + register: error_on_invalid_object + +- name: Verify error_on_invalid_object + assert: + that: + - error_on_invalid_object is failed + - error_on_invalid_object.method == 'POST' + - "error_on_invalid_object.msg == 'APIC Error 122: unknown managed object class fvFoobar'" + - 'error_on_invalid_object.response == "HTTP Error 400: Bad Request"' + - error_on_invalid_object.status == 400 + - "'current' not in error_on_invalid_object" + - "'previous' not in error_on_invalid_object" + - "'sent' not in error_on_invalid_object" + - "'proposed' not in error_on_invalid_object" diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/json_inline.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/json_inline.yml new file mode 100644 index 00000000..dd27525f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/json_inline.yml @@ -0,0 +1,166 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# CLEAN ENVIRONMENT +- name: Remove tenant + cisco.aci.aci_rest: &tenant_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].json + method: delete + +# ADD TENANT +- name: Add tenant (normal mode) + cisco.aci.aci_rest: &tenant_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni.json + method: post + content: + { + "fvTenant": { + "attributes": { + "name": "ansible_test" + } + } + } + delegate_to: localhost + register: nm_add_tenant + +- name: Add tenant again (normal mode) + cisco.aci.aci_rest: *tenant_present + delegate_to: localhost + register: nm_add_tenant_again + +- name: Verify add_tenant + assert: + that: + - nm_add_tenant is changed + - nm_add_tenant_again is not changed + +# CHANGE TENANT +- name: Change description of tenant (normal mode) + cisco.aci.aci_rest: &tenant_changed + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni.json + method: post + content: + { + "fvTenant": { + "attributes": { + "descr": "Ansible test tenant", + "name": "ansible_test" + } + } + } + delegate_to: localhost + register: nm_add_tenant_descr + +- name: Change description of tenant again (normal mode) + cisco.aci.aci_rest: *tenant_changed + delegate_to: localhost + register: nm_add_tenant_descr_again + +- name: Verify add_tenant_descr + assert: + that: + - nm_add_tenant_descr is changed + - nm_add_tenant_descr_again is not changed + +# ADD TENANT AGAIN +- name: Add tenant again with no description (normal mode) + cisco.aci.aci_rest: *tenant_present + delegate_to: localhost + register: nm_add_tenant_again_no_descr + +- name: Verify add_tenant_again_no_descr + assert: + that: + - nm_add_tenant_again_no_descr is not changed + +# QUERY ALL TENANTS +- name: Query all tenants (normal mode) + cisco.aci.aci_rest: &tenant_query_all + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].json + method: get + delegate_to: localhost + register: nm_query_all_tenants + +- name: Verify query_all_tenants + assert: + that: + - nm_query_all_tenants is not changed + +# QUERY A TENANT +- name: Query our tenant + cisco.aci.aci_rest: &tenant_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].json + method: get + delegate_to: localhost + register: nm_query_tenant + +- name: Verify query_tenant + assert: + that: + - nm_query_tenant is not changed + +# REMOVE TENANT +- name: Remove tenant (normal mode) + cisco.aci.aci_rest: *tenant_absent + delegate_to: localhost + register: nm_remove_tenant + +- name: Remove tenant again (normal mode) + cisco.aci.aci_rest: *tenant_absent + delegate_to: localhost + register: nm_remove_tenant_again + +- name: Verify remove_tenant + assert: + that: + - nm_remove_tenant is changed + - nm_remove_tenant_again is not changed + +# QUERY NON-EXISTING TENANT +- name: Query non-existing tenant (normal mode) + cisco.aci.aci_rest: *tenant_query + delegate_to: localhost + register: nm_query_non_tenant + +- name: Verify query_non_tenant + assert: + that: + - nm_query_non_tenant is not changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/json_string.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/json_string.yml new file mode 100644 index 00000000..9d1f6848 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/json_string.yml @@ -0,0 +1,156 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# CLEAN ENVIRONMENT +- name: Remove tenant + cisco.aci.aci_rest: &tenant_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].json + method: delete + +# ADD TENANT +- name: Add tenant (normal mode) + cisco.aci.aci_rest: &tenant_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni.json + method: post + content: | + { + "fvTenant": { + "attributes": { + "name": "ansible_test" + } + } + } + register: nm_add_tenant + +- name: Add tenant again (normal mode) + cisco.aci.aci_rest: *tenant_present + register: nm_add_tenant_again + +- name: Verify add_tenant + assert: + that: + - nm_add_tenant is changed + - nm_add_tenant_again is not changed + +# CHANGE TENANT +- name: Change description of tenant (normal mode) + cisco.aci.aci_rest: &tenant_changed + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni.json + method: post + content: | + { + "fvTenant": { + "attributes": { + "descr": "Ansible test tenant", + "name": "ansible_test" + } + } + } + register: nm_add_tenant_descr + +- name: Change description of tenant again (normal mode) + cisco.aci.aci_rest: *tenant_changed + register: nm_add_tenant_descr_again + +- name: Verify add_tenant_descr + assert: + that: + - nm_add_tenant_descr is changed + - nm_add_tenant_descr_again is not changed + +# ADD TENANT AGAIN +- name: Add tenant again with no description (normal mode) + cisco.aci.aci_rest: *tenant_present + register: nm_add_tenant_again_no_descr + +- name: Verify add_tenant_again_no_descr + assert: + that: + - nm_add_tenant_again_no_descr is not changed + +# QUERY ALL TENANTS +- name: Query all tenants (normal mode) + cisco.aci.aci_rest: &tenant_query_all + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].json + method: get + register: nm_query_all_tenants + +- name: Verify query_all_tenants + assert: + that: + - nm_query_all_tenants is not changed + +# QUERY A TENANT +- name: Query our tenant + cisco.aci.aci_rest: &tenant_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].json + method: get + register: nm_query_tenant + +- name: Verify query_tenant + assert: + that: + - nm_query_tenant is not changed + +# REMOVE TENANT +- name: Remove tenant (normal mode) + cisco.aci.aci_rest: *tenant_absent + register: nm_remove_tenant + +- name: Remove tenant again (normal mode) + cisco.aci.aci_rest: *tenant_absent + register: nm_remove_tenant_again + +- name: Verify remove_tenant + assert: + that: + - nm_remove_tenant is changed + - nm_remove_tenant_again is not changed + +# QUERY NON-EXISTING TENANT +- name: Query non-existing tenant (normal mode) + cisco.aci.aci_rest: *tenant_query + register: nm_query_non_tenant + +- name: Verify query_non_tenant + assert: + that: + - nm_query_non_tenant is not changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/main.yml new file mode 100644 index 00000000..85b50cbf --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/main.yml @@ -0,0 +1,28 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> +# Copyright: (c) 2020, Cindy Zhao (@cizhao) <cizhao@cisco.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- include_tasks: yaml_inline.yml + tags: yaml_inline + +- include_tasks: yaml_string.yml + tags: yaml_string + +- include_tasks: json_inline.yml + tags: json_inline + +- include_tasks: json_string.yml + tags: json_string + +- include_tasks: xml_string.yml + tags: xml_string + +- include_tasks: error_handling.yml + tags: error_handling diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/xml_string.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/xml_string.yml new file mode 100644 index 00000000..c8729177 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/xml_string.yml @@ -0,0 +1,167 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# CLEAN ENVIRONMENT +- name: Remove tenant + cisco.aci.aci_rest: &tenant_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].xml + method: delete + +# ADD TENANT +- name: Add tenant (normal mode) + cisco.aci.aci_rest: &tenant_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni.xml + method: post + content: + { + "fvTenant": { + "attributes": { + "name": "ansible_test" + } + } + } + register: nm_add_tenant + +- name: Add tenant again (normal mode) + cisco.aci.aci_rest: + <<: *tenant_present + path: /api/mo/uni.xml + content: + <fvTenant name="ansible_test"/> + register: nm_add_tenant_again + +- name: Verify add_tenant + assert: + that: + - nm_add_tenant is changed + - nm_add_tenant_again is not changed + +# CHANGE TENANT +- name: Change description of tenant (normal mode) + cisco.aci.aci_rest: &tenant_changed + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni.xml + method: post + content: + fvTenant: + attributes: + name: ansible_test + descr: Ansible test tenant + register: nm_add_tenant_descr + +- name: Change description of tenant again (normal mode) + cisco.aci.aci_rest: + <<: *tenant_changed + path: /api/mo/uni.xml + content: + fvTenant: + attributes: + name: ansible_test + descr: Ansible test tenant + register: nm_add_tenant_descr_again + +- name: Verify add_tenant_descr + assert: + that: + - nm_add_tenant_descr is changed + - nm_add_tenant_descr_again is not changed + +# ADD TENANT AGAIN +- name: Add tenant again with no description (normal mode) + cisco.aci.aci_rest: + <<: *tenant_present + path: /api/mo/uni.xml + content: + <fvTenant name="ansible_test"/> + register: nm_add_tenant_again_no_descr + +- name: Verify add_tenant_again_no_descr + assert: + that: + - nm_add_tenant_again_no_descr is not changed + +# QUERY ALL TENANTS +- name: Query all tenants (normal mode) + cisco.aci.aci_rest: &tenant_query_all + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].xml + method: get + register: nm_query_all_tenants + +- name: Verify query_all_tenants + assert: + that: + - nm_query_all_tenants is not changed + +# QUERY A TENANT +- name: Query our tenant + cisco.aci.aci_rest: &tenant_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].xml + method: get + register: nm_query_tenant + +- name: Verify query_tenant + assert: + that: + - nm_query_tenant is not changed + +# REMOVE TENANT +- name: Remove tenant (normal mode) + cisco.aci.aci_rest: *tenant_absent + register: nm_remove_tenant + +- name: Remove tenant again (normal mode) + cisco.aci.aci_rest: *tenant_absent + register: nm_remove_tenant_again + +- name: Verify remove_tenant + assert: + that: + - nm_remove_tenant is changed + - nm_remove_tenant_again is not changed + +# QUERY NON-EXISTING TENANT +- name: Query non-existing tenant (normal mode) + cisco.aci.aci_rest: *tenant_query + register: nm_query_non_tenant + +- name: Verify query_non_tenant + assert: + that: + - nm_query_non_tenant is not changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/yaml_inline.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/yaml_inline.yml new file mode 100644 index 00000000..d7538f9a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/yaml_inline.yml @@ -0,0 +1,148 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# CLEAN ENVIRONMENT +- name: Remove tenant + cisco.aci.aci_rest: &tenant_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].json + method: delete + +# ADD TENANT +- name: Add tenant (normal mode) + cisco.aci.aci_rest: &tenant_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni.json + method: post + content: + fvTenant: + attributes: + name: ansible_test + register: nm_add_tenant + +- name: Add tenant again (normal mode) + cisco.aci.aci_rest: *tenant_present + register: nm_add_tenant_again + +- name: Verify add_tenant + assert: + that: + - nm_add_tenant is changed + - nm_add_tenant_again is not changed + +# CHANGE TENANT +- name: Change description of tenant (normal mode) + cisco.aci.aci_rest: &tenant_changed + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni.json + method: post + content: + fvTenant: + attributes: + name: ansible_test + descr: Ansible test tenant + register: nm_add_tenant_descr + +- name: Change description of tenant again (normal mode) + cisco.aci.aci_rest: *tenant_changed + register: nm_add_tenant_descr_again + +- name: Verify add_tenant_descr + assert: + that: + - nm_add_tenant_descr is changed + - nm_add_tenant_descr_again is not changed + +# ADD TENANT AGAIN +- name: Add tenant again with no description (normal mode) + cisco.aci.aci_rest: *tenant_present + register: nm_add_tenant_again_no_descr + +- name: Verify add_tenant_again_no_descr + assert: + that: + - nm_add_tenant_again_no_descr is not changed + +# QUERY ALL TENANTS +- name: Query all tenants (normal mode) + cisco.aci.aci_rest: &tenant_query_all + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].json + method: get + register: nm_query_all_tenants + +- name: Verify query_all_tenants + assert: + that: + - nm_query_all_tenants is not changed + +# QUERY A TENANT +- name: Query our tenant + cisco.aci.aci_rest: &tenant_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].json + method: get + register: nm_query_tenant + +- name: Verify query_tenant + assert: + that: + - nm_query_tenant is not changed + +# REMOVE TENANT +- name: Remove tenant (normal mode) + cisco.aci.aci_rest: *tenant_absent + register: nm_remove_tenant + +- name: Remove tenant again (normal mode) + cisco.aci.aci_rest: *tenant_absent + register: nm_remove_tenant_again + +- name: Verify remove_tenant + assert: + that: + - nm_remove_tenant is changed + - nm_remove_tenant_again is not changed + +# QUERY NON-EXISTING TENANT +- name: Query non-existing tenant (normal mode) + cisco.aci.aci_rest: *tenant_query + register: nm_query_non_tenant + +- name: Verify query_non_tenant + assert: + that: + - nm_query_non_tenant is not changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/yaml_string.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/yaml_string.yml new file mode 100644 index 00000000..d7538f9a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_rest/tasks/yaml_string.yml @@ -0,0 +1,148 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# CLEAN ENVIRONMENT +- name: Remove tenant + cisco.aci.aci_rest: &tenant_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].json + method: delete + +# ADD TENANT +- name: Add tenant (normal mode) + cisco.aci.aci_rest: &tenant_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni.json + method: post + content: + fvTenant: + attributes: + name: ansible_test + register: nm_add_tenant + +- name: Add tenant again (normal mode) + cisco.aci.aci_rest: *tenant_present + register: nm_add_tenant_again + +- name: Verify add_tenant + assert: + that: + - nm_add_tenant is changed + - nm_add_tenant_again is not changed + +# CHANGE TENANT +- name: Change description of tenant (normal mode) + cisco.aci.aci_rest: &tenant_changed + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni.json + method: post + content: + fvTenant: + attributes: + name: ansible_test + descr: Ansible test tenant + register: nm_add_tenant_descr + +- name: Change description of tenant again (normal mode) + cisco.aci.aci_rest: *tenant_changed + register: nm_add_tenant_descr_again + +- name: Verify add_tenant_descr + assert: + that: + - nm_add_tenant_descr is changed + - nm_add_tenant_descr_again is not changed + +# ADD TENANT AGAIN +- name: Add tenant again with no description (normal mode) + cisco.aci.aci_rest: *tenant_present + register: nm_add_tenant_again_no_descr + +- name: Verify add_tenant_again_no_descr + assert: + that: + - nm_add_tenant_again_no_descr is not changed + +# QUERY ALL TENANTS +- name: Query all tenants (normal mode) + cisco.aci.aci_rest: &tenant_query_all + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].json + method: get + register: nm_query_all_tenants + +- name: Verify query_all_tenants + assert: + that: + - nm_query_all_tenants is not changed + +# QUERY A TENANT +- name: Query our tenant + cisco.aci.aci_rest: &tenant_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + path: /api/mo/uni/tn-[ansible_test].json + method: get + register: nm_query_tenant + +- name: Verify query_tenant + assert: + that: + - nm_query_tenant is not changed + +# REMOVE TENANT +- name: Remove tenant (normal mode) + cisco.aci.aci_rest: *tenant_absent + register: nm_remove_tenant + +- name: Remove tenant again (normal mode) + cisco.aci.aci_rest: *tenant_absent + register: nm_remove_tenant_again + +- name: Verify remove_tenant + assert: + that: + - nm_remove_tenant is changed + - nm_remove_tenant_again is not changed + +# QUERY NON-EXISTING TENANT +- name: Query non-existing tenant (normal mode) + cisco.aci.aci_rest: *tenant_query + register: nm_query_non_tenant + +- name: Verify query_non_tenant + assert: + that: + - nm_query_non_tenant is not changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_static_binding_to_epg/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_static_binding_to_epg/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_static_binding_to_epg/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_static_binding_to_epg/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_static_binding_to_epg/tasks/main.yml new file mode 100644 index 00000000..aa1e69dc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_static_binding_to_epg/tasks/main.yml @@ -0,0 +1,206 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Bruno Calogero <bcalogero@cisco.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Ensure static path to epg is deleted for test kick off + cisco.aci.aci_static_binding_to_epg: &aci_static_binding_to_epg_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + tenant: anstest + ap: anstest + epg: anstest + interface_type: switch_port + pod: 1 + leafs: 101 + interface: '1/7' + state: absent + +- name: Ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + tenant: anstest + state: present + register: tenant_present + +- name: Ensure ap exists + cisco.aci.aci_ap: &aci_ap_present + <<: *aci_tenant_present + ap: anstest + register: ap_present + +- name: Ensure epg exists + cisco.aci.aci_epg: &aci_epg_present + <<: *aci_ap_present + epg: anstest + register: epg_present + +- name: Bind static-binding to epg - check mode works + cisco.aci.aci_static_binding_to_epg: &aci_static_binding_to_epg_present + <<: *aci_epg_present + encap_id: 222 + deploy_immediacy: lazy + interface_mode: trunk + interface_type: switch_port + pod: 1 + leafs: 101 + interface: '1/7' + check_mode: yes + register: provide_present_check_mode + +- name: Bind static-binding to epg - provide works (creation w/o check-mode) + cisco.aci.aci_static_binding_to_epg: + <<: *aci_static_binding_to_epg_present + ignore_errors: yes + register: provide_present + +- name: Bind static-binding to epg - primary_encap_id works + cisco.aci.aci_static_binding_to_epg: &primary_encap_id_present + <<: *aci_static_binding_to_epg_present + primary_encap_id: 50 + register: primary_ecap_id_present + +- name: Bind contract to epg - idempotency works again + cisco.aci.aci_static_binding_to_epg: + <<: *primary_encap_id_present + register: idempotent_present + +- name: Bind contract to epg - update description (check mode) + cisco.aci.aci_static_binding_to_epg: + <<: *primary_encap_id_present + description: Binding description + check_mode: yes + register: description_cm + +- name: Bind contract to epg - update description (run mode) + cisco.aci.aci_static_binding_to_epg: + <<: *primary_encap_id_present + description: Binding description + register: description + +- name: Bind contract to epg - update description (check mode) + cisco.aci.aci_static_binding_to_epg: + <<: *primary_encap_id_present + description: Binding description + register: idempotent_description_cm + +- name: Bind contract to epg - update description (run mode) + cisco.aci.aci_static_binding_to_epg: + <<: *primary_encap_id_present + description: Binding description + register: idempotent_description + +- name: Missing required param - failure message works + cisco.aci.aci_static_binding_to_epg: + <<: *aci_tenant_present + ignore_errors: yes + register: missing_required_present + +- name: Present assertions + assert: + that: + - provide_present_check_mode is changed + - provide_present_check_mode.sent.fvRsPathAtt.attributes.encap == 'vlan-222' + - provide_present_check_mode.sent.fvRsPathAtt.attributes.instrImedcy == 'lazy' + - provide_present_check_mode.sent.fvRsPathAtt.attributes.mode == 'regular' + - provide_present_check_mode.sent.fvRsPathAtt.attributes.tDn == 'topology/pod-1/paths-101/pathep-[eth1/7]' + - provide_present is changed + - provide_present.sent == provide_present_check_mode.sent + - provide_present.previous == [] + - primary_ecap_id_present is changed + - primary_ecap_id_present.sent.fvRsPathAtt.attributes.primaryEncap == 'vlan-50' + - description_cm is changed + - description is changed + - idempotent_description_cm is not changed + - idempotent_description is not changed + - missing_required_present is failed + - 'missing_required_present.msg == "state is present but all of the following are missing: ap, encap_id, epg, interface, leafs, pod_id"' + - missing_required_present is failed + + +- name: Query specific binding + cisco.aci.aci_static_binding_to_epg: + <<: *primary_encap_id_present + state: query + register: query_static_binding + +- name: Query all bindings + cisco.aci.aci_static_binding_to_epg: + <<: *aci_tenant_present + state: query + register: query_all + +- name: Query assertions + assert: + that: + - query_static_binding is not changed + - query_static_binding.current != [] + - '"uni/tn-anstest/ap-anstest/epg-anstest/rspathAtt-[topology/pod-1/paths-101/pathep-[eth1/7]]" in query_static_binding.url' + - query_all is not changed + - '"uni/tn-anstest.json" in query_all.url' + + +- name: Delete provide binding - deletion works + cisco.aci.aci_static_binding_to_epg: + <<: *primary_encap_id_present + state: absent + register: provide_absent + +- name: Delete provide binding - idempotency works + cisco.aci.aci_static_binding_to_epg: + <<: *primary_encap_id_present + state: absent + register: provide_absent_idempotent + +- name: Missing param - failure message works + cisco.aci.aci_static_binding_to_epg: + <<: *aci_tenant_present + state: absent + ignore_errors: yes + register: missing_param_absent + +- name: Absent assertions + assert: + that: + - provide_absent is changed + - provide_absent.previous.0.fvRsPathAtt is defined + - provide_absent_idempotent is not changed + - provide_absent_idempotent.previous == [] + - missing_param_absent is failed + - missing_param_absent is failed + - 'missing_param_absent.msg == "state is absent but all of the following are missing: ap, epg, interface, leafs, pod_id"' + +- name: Cleanup binding + cisco.aci.aci_static_binding_to_epg: + <<: *aci_static_binding_to_epg_absent + +- name: Cleanup epg + cisco.aci.aci_epg: + <<: *aci_epg_present + state: absent + +- name: Cleanup ap + cisco.aci.aci_ap: + <<: *aci_ap_present + state: absent + +- name: Cleanup tenant + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_leaf_selector/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_leaf_selector/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_leaf_selector/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_leaf_selector/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_leaf_selector/tasks/main.yml new file mode 100644 index 00000000..8b82411f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_leaf_selector/tasks/main.yml @@ -0,0 +1,142 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Deleting Switch Policy Leaf profile exists for kick off + cisco.aci.aci_switch_policy_leaf_profile: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + leaf_profile: sw_name_test + state: absent + +- name: Ensuring Switch Policy Leaf profile exists for kick off + cisco.aci.aci_switch_policy_leaf_profile: &aci_switch_policy_leaf_profile_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + leaf_profile: sw_name_test + state: present + register: leaf_profile_present + +# TODO: Ensure that leaf Policy Group Exists (module missing) (infra:AccPortGrp) + +- name: Adding a switch policy leaf profile selector associated Node Block range (w/o policy group) - check mode works + cisco.aci.aci_switch_leaf_selector: &aci_switch_leaf_selector_present + <<: *aci_switch_policy_leaf_profile_present + leaf: leaf_selector_name + leaf_node_blk: node_blk_name + from: 1011 + to: 1011 + check_mode: yes + register: sw_leaf_selec_check_mode_present + +- name: Adding a switch policy leaf profile selector associated Node Block range (w/o policy group) - creation works + cisco.aci.aci_switch_leaf_selector: + <<: *aci_switch_leaf_selector_present + register: sw_leaf_selec_present + +- name: Adding a switch policy leaf profile selector associated Node Block range (w/o policy group) - idempotency works + cisco.aci.aci_switch_leaf_selector: + <<: *aci_switch_leaf_selector_present + register: sw_leaf_selec_idempotent + +- name: Adding a switch policy leaf profile selector associated Node Block range (w/ policy group) - update works + cisco.aci.aci_switch_leaf_selector: + <<: *aci_switch_leaf_selector_present + policy_group: anstest_policygroupname + register: sw_leaf_selec_update + +# TODO: also test for errors +- name: present assertions + assert: + that: + - sw_leaf_selec_check_mode_present is changed + - sw_leaf_selec_present is changed + - sw_leaf_selec_present.previous == [] + - sw_leaf_selec_present.sent.infraLeafS.attributes.name == 'leaf_selector_name' + - sw_leaf_selec_present.sent.infraLeafS.children.0.infraNodeBlk.attributes.from_ == '1011' + - sw_leaf_selec_present.sent.infraLeafS.children.0.infraNodeBlk.attributes.to_ == '1011' + - sw_leaf_selec_present.sent.infraLeafS.children.0.infraNodeBlk.attributes.name == 'node_blk_name' + - sw_leaf_selec_idempotent is not changed + - sw_leaf_selec_idempotent.sent == {} + - sw_leaf_selec_update is changed + - sw_leaf_selec_update.sent.infraLeafS.attributes == {} + - sw_leaf_selec_update.sent.infraLeafS.children.0.infraRsAccNodePGrp.attributes.tDn == 'uni/infra/funcprof/accnodepgrp-anstest_policygroupname' + +- name: Query Specific switch policy leaf profile selector + cisco.aci.aci_switch_leaf_selector: + <<: *aci_switch_policy_leaf_profile_present + leaf: leaf_selector_name # "{{ fake_var | default(omit) }}" ? + state: query + register: binding_query + +- name: present assertions + assert: + that: + - binding_query is not changed + - binding_query.current | length >= 1 + - '"api/mo/uni/infra/nprof-sw_name_test/leaves-leaf_selector_name-typ-range.json" in binding_query.url' + +- name: Remove binding of interface access port selector and Interface Policy Leaf Profile - check mode + cisco.aci.aci_switch_leaf_selector: &aci_switch_leaf_selector_absent + <<: *aci_switch_policy_leaf_profile_present + leaf: leaf_selector_name + state: absent + check_mode: yes + register: sw_leaf_selec_check_mode_absent + +- name: Remove switch policy leaf profile selector - delete works + cisco.aci.aci_switch_leaf_selector: + <<: *aci_switch_leaf_selector_absent + register: sw_leaf_selec_absent + +- name: Remove switch policy leaf profile selector - idempotency works + cisco.aci.aci_switch_leaf_selector: + <<: *aci_switch_leaf_selector_absent + register: sw_leaf_selec_absent_idempotent + +- name: Remove switch policy leaf profile selector - check mode + cisco.aci.aci_switch_leaf_selector: + <<: *aci_switch_policy_leaf_profile_present + #access_port_selector: anstest_accessportselector + state: absent + ignore_errors: yes + register: sw_leaf_selec_absent_missing_param + +- name: absent assertions + assert: + that: + - sw_leaf_selec_check_mode_absent is changed + - sw_leaf_selec_check_mode_absent.previous != [] + - sw_leaf_selec_absent is changed + - sw_leaf_selec_absent.previous == sw_leaf_selec_check_mode_absent.previous + - sw_leaf_selec_absent_idempotent is not changed + - sw_leaf_selec_absent_idempotent.previous == [] + - sw_leaf_selec_absent_missing_param is failed + - 'sw_leaf_selec_absent_missing_param.msg == "state is absent but all of the following are missing: leaf"' + + +- name: Remove switch policy leaf profile selector - Clean up + cisco.aci.aci_switch_leaf_selector: + <<: *aci_switch_leaf_selector_absent + state: absent + +- name: Deleting Switch Policy Leaf profile exists for kick off + cisco.aci.aci_switch_policy_leaf_profile: + <<: *aci_switch_policy_leaf_profile_present + state: absent diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_policy_leaf_profile/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_policy_leaf_profile/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_policy_leaf_profile/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_policy_leaf_profile/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_policy_leaf_profile/tasks/main.yml new file mode 100644 index 00000000..80344aeb --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_policy_leaf_profile/tasks/main.yml @@ -0,0 +1,213 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + + +# CLEAN ENVIRONMENT +- name: Remove leaf profile + cisco.aci.aci_switch_policy_leaf_profile: &aci_switch_policy_leaf_profile_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + leaf_profile: ansible_test + state: absent + + +# ADD LEAF PROFILE +- name: Add switch policy leaf profile (check_mode) + cisco.aci.aci_switch_policy_leaf_profile: &aci_switch_policy_leaf_profile_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + leaf_profile: ansible_test + state: present + check_mode: yes + register: cm_add_switch_leaf_profile + +- name: Add leaf profile (normal mode) + cisco.aci.aci_switch_policy_leaf_profile: *aci_switch_policy_leaf_profile_present + register: nm_add_switch_leaf_profile + +- name: Add leaf profile again (check_mode) + cisco.aci.aci_switch_policy_leaf_profile: *aci_switch_policy_leaf_profile_present + check_mode: yes + register: cm_add_switch_leaf_profile_again + +- name: Add leaf profile again (normal mode) + cisco.aci.aci_switch_policy_leaf_profile: *aci_switch_policy_leaf_profile_present + register: nm_add_switch_leaf_profile_again + +- name: Verify add_switch_leaf_profile + assert: + that: + - cm_add_switch_leaf_profile is changed + - nm_add_switch_leaf_profile is changed + - cm_add_switch_leaf_profile_again is not changed + - nm_add_switch_leaf_profile_again is not changed + + +# CHANGE SWITCH LEAF PROFILE +- name: Change description of leaf profile (check_mode) + cisco.aci.aci_switch_policy_leaf_profile: + <<: *aci_switch_policy_leaf_profile_present + description: Ansible test leaf profile + check_mode: yes + register: cm_add_switch_leaf_profile_descr + +- name: Change description of leaf profile (normal mode) + cisco.aci.aci_switch_policy_leaf_profile: + <<: *aci_switch_policy_leaf_profile_present + description: Ansible test leaf profile + register: nm_add_switch_leaf_profile_descr + +- name: Change description of leaf profile again (check_mode) + cisco.aci.aci_switch_policy_leaf_profile: + <<: *aci_switch_policy_leaf_profile_present + description: Ansible test leaf profile + check_mode: yes + register: cm_add_switch_leaf_profile_descr_again + +- name: Change description of leaf profile again (normal mode) + cisco.aci.aci_switch_policy_leaf_profile: + <<: *aci_switch_policy_leaf_profile_present + description: Ansible test leaf profile + register: nm_add_switch_leaf_profile_descr_again + +- name: Verify add_switch_leaf_profile_descr + assert: + that: + - cm_add_switch_leaf_profile_descr is changed + - nm_add_switch_leaf_profile_descr is changed + - cm_add_switch_leaf_profile_descr_again is not changed + - nm_add_switch_leaf_profile_descr_again is not changed + + +# ADD LEAF PROFILE AGAIN +- name: Add leaf profile again with no description (check_mode) + cisco.aci.aci_switch_policy_leaf_profile: *aci_switch_policy_leaf_profile_present + check_mode: yes + register: cm_add_switch_leaf_profile_again_no_descr + +- name: Add leaf profile again with no description (normal mode) + cisco.aci.aci_switch_policy_leaf_profile: *aci_switch_policy_leaf_profile_present + register: nm_add_switch_leaf_profile_again_no_descr + +- name: Verify add_switch_leaf_profile_again_no_descr + assert: + that: + - cm_add_switch_leaf_profile_again_no_descr is not changed + - nm_add_switch_leaf_profile_again_no_descr is not changed + + +# QUERY ALL LEAF PROFILES +- name: Query all profiles (check_mode) + cisco.aci.aci_switch_policy_leaf_profile: &aci_switch_policy_leaf_profile_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_switch_leaf_profiles + +- name: Query all switch_leaf_profiles (normal mode) + cisco.aci.aci_switch_policy_leaf_profile: *aci_switch_policy_leaf_profile_query + register: nm_query_all_switch_leaf_profiles + +- name: Verify query_all_switch_leaf_profiles + assert: + that: + - cm_query_all_switch_leaf_profiles is not changed + - nm_query_all_switch_leaf_profiles is not changed + # NOTE: Order of switch_leaf_profiles is not stable between calls + #- cm_query_all_switch_leaf_profiles == nm_query_all_switch_leaf_profiles + + +# QUERY A LEAF PROFILE +- name: Query our switch_leaf_profile + cisco.aci.aci_switch_policy_leaf_profile: + <<: *aci_switch_policy_leaf_profile_query + leaf_profile: ansible_test + check_mode: yes + register: cm_query_switch_leaf_profile + +- name: Query our switch_leaf_profile + cisco.aci.aci_switch_policy_leaf_profile: + <<: *aci_switch_policy_leaf_profile_query + leaf_profile: ansible_test + register: nm_query_switch_leaf_profile + +- name: Verify query_switch_leaf_profile + assert: + that: + - cm_query_switch_leaf_profile is not changed + - nm_query_switch_leaf_profile is not changed + - cm_query_switch_leaf_profile == nm_query_switch_leaf_profile + + +# REMOVE LEAF PROFILE +- name: Remove switch_leaf_profile (check_mode) + cisco.aci.aci_switch_policy_leaf_profile: *aci_switch_policy_leaf_profile_absent + check_mode: yes + register: cm_remove_switch_leaf_profile + +- name: Remove switch_leaf_profile (normal mode) + cisco.aci.aci_switch_policy_leaf_profile: *aci_switch_policy_leaf_profile_absent + register: nm_remove_switch_leaf_profile + +- name: Remove switch_leaf_profile again (check_mode) + cisco.aci.aci_switch_policy_leaf_profile: *aci_switch_policy_leaf_profile_absent + check_mode: yes + register: cm_remove_switch_leaf_profile_again + +- name: Remove switch_leaf_profile again (normal mode) + cisco.aci.aci_switch_policy_leaf_profile: *aci_switch_policy_leaf_profile_absent + register: nm_remove_switch_leaf_profile_again + +- name: Verify remove_switch_leaf_profile + assert: + that: + - cm_remove_switch_leaf_profile is changed + - nm_remove_switch_leaf_profile is changed + - cm_remove_switch_leaf_profile_again is not changed + - nm_remove_switch_leaf_profile_again is not changed + + +# QUERY NON-EXISTING LEAF PROFILE +- name: Query non-existing switch_leaf_profile (check_mode) + cisco.aci.aci_switch_policy_leaf_profile: + <<: *aci_switch_policy_leaf_profile_query + leaf_profile: ansible_test + check_mode: yes + register: cm_query_non_switch_leaf_profile + +- name: Query non-existing switch_leaf_profile (normal mode) + cisco.aci.aci_switch_policy_leaf_profile: + <<: *aci_switch_policy_leaf_profile_query + leaf_profile: ansible_test + register: nm_query_non_switch_leaf_profile + +# TODO: Implement more tests +- name: Verify query_non_switch_leaf_profile + assert: + that: + - cm_query_non_switch_leaf_profile is not changed + - nm_query_non_switch_leaf_profile is not changed + - cm_query_non_switch_leaf_profile == nm_query_non_switch_leaf_profile diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_policy_vpc_protection_group/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_policy_vpc_protection_group/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_policy_vpc_protection_group/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_policy_vpc_protection_group/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_policy_vpc_protection_group/tasks/main.yml new file mode 100644 index 00000000..79785eae --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_switch_policy_vpc_protection_group/tasks/main.yml @@ -0,0 +1,213 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +# CLEAN ENVIRONMENT +- name: Remove vpc protection group + cisco.aci.aci_switch_policy_vpc_protection_group: &aci_switch_policy_vpc_protection_group_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + protection_group: ansible_test + state: absent + + +# ADD VPC PROTECTION GROUP +- name: Add vpc protection group (check_mode) + cisco.aci.aci_switch_policy_vpc_protection_group: &aci_switch_policy_vpc_protection_group_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + protection_group: ansible_test + protection_group_id: 6 + switch_1_id: 3811 + switch_2_id: 3812 + state: present + check_mode: yes + register: cm_add_vpc_prot_grp + +- name: Add vpc protection group (normal mode) + cisco.aci.aci_switch_policy_vpc_protection_group: *aci_switch_policy_vpc_protection_group_present + register: nm_add_vpc_prot_grp + +- name: Add vpc protection group again (check_mode) + cisco.aci.aci_switch_policy_vpc_protection_group: *aci_switch_policy_vpc_protection_group_present + check_mode: yes + register: cm_add_vpc_prot_grp_again + +- name: Add vpc protection group again (normal mode) + cisco.aci.aci_switch_policy_vpc_protection_group: *aci_switch_policy_vpc_protection_group_present + register: nm_add_vpc_prot_grp_again + +- name: Verify add_vpc_prot_grp_again + assert: + that: + - cm_add_vpc_prot_grp is changed + - nm_add_vpc_prot_grp is changed + - cm_add_vpc_prot_grp_again is not changed + - nm_add_vpc_prot_grp_again is not changed + + +# CHANGE VPC PROTECTION GROUP +- name: Change vpc domain policy of vpc protection group (check_mode) + cisco.aci.aci_switch_policy_vpc_protection_group: + <<: *aci_switch_policy_vpc_protection_group_present + vpc_domain_policy: ansible_test_pol + check_mode: yes + register: cm_add_vpc_prot_grp_pol + +- name: Change vpc domain policy of vpc protection group (normal mode) + cisco.aci.aci_switch_policy_vpc_protection_group: + <<: *aci_switch_policy_vpc_protection_group_present + vpc_domain_policy: ansible_test_pol + register: nm_add_vpc_prot_grp_pol + +- name: Change vpc domain policy of vpc protection group again (check_mode) + cisco.aci.aci_switch_policy_vpc_protection_group: + <<: *aci_switch_policy_vpc_protection_group_present + vpc_domain_policy: ansible_test_pol + check_mode: yes + register: cm_add_vpc_prot_grp_pol_again + +- name: Change vpc domain policy of vpc protection group again (normal mode) + cisco.aci.aci_switch_policy_vpc_protection_group: + <<: *aci_switch_policy_vpc_protection_group_present + vpc_domain_policy: ansible_test_pol + register: nm_add_vpc_prot_grp_pol_again + +- name: Verify add_vpc_prot_grp_pol + assert: + that: + - cm_add_vpc_prot_grp_pol is changed + - nm_add_vpc_prot_grp_pol is changed + - cm_add_vpc_prot_grp_pol_again is not changed + - nm_add_vpc_prot_grp_pol_again is not changed + + +# ADD FABRIC NODE AGAIN +- name: Add vpc protection group again with no domain policy (check_mode) + cisco.aci.aci_switch_policy_vpc_protection_group: *aci_switch_policy_vpc_protection_group_present + check_mode: yes + register: cm_add_vpc_prot_grp_again_no_pol + +- name: Add vpc protection group again with no domain policy (normal mode) + cisco.aci.aci_switch_policy_vpc_protection_group: *aci_switch_policy_vpc_protection_group_present + register: nm_add_vpc_prot_grp_again_no_pol + +- name: Verify add_vpc_prot_grp_again_no_pol + assert: + that: + - cm_add_vpc_prot_grp_again_no_pol is not changed + - nm_add_vpc_prot_grp_again_no_pol is not changed + + +# QUERY ALL VPC PROTECTION GROUPS +- name: Query vpc protection groups (check_mode) + cisco.aci.aci_switch_policy_vpc_protection_group: &aci_switch_policy_vpc_protection_group_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_vpc_prot_grps + +- name: Query all vpc protection groups (normal mode) + cisco.aci.aci_switch_policy_vpc_protection_group: *aci_switch_policy_vpc_protection_group_query + register: nm_query_all_vpc_prot_grps + +- name: Verify query_all_vpc_prot_grps + assert: + that: + - cm_query_all_vpc_prot_grps is not changed + - nm_query_all_vpc_prot_grps is not changed + - cm_query_all_vpc_prot_grps == nm_query_all_vpc_prot_grps + + +# QUERY A VPC PROTECTION GROUP +- name: Query our vpc protection group + cisco.aci.aci_switch_policy_vpc_protection_group: + <<: *aci_switch_policy_vpc_protection_group_query + protection_group: ansible_test # might need node_id too + check_mode: yes + register: cm_query_vpc_prot_grp + +- name: Query our vpc protection group + cisco.aci.aci_switch_policy_vpc_protection_group: + <<: *aci_switch_policy_vpc_protection_group_query + protection_group: ansible_test + register: nm_query_vpc_prot_grp + +- name: Verify query_vpc_prot_grp + assert: + that: + - cm_query_vpc_prot_grp is not changed + - nm_query_vpc_prot_grp is not changed + - cm_query_vpc_prot_grp == nm_query_vpc_prot_grp + + +# REMOVE FABRIC NODE +- name: Remove vpc protection group (check_mode) + cisco.aci.aci_switch_policy_vpc_protection_group: *aci_switch_policy_vpc_protection_group_absent + check_mode: yes + register: cm_remove_vpc_prot_grp + +- name: Remove vpc protection group (normal mode) + cisco.aci.aci_switch_policy_vpc_protection_group: *aci_switch_policy_vpc_protection_group_absent + register: nm_remove_vpc_prot_grp + +- name: Remove vpc protection group again (check_mode) + cisco.aci.aci_switch_policy_vpc_protection_group: *aci_switch_policy_vpc_protection_group_absent + check_mode: yes + register: cm_remove_vpc_prot_grp_again + +- name: Remove vpc protection group again (normal mode) + cisco.aci.aci_switch_policy_vpc_protection_group: *aci_switch_policy_vpc_protection_group_absent + register: nm_remove_vpc_prot_grp_again + +- name: Verify remove_vpc_prot_grp + assert: + that: + - cm_remove_vpc_prot_grp is changed + - nm_remove_vpc_prot_grp is changed + - cm_remove_vpc_prot_grp_again is not changed + - nm_remove_vpc_prot_grp_again is not changed + + +# QUERY NON-EXISTING LEAF PROFILE +- name: Query non-existing vpc protection group (check_mode) + cisco.aci.aci_switch_policy_vpc_protection_group: + <<: *aci_switch_policy_vpc_protection_group_query + protection_group: ansible_test + check_mode: yes + register: cm_query_non_vpc_prot_grp + +- name: Query non-existing vpc protection group (normal mode) + cisco.aci.aci_switch_policy_vpc_protection_group: + <<: *aci_switch_policy_vpc_protection_group_query + protection_group: ansible_test + register: nm_query_non_vpc_prot_grp + +- name: Verify query_non_vpc_prot_grp + assert: + that: + - cm_query_non_vpc_prot_grp is not changed + - nm_query_non_vpc_prot_grp is not changed + - cm_query_non_vpc_prot_grp == nm_query_non_vpc_prot_grp diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_system/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_system/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_system/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_system/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_system/tasks/main.yml new file mode 100644 index 00000000..483bd540 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_system/tasks/main.yml @@ -0,0 +1,63 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Lionel Hercot (@lhercot) <lhercot@cisco.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +# CLEAN ENVIRONMENT +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +# QUERY OBJECTS +- name: Query all controllers system information + aci_system: + <<: *aci_info + state: query + register: query_all + +- name: Verify query_all + assert: + that: + - query_all is not changed + - query_all.current.0.topSystem.attributes.id == "1" + - '"version" in query_all.current.0.topSystem.attributes' + + +- name: Query a specific controller system information + aci_system: + <<: *aci_info + id: 1 + state: query + register: query_controller + +- name: Verify query_controller + assert: + that: + - query_controller is not changed + - query_controller.current.0.topSystem.attributes.id == "1" + - '"version" in query_controller.current.0.topSystem.attributes' + +- name: Query non_existing controller + aci_system: + <<: *aci_info + id: 99 + state: query + register: query_non_existing + +- name: Verify query_non_existing + assert: + that: + - query_non_existing is not changed + - query_non_existing.current == []
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_taboo_contract/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_taboo_contract/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_taboo_contract/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_taboo_contract/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_taboo_contract/tasks/main.yml new file mode 100644 index 00000000..8bcdd7d7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_taboo_contract/tasks/main.yml @@ -0,0 +1,289 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + + +# CLEAN ENVIRONMENT +- name: Remove taboo contract + cisco.aci.aci_taboo_contract: &taboo_contract_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: ansible_test + taboo_contract: taboo_contract_test + state: absent + +- name: Add tenant + cisco.aci.aci_tenant: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: ansible_test + state: present + + +# ADD TABOO CONTRACT +- name: Add taboo contract (check_mode) + cisco.aci.aci_taboo_contract: &taboo_contract_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: ansible_test + taboo_contract: taboo_contract_test + state: present + check_mode: yes + register: cm_add_taboo_contract + +- name: Add taboo contract (normal mode) + cisco.aci.aci_taboo_contract: *taboo_contract_present + register: nm_add_taboo_contract + +- name: Verify add_taboo_contract + assert: + that: + - cm_add_taboo_contract is changed + - nm_add_taboo_contract is changed + - cm_add_taboo_contract.sent.vzTaboo.attributes.name == nm_add_taboo_contract.sent.vzTaboo.attributes.name == 'taboo_contract_test' + - cm_add_taboo_contract.proposed.vzTaboo.attributes.name == nm_add_taboo_contract.proposed.vzTaboo.attributes.name == 'taboo_contract_test' + - cm_add_taboo_contract.previous == nm_add_taboo_contract.previous == [] + # NOTE: We cannot fix this easily + - cm_add_taboo_contract.current == [] + - nm_add_taboo_contract.current.0.vzTaboo.attributes.descr == '' + - nm_add_taboo_contract.current.0.vzTaboo.attributes.dn == 'uni/tn-ansible_test/taboo-taboo_contract_test' + - nm_add_taboo_contract.current.0.vzTaboo.attributes.name == 'taboo_contract_test' + +- name: Add taboo_contract again (check_mode) + cisco.aci.aci_taboo_contract: *taboo_contract_present + check_mode: yes + register: cm_add_taboo_contract_again + +- name: Add taboo contract again (normal mode) + cisco.aci.aci_taboo_contract: *taboo_contract_present + register: nm_add_taboo_contract_again + +- name: Verify add_taboo_contract_again + assert: + that: + - cm_add_taboo_contract_again is not changed + - nm_add_taboo_contract_again is not changed + - cm_add_taboo_contract_again.current == nm_add_taboo_contract_again.current == nm_add_taboo_contract.current + + +# CHANGE TABOO CONTRACT +- name: Change description of taboo contract (check_mode) + cisco.aci.aci_taboo_contract: + <<: *taboo_contract_present + description: Ansible test taboo contract + check_mode: yes + register: cm_add_taboo_contract_descr + +- name: Change description of taboo contract (normal mode) + cisco.aci.aci_taboo_contract: + <<: *taboo_contract_present + description: Ansible test taboo contract + register: nm_add_taboo_contract_descr + +- name: Verify add_taboo_contract_descr + assert: + that: + - cm_add_taboo_contract_descr is changed + - nm_add_taboo_contract_descr is changed + - cm_add_taboo_contract_descr.sent.vzTaboo.attributes.descr == nm_add_taboo_contract_descr.sent.vzTaboo.attributes.descr == 'Ansible test taboo contract' + - cm_add_taboo_contract_descr.proposed.vzTaboo.attributes.descr == nm_add_taboo_contract_descr.proposed.vzTaboo.attributes.descr == 'Ansible test taboo contract' + - cm_add_taboo_contract_descr.proposed.vzTaboo.attributes.name == nm_add_taboo_contract_descr.proposed.vzTaboo.attributes.name == 'taboo_contract_test' + - cm_add_taboo_contract_descr.previous == nm_add_taboo_contract_descr.previous == cm_add_taboo_contract_descr.current == nm_add_taboo_contract.current + - nm_add_taboo_contract_descr.current.0.vzTaboo.attributes.descr == 'Ansible test taboo contract' + - nm_add_taboo_contract_descr.current.0.vzTaboo.attributes.dn == 'uni/tn-ansible_test/taboo-taboo_contract_test' + - nm_add_taboo_contract_descr.current.0.vzTaboo.attributes.name == 'taboo_contract_test' + +- name: Change description of taboo contract again (check_mode) + cisco.aci.aci_taboo_contract: + <<: *taboo_contract_present + description: Ansible test taboo contract + check_mode: yes + register: cm_add_taboo_contract_descr_again + +- name: Change description of taboo contract again (normal mode) + cisco.aci.aci_taboo_contract: + <<: *taboo_contract_present + description: Ansible test taboo contract + register: nm_add_taboo_contract_descr_again + +- name: Verify add_taboo_contract_descr_again + assert: + that: + - cm_add_taboo_contract_descr_again is not changed + - nm_add_taboo_contract_descr_again is not changed + - cm_add_taboo_contract_descr_again.current == nm_add_taboo_contract_descr_again.current == nm_add_taboo_contract_descr.current + + +# ADD TABOO CONTRACT AGAIN +- name: Add taboo contract again with no description (check_mode) + cisco.aci.aci_taboo_contract: *taboo_contract_present + check_mode: yes + register: cm_add_taboo_contract_again_no_descr + +- name: Add taboo contract again with no description (normal mode) + cisco.aci.aci_taboo_contract: *taboo_contract_present + register: nm_add_taboo_contract_again_no_descr + +- name: Verify add_taboo_contract_again_no_descr + assert: + that: + - cm_add_taboo_contract_again_no_descr is not changed + - nm_add_taboo_contract_again_no_descr is not changed + - cm_add_taboo_contract_again_no_descr.current == nm_add_taboo_contract_again_no_descr.current == nm_add_taboo_contract_descr.current + + +# QUERY ALL TABOO CONTRACTS +- name: Query all taboo contracts (check_mode) + cisco.aci.aci_taboo_contract: &taboo_contract_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_taboo_contracts + +- name: Query all taboo contracts (normal mode) + cisco.aci.aci_taboo_contract: *taboo_contract_query + register: nm_query_all_taboo_contracts + +- name: Verify query_all_taboo_contracts + assert: + that: + - cm_query_all_taboo_contracts is not changed + - nm_query_all_taboo_contracts is not changed + - cm_query_all_taboo_contracts == nm_query_all_taboo_contracts + - cm_query_all_taboo_contracts.current|length >= 1 + + +# QUERY A TABOO CONTRACT +- name: Query our taboo contract + cisco.aci.aci_taboo_contract: + <<: *taboo_contract_query + tenant: ansible_test + taboo_contract: taboo_contract_test + check_mode: yes + register: cm_query_taboo_contract + +- name: Query our taboo contract + cisco.aci.aci_taboo_contract: + <<: *taboo_contract_query + tenant: ansible_test + taboo_contract: taboo_contract_test + register: nm_query_taboo_contract + +- name: Verify query_taboo_contract + assert: + that: + - cm_query_taboo_contract is not changed + - nm_query_taboo_contract is not changed + - cm_query_taboo_contract == nm_query_taboo_contract + - nm_query_taboo_contract.current.0.vzTaboo.attributes.descr == 'Ansible test taboo contract' + - nm_query_taboo_contract.current.0.vzTaboo.attributes.dn == 'uni/tn-ansible_test/taboo-taboo_contract_test' + - nm_query_taboo_contract.current.0.vzTaboo.attributes.name == 'taboo_contract_test' + + +# REMOVE TABOO CONTRACT +- name: Remove taboo contract (check_mode) + cisco.aci.aci_taboo_contract: *taboo_contract_absent + check_mode: yes + register: cm_remove_taboo_contract + +- name: Remove taboo contract (normal mode) + cisco.aci.aci_taboo_contract: *taboo_contract_absent + register: nm_remove_taboo_contract + +- name: Verify remove_taboo_contract + assert: + that: + - cm_remove_taboo_contract is changed + - nm_remove_taboo_contract is changed + - cm_remove_taboo_contract.current.0.vzTaboo.attributes.descr == cm_remove_taboo_contract.previous.0.vzTaboo.attributes.descr == nm_remove_taboo_contract.previous.0.vzTaboo.attributes.descr == 'Ansible test taboo contract' + - cm_remove_taboo_contract.current.0.vzTaboo.attributes.name == cm_remove_taboo_contract.previous.0.vzTaboo.attributes.name == nm_remove_taboo_contract.previous.0.vzTaboo.attributes.name == 'taboo_contract_test' + - cm_remove_taboo_contract.current.0.vzTaboo.attributes.dn == cm_remove_taboo_contract.previous.0.vzTaboo.attributes.dn == nm_remove_taboo_contract.previous.0.vzTaboo.attributes.dn == 'uni/tn-ansible_test/taboo-taboo_contract_test' + - nm_remove_taboo_contract.current == [] + +- name: Remove taboo contract again (check_mode) + cisco.aci.aci_taboo_contract: *taboo_contract_absent + check_mode: yes + register: cm_remove_taboo_contract_again + +- name: Remove taboo contract again (normal mode) + cisco.aci.aci_taboo_contract: *taboo_contract_absent + register: nm_remove_taboo_contract_again + +- name: Verify remove_taboo_contract_again + assert: + that: + - cm_remove_taboo_contract_again is not changed + - nm_remove_taboo_contract_again is not changed + - cm_remove_taboo_contract_again.proposed == nm_remove_taboo_contract_again.proposed == {} + - cm_remove_taboo_contract_again.sent == nm_remove_taboo_contract_again.sent == {} + - cm_remove_taboo_contract_again.previous == nm_remove_taboo_contract_again.previous == [] + - cm_remove_taboo_contract_again.current == nm_remove_taboo_contract_again.current == [] + + +# QUERY NON-EXISTING TABOO CONTRACT +- name: Query non-existing taboo contract (check_mode) + cisco.aci.aci_taboo_contract: + <<: *taboo_contract_query + tenant: ansible_test + taboo_contract: taboo_contract_test + check_mode: yes + register: cm_query_non_taboo_contract + +- name: Query non-existing taboo contract (normal mode) + cisco.aci.aci_taboo_contract: + <<: *taboo_contract_query + tenant: ansible_test + taboo_contract: taboo_contract_test + register: nm_query_non_taboo_contract + +# TODO: Implement more tests +- name: Verify query_non_taboo_contract + assert: + that: + - cm_query_non_taboo_contract is not changed + - nm_query_non_taboo_contract is not changed + - cm_remove_taboo_contract_again.previous == nm_remove_taboo_contract_again.previous == [] + - cm_remove_taboo_contract_again.current == nm_remove_taboo_contract_again.current == [] + + +# PROVOKE ERRORS +- name: Error when required parameter is missing + cisco.aci.aci_taboo_contract: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: present + ignore_errors: yes + register: error_on_missing_required_param + +- name: Verify error_on_missing_required_param + assert: + that: + - error_on_missing_required_param is failed + - 'error_on_missing_required_param.msg == "state is present but all of the following are missing: tenant, taboo_contract"' diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_tenant/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_tenant/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_tenant/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_tenant/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_tenant/tasks/main.yml new file mode 100644 index 00000000..98478222 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_tenant/tasks/main.yml @@ -0,0 +1,261 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> +# Copyright: (c) 2020, Lionel Hercot (@lhercot) <lhercot@cisco.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + + +# CLEAN ENVIRONMENT +- name: Remove tenant + cisco.aci.aci_tenant: &tenant_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: ansible_test + state: absent + + +# ADD TENANT +- name: Add tenant (check_mode) + cisco.aci.aci_tenant: &tenant_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: ansible_test + state: present + annotation: ansible_test + owner_key: ansible_key + owner_tag: ansible_tag + check_mode: yes + register: cm_add_tenant + +- name: Add tenant (normal mode) + cisco.aci.aci_tenant: *tenant_present + register: nm_add_tenant + +- name: Add tenant again (check_mode) + cisco.aci.aci_tenant: *tenant_present + check_mode: yes + register: cm_add_tenant_again + +- name: Add tenant again (normal mode) + cisco.aci.aci_tenant: *tenant_present + register: nm_add_tenant_again + +- name: Verify add_tenant + assert: + that: + - cm_add_tenant is changed + - nm_add_tenant is changed + - cm_add_tenant_again is not changed + - nm_add_tenant_again is not changed + - nm_add_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test' + - cm_add_tenant.proposed.fvTenant.attributes.annotation == 'ansible_test' + - nm_add_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key' + - cm_add_tenant.proposed.fvTenant.attributes.ownerKey == 'ansible_key' + - nm_add_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag' + - cm_add_tenant.proposed.fvTenant.attributes.ownerTag == 'ansible_tag' + + +# CHANGE TENANT +- name: Change description and annotation/owner_tag/owner_key of tenant (check_mode) + cisco.aci.aci_tenant: + <<: *tenant_present + description: Ansible test tenant + annotation: ansible_test_changed + owner_key: ansible_key_changed + owner_tag: ansible_tag_changed + check_mode: yes + register: cm_add_tenant_descr + +- name: Change description and annotation/owner_tag/owner_key of tenant (normal mode) + cisco.aci.aci_tenant: + <<: *tenant_present + description: Ansible test tenant + annotation: ansible_test_changed + owner_key: ansible_key_changed + owner_tag: ansible_tag_changed + register: nm_add_tenant_descr + +- name: Change description and annotation/owner_tag/owner_key of tenant again (check_mode) + cisco.aci.aci_tenant: + <<: *tenant_present + description: Ansible test tenant + annotation: ansible_test_changed + owner_key: ansible_key_changed + owner_tag: ansible_tag_changed + check_mode: yes + register: cm_add_tenant_descr_again + +- name: Change description and annotation of tenant again (normal mode) + cisco.aci.aci_tenant: + <<: *tenant_present + description: Ansible test tenant + annotation: ansible_test_changed + owner_key: ansible_key_changed + owner_tag: ansible_tag_changed + register: nm_add_tenant_descr_again + +- name: Verify add_tenant_descr + assert: + that: + - cm_add_tenant_descr is changed + - nm_add_tenant_descr is changed + - cm_add_tenant_descr_again is not changed + - nm_add_tenant_descr_again is not changed + - cm_add_tenant_descr.proposed.fvTenant.attributes.annotation == 'ansible_test_changed' + - nm_add_tenant_descr.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' + - cm_add_tenant_descr_again.proposed.fvTenant.attributes.annotation == 'ansible_test_changed' + - nm_add_tenant_descr_again.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' + - nm_add_tenant_descr.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' + - cm_add_tenant_descr.proposed.fvTenant.attributes.ownerKey == 'ansible_key_changed' + - nm_add_tenant_descr_again.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' + - cm_add_tenant_descr_again.proposed.fvTenant.attributes.ownerKey == 'ansible_key_changed' + - nm_add_tenant_descr.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' + - cm_add_tenant_descr.proposed.fvTenant.attributes.ownerTag == 'ansible_tag_changed' + - nm_add_tenant_descr_again.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' + - cm_add_tenant_descr_again.proposed.fvTenant.attributes.ownerTag == 'ansible_tag_changed' + + +# ADD TENANT AGAIN +- name: Add tenant again with no description (check_mode) + cisco.aci.aci_tenant: + <<: *tenant_present + annotation: ansible_test_changed + owner_key: ansible_key_changed + owner_tag: ansible_tag_changed + check_mode: yes + register: cm_add_tenant_again_no_descr + +- name: Add tenant again with no description (normal mode) + cisco.aci.aci_tenant: + <<: *tenant_present + annotation: ansible_test_changed + owner_key: ansible_key_changed + owner_tag: ansible_tag_changed + register: nm_add_tenant_again_no_descr + +- name: Verify add_tenant_again_no_descr + assert: + that: + - cm_add_tenant_again_no_descr is not changed + - nm_add_tenant_again_no_descr is not changed + + +# QUERY ALL TENANTS +- name: Query all tenants (check_mode) + cisco.aci.aci_tenant: &tenant_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_tenants + +- name: Query all tenants (normal mode) + cisco.aci.aci_tenant: *tenant_query + register: nm_query_all_tenants + +- name: Verify query_all_tenants + assert: + that: + - cm_query_all_tenants is not changed + - nm_query_all_tenants is not changed + # NOTE: Order of tenants is not stable between calls + #- cm_query_all_tenants == nm_query_all_tenants + + +# QUERY A TENANT +- name: Query our tenant + cisco.aci.aci_tenant: + <<: *tenant_query + tenant: ansible_test + check_mode: yes + register: cm_query_tenant + +- name: Query our tenant + cisco.aci.aci_tenant: + <<: *tenant_query + tenant: ansible_test + register: nm_query_tenant + +- name: Verify query_tenant + assert: + that: + - cm_query_tenant is not changed + - nm_query_tenant is not changed + - cm_query_tenant == nm_query_tenant + - cm_query_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' + - nm_query_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' + - nm_query_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' + - cm_query_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' + - nm_query_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' + - cm_query_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' + + +# REMOVE TENANT +- name: Remove tenant (check_mode) + cisco.aci.aci_tenant: *tenant_absent + check_mode: yes + register: cm_remove_tenant + +- name: Remove tenant (normal mode) + cisco.aci.aci_tenant: *tenant_absent + register: nm_remove_tenant + +- name: Remove tenant again (check_mode) + cisco.aci.aci_tenant: *tenant_absent + check_mode: yes + register: cm_remove_tenant_again + +- name: Remove tenant again (normal mode) + cisco.aci.aci_tenant: *tenant_absent + register: nm_remove_tenant_again + +- name: Verify remove_tenant + assert: + that: + - cm_remove_tenant is changed + - nm_remove_tenant is changed + - cm_remove_tenant_again is not changed + - nm_remove_tenant_again is not changed + + +# QUERY NON-EXISTING TENANT +- name: Query non-existing tenant (check_mode) + cisco.aci.aci_tenant: + <<: *tenant_query + tenant: ansible_test + check_mode: yes + register: cm_query_non_tenant + +- name: Query non-existing tenant (normal mode) + cisco.aci.aci_tenant: + <<: *tenant_query + tenant: ansible_test + register: nm_query_non_tenant + +# TODO: Implement more tests +- name: Verify query_non_tenant + assert: + that: + - cm_query_non_tenant is not changed + - nm_query_non_tenant is not changed + - cm_query_non_tenant == nm_query_non_tenant diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool/tasks/dynamic.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool/tasks/dynamic.yml new file mode 100644 index 00000000..6153f813 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool/tasks/dynamic.yml @@ -0,0 +1,303 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +# CLEAN ENVIRONMENT +- name: Remove dynamic vlan pool + cisco.aci.aci_vlan_pool: &dynamic_vlan_pool_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + pool: anstest + pool_allocation_mode: dynamic + state: absent + + +# ADD VLAN POOL +- name: Add dynamic vlan pool (check_mode) + cisco.aci.aci_vlan_pool: &dynamic_vlan_pool_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + pool: anstest + pool_allocation_mode: dynamic + state: present + check_mode: yes + register: cm_add_dynamic_vlan_pool + +- name: Add dynamic vlan pool (normal mode) + cisco.aci.aci_vlan_pool: *dynamic_vlan_pool_present + register: nm_add_dynamic_vlan_pool + +- name: Verify add_dynamic_vlan_pool + assert: + that: + - cm_add_dynamic_vlan_pool is changed + - nm_add_dynamic_vlan_pool is changed + - cm_add_dynamic_vlan_pool.sent.fvnsVlanInstP.attributes.allocMode == nm_add_dynamic_vlan_pool.sent.fvnsVlanInstP.attributes.allocMode == 'dynamic' + - cm_add_dynamic_vlan_pool.sent.fvnsVlanInstP.attributes.name == nm_add_dynamic_vlan_pool.sent.fvnsVlanInstP.attributes.name == 'anstest' + - cm_add_dynamic_vlan_pool.proposed.fvnsVlanInstP.attributes.allocMode == nm_add_dynamic_vlan_pool.proposed.fvnsVlanInstP.attributes.allocMode == 'dynamic' + - cm_add_dynamic_vlan_pool.proposed.fvnsVlanInstP.attributes.name == nm_add_dynamic_vlan_pool.proposed.fvnsVlanInstP.attributes.name == 'anstest' + - cm_add_dynamic_vlan_pool.previous == nm_add_dynamic_vlan_pool.previous == [] + # NOTE: We cannot fix this easily + - cm_add_dynamic_vlan_pool.current == [] + - nm_add_dynamic_vlan_pool.current.0.fvnsVlanInstP.attributes.allocMode == 'dynamic' + - nm_add_dynamic_vlan_pool.current.0.fvnsVlanInstP.attributes.descr == '' + - nm_add_dynamic_vlan_pool.current.0.fvnsVlanInstP.attributes.dn == 'uni/infra/vlanns-[anstest]-dynamic' + - nm_add_dynamic_vlan_pool.current.0.fvnsVlanInstP.attributes.name == 'anstest' + +- name: Add dynamic_vlan_pool again (check_mode) + cisco.aci.aci_vlan_pool: *dynamic_vlan_pool_present + check_mode: yes + register: cm_add_dynamic_vlan_pool_again + +- name: Add dynamic vlan pool again (normal mode) + cisco.aci.aci_vlan_pool: *dynamic_vlan_pool_present + register: nm_add_dynamic_vlan_pool_again + +- name: Verify add_dynamic_vlan_pool_again + assert: + that: + - cm_add_dynamic_vlan_pool_again is not changed + - nm_add_dynamic_vlan_pool_again is not changed + - cm_add_dynamic_vlan_pool_again.current == nm_add_dynamic_vlan_pool_again.current == nm_add_dynamic_vlan_pool.current + + +# CHANGE VLAN POOL +- name: Change description of dynamic vlan pool (check_mode) + cisco.aci.aci_vlan_pool: + <<: *dynamic_vlan_pool_present + description: Ansible test dynamic vlan pool + check_mode: yes + register: cm_add_dynamic_vlan_pool_descr + +- name: Change description of dynamic vlan pool (normal mode) + cisco.aci.aci_vlan_pool: + <<: *dynamic_vlan_pool_present + description: Ansible test dynamic vlan pool + register: nm_add_dynamic_vlan_pool_descr + +- name: Verify add_dynamic_vlan_pool_descr + assert: + that: + - cm_add_dynamic_vlan_pool_descr is changed + - nm_add_dynamic_vlan_pool_descr is changed + - cm_add_dynamic_vlan_pool_descr.sent.fvnsVlanInstP.attributes.descr == nm_add_dynamic_vlan_pool_descr.sent.fvnsVlanInstP.attributes.descr == 'Ansible test dynamic vlan pool' + - cm_add_dynamic_vlan_pool_descr.proposed.fvnsVlanInstP.attributes.allocMode == nm_add_dynamic_vlan_pool_descr.proposed.fvnsVlanInstP.attributes.allocMode == 'dynamic' + - cm_add_dynamic_vlan_pool_descr.proposed.fvnsVlanInstP.attributes.descr == nm_add_dynamic_vlan_pool_descr.proposed.fvnsVlanInstP.attributes.descr == 'Ansible test dynamic vlan pool' + - cm_add_dynamic_vlan_pool_descr.proposed.fvnsVlanInstP.attributes.name == nm_add_dynamic_vlan_pool_descr.proposed.fvnsVlanInstP.attributes.name == 'anstest' + - cm_add_dynamic_vlan_pool_descr.previous == nm_add_dynamic_vlan_pool_descr.previous == cm_add_dynamic_vlan_pool_descr.current == nm_add_dynamic_vlan_pool.current + - nm_add_dynamic_vlan_pool_descr.current.0.fvnsVlanInstP.attributes.allocMode == 'dynamic' + - nm_add_dynamic_vlan_pool_descr.current.0.fvnsVlanInstP.attributes.descr == 'Ansible test dynamic vlan pool' + - nm_add_dynamic_vlan_pool_descr.current.0.fvnsVlanInstP.attributes.dn == 'uni/infra/vlanns-[anstest]-dynamic' + - nm_add_dynamic_vlan_pool_descr.current.0.fvnsVlanInstP.attributes.name == 'anstest' + +- name: Change description of dynamic vlan pool again (check_mode) + cisco.aci.aci_vlan_pool: + <<: *dynamic_vlan_pool_present + description: Ansible test dynamic vlan pool + check_mode: yes + register: cm_add_dynamic_vlan_pool_descr_again + +- name: Change description of dynamic vlan pool again (normal mode) + cisco.aci.aci_vlan_pool: + <<: *dynamic_vlan_pool_present + description: Ansible test dynamic vlan pool + register: nm_add_dynamic_vlan_pool_descr_again + +- name: Verify add_dynamic_vlan_pool_descr_again + assert: + that: + - cm_add_dynamic_vlan_pool_descr_again is not changed + - nm_add_dynamic_vlan_pool_descr_again is not changed + - cm_add_dynamic_vlan_pool_descr_again.current == nm_add_dynamic_vlan_pool_descr_again.current == nm_add_dynamic_vlan_pool_descr.current + + +# ADD VLAN POOL AGAIN +- name: Add dynamic vlan pool again with no description (check_mode) + cisco.aci.aci_vlan_pool: *dynamic_vlan_pool_present + check_mode: yes + register: cm_add_dynamic_vlan_pool_again_no_descr + +- name: Add dynamic vlan pool again with no description (normal mode) + cisco.aci.aci_vlan_pool: *dynamic_vlan_pool_present + register: nm_add_dynamic_vlan_pool_again_no_descr + +- name: Verify add_dynamic_vlan_pool_again_no_descr + assert: + that: + - cm_add_dynamic_vlan_pool_again_no_descr is not changed + - nm_add_dynamic_vlan_pool_again_no_descr is not changed + - cm_add_dynamic_vlan_pool_again_no_descr.current == nm_add_dynamic_vlan_pool_again_no_descr.current == nm_add_dynamic_vlan_pool_descr.current + + +# QUERY ALL VLAN POOLS +- name: Query all dynamic vlan pools (check_mode) + cisco.aci.aci_vlan_pool: &dynamic_vlan_pool_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_dynamic_vlan_pools + +- name: Query all dynamic vlan pools (normal mode) + cisco.aci.aci_vlan_pool: *dynamic_vlan_pool_query + register: nm_query_all_dynamic_vlan_pools + +- name: Verify query_all_dynamic_vlan_pools + assert: + that: + - cm_query_all_dynamic_vlan_pools is not changed + - nm_query_all_dynamic_vlan_pools is not changed + - cm_query_all_dynamic_vlan_pools == nm_query_all_dynamic_vlan_pools + - cm_query_all_dynamic_vlan_pools.current|length >= 1 + + +# QUERY A VLAN POOL +- name: Query our dynamic vlan pool + cisco.aci.aci_vlan_pool: + <<: *dynamic_vlan_pool_query + pool: anstest + pool_allocation_mode: dynamic + check_mode: yes + register: cm_query_dynamic_vlan_pool + +- name: Query our dynamic vlan pool + cisco.aci.aci_vlan_pool: + <<: *dynamic_vlan_pool_query + pool: anstest + pool_allocation_mode: dynamic + register: nm_query_dynamic_vlan_pool + +- name: Verify query_dynamic_vlan_pool + assert: + that: + - cm_query_dynamic_vlan_pool is not changed + - nm_query_dynamic_vlan_pool is not changed + - cm_query_dynamic_vlan_pool == nm_query_dynamic_vlan_pool + - nm_query_dynamic_vlan_pool.current.0.fvnsVlanInstP.attributes.allocMode == 'dynamic' + - nm_query_dynamic_vlan_pool.current.0.fvnsVlanInstP.attributes.descr == 'Ansible test dynamic vlan pool' + - nm_query_dynamic_vlan_pool.current.0.fvnsVlanInstP.attributes.dn == 'uni/infra/vlanns-[anstest]-dynamic' + - nm_query_dynamic_vlan_pool.current.0.fvnsVlanInstP.attributes.name == 'anstest' + + +# REMOVE VLAN POOL +- name: Remove dynamic vlan pool (check_mode) + cisco.aci.aci_vlan_pool: *dynamic_vlan_pool_absent + check_mode: yes + register: cm_remove_dynamic_vlan_pool + +- name: Remove dynamic vlan pool (normal mode) + cisco.aci.aci_vlan_pool: *dynamic_vlan_pool_absent + register: nm_remove_dynamic_vlan_pool + +- name: Verify remove_dynamic_vlan_pool + assert: + that: + - cm_remove_dynamic_vlan_pool is changed + - nm_remove_dynamic_vlan_pool is changed + - cm_remove_dynamic_vlan_pool.current.0.fvnsVlanInstP.attributes.allocMode == cm_remove_dynamic_vlan_pool.previous.0.fvnsVlanInstP.attributes.allocMode == nm_remove_dynamic_vlan_pool.previous.0.fvnsVlanInstP.attributes.allocMode == 'dynamic' + - cm_remove_dynamic_vlan_pool.current.0.fvnsVlanInstP.attributes.descr == cm_remove_dynamic_vlan_pool.previous.0.fvnsVlanInstP.attributes.descr == nm_remove_dynamic_vlan_pool.previous.0.fvnsVlanInstP.attributes.descr == 'Ansible test dynamic vlan pool' + - cm_remove_dynamic_vlan_pool.current.0.fvnsVlanInstP.attributes.dn == cm_remove_dynamic_vlan_pool.previous.0.fvnsVlanInstP.attributes.dn == nm_remove_dynamic_vlan_pool.previous.0.fvnsVlanInstP.attributes.dn == 'uni/infra/vlanns-[anstest]-dynamic' + - cm_remove_dynamic_vlan_pool.current.0.fvnsVlanInstP.attributes.name == cm_remove_dynamic_vlan_pool.previous.0.fvnsVlanInstP.attributes.name == nm_remove_dynamic_vlan_pool.previous.0.fvnsVlanInstP.attributes.name == 'anstest' + - nm_remove_dynamic_vlan_pool.current == [] + +- name: Remove dynamic vlan pool again (check_mode) + cisco.aci.aci_vlan_pool: *dynamic_vlan_pool_absent + check_mode: yes + register: cm_remove_dynamic_vlan_pool_again + +- name: Remove dynamic vlan pool again (normal mode) + cisco.aci.aci_vlan_pool: *dynamic_vlan_pool_absent + register: nm_remove_dynamic_vlan_pool_again + +- name: Verify remove_dynamic_vlan_pool_again + assert: + that: + - cm_remove_dynamic_vlan_pool_again is not changed + - nm_remove_dynamic_vlan_pool_again is not changed + - cm_remove_dynamic_vlan_pool_again.proposed == nm_remove_dynamic_vlan_pool_again.proposed == {} + - cm_remove_dynamic_vlan_pool_again.sent == nm_remove_dynamic_vlan_pool_again.sent == {} + - cm_remove_dynamic_vlan_pool_again.previous == nm_remove_dynamic_vlan_pool_again.previous == [] + - cm_remove_dynamic_vlan_pool_again.current == nm_remove_dynamic_vlan_pool_again.current == [] + + +# QUERY NON-EXISTING VLAN POOL +- name: Query non-existing dynamic vlan pool (check_mode) + cisco.aci.aci_vlan_pool: + <<: *dynamic_vlan_pool_query + pool: anstest + pool_allocation_mode: dynamic + check_mode: yes + register: cm_query_non_dynamic_vlan_pool + +- name: Query non-existing dynamic vlan pool (normal mode) + cisco.aci.aci_vlan_pool: + <<: *dynamic_vlan_pool_query + pool: anstest + pool_allocation_mode: dynamic + register: nm_query_non_dynamic_vlan_pool + +# TODO: Implement more tests +- name: Verify query_non_dynamic_vlan_pool + assert: + that: + - cm_query_non_dynamic_vlan_pool is not changed + - nm_query_non_dynamic_vlan_pool is not changed + - cm_remove_dynamic_vlan_pool_again.previous == nm_remove_dynamic_vlan_pool_again.previous == [] + - cm_remove_dynamic_vlan_pool_again.current == nm_remove_dynamic_vlan_pool_again.current == [] + + +# PROVOKE ERRORS +- name: Error when required parameter is missing + cisco.aci.aci_vlan_pool: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: present + ignore_errors: yes + register: error_on_missing_required_param + +- name: Verify error_on_missing_required_param + assert: + that: + - error_on_missing_required_param is failed + - 'error_on_missing_required_param.msg == "state is present but all of the following are missing: pool"' + +- name: Error when together parameter is missing + cisco.aci.aci_vlan_pool: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + pool: anstest + state: present + ignore_errors: yes + register: error_on_missing_together_param + +- name: Verify error_on_missing_together_param + assert: + that: + - error_on_missing_together_param is failed + - error_on_missing_together_param.msg == "ACI requires the 'pool_allocation_mode' when 'pool' is provided" diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool/tasks/main.yml new file mode 100644 index 00000000..e19ced38 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool/tasks/main.yml @@ -0,0 +1,15 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- include_tasks: static.yml + when: static is not defined or static + +- include_tasks: dynamic.yml + when: dynamic is not defined or dynamic diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool/tasks/static.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool/tasks/static.yml new file mode 100644 index 00000000..9962f095 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool/tasks/static.yml @@ -0,0 +1,303 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +# CLEAN ENVIRONMENT +- name: Remove static vlan pool + cisco.aci.aci_vlan_pool: &static_vlan_pool_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + pool: anstest + pool_allocation_mode: static + state: absent + + +# ADD VLAN POOL +- name: Add static vlan pool (check_mode) + cisco.aci.aci_vlan_pool: &static_vlan_pool_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + pool: anstest + pool_allocation_mode: static + state: present + check_mode: yes + register: cm_add_static_vlan_pool + +- name: Add static vlan pool (normal mode) + cisco.aci.aci_vlan_pool: *static_vlan_pool_present + register: nm_add_static_vlan_pool + +- name: Verify add_static_vlan_pool + assert: + that: + - cm_add_static_vlan_pool is changed + - nm_add_static_vlan_pool is changed + - cm_add_static_vlan_pool.sent.fvnsVlanInstP.attributes.allocMode == nm_add_static_vlan_pool.sent.fvnsVlanInstP.attributes.allocMode == 'static' + - cm_add_static_vlan_pool.sent.fvnsVlanInstP.attributes.name == nm_add_static_vlan_pool.sent.fvnsVlanInstP.attributes.name == 'anstest' + - cm_add_static_vlan_pool.proposed.fvnsVlanInstP.attributes.allocMode == nm_add_static_vlan_pool.proposed.fvnsVlanInstP.attributes.allocMode == 'static' + - cm_add_static_vlan_pool.proposed.fvnsVlanInstP.attributes.name == nm_add_static_vlan_pool.proposed.fvnsVlanInstP.attributes.name == 'anstest' + - cm_add_static_vlan_pool.previous == nm_add_static_vlan_pool.previous == [] + # NOTE: We cannot fix this easily + - cm_add_static_vlan_pool.current == [] + - nm_add_static_vlan_pool.current.0.fvnsVlanInstP.attributes.allocMode == 'static' + - nm_add_static_vlan_pool.current.0.fvnsVlanInstP.attributes.descr == '' + - nm_add_static_vlan_pool.current.0.fvnsVlanInstP.attributes.dn == 'uni/infra/vlanns-[anstest]-static' + - nm_add_static_vlan_pool.current.0.fvnsVlanInstP.attributes.name == 'anstest' + +- name: Add static_vlan_pool again (check_mode) + cisco.aci.aci_vlan_pool: *static_vlan_pool_present + check_mode: yes + register: cm_add_static_vlan_pool_again + +- name: Add static vlan pool again (normal mode) + cisco.aci.aci_vlan_pool: *static_vlan_pool_present + register: nm_add_static_vlan_pool_again + +- name: Verify add_static_vlan_pool_again + assert: + that: + - cm_add_static_vlan_pool_again is not changed + - nm_add_static_vlan_pool_again is not changed + - cm_add_static_vlan_pool_again.current == nm_add_static_vlan_pool_again.current == nm_add_static_vlan_pool.current + + +# CHANGE VLAN POOL +- name: Change description of static vlan pool (check_mode) + cisco.aci.aci_vlan_pool: + <<: *static_vlan_pool_present + description: Ansible test static vlan pool + check_mode: yes + register: cm_add_static_vlan_pool_descr + +- name: Change description of static vlan pool (normal mode) + cisco.aci.aci_vlan_pool: + <<: *static_vlan_pool_present + description: Ansible test static vlan pool + register: nm_add_static_vlan_pool_descr + +- name: Verify add_static_vlan_pool_descr + assert: + that: + - cm_add_static_vlan_pool_descr is changed + - nm_add_static_vlan_pool_descr is changed + - cm_add_static_vlan_pool_descr.sent.fvnsVlanInstP.attributes.descr == nm_add_static_vlan_pool_descr.sent.fvnsVlanInstP.attributes.descr == 'Ansible test static vlan pool' + - cm_add_static_vlan_pool_descr.proposed.fvnsVlanInstP.attributes.allocMode == nm_add_static_vlan_pool_descr.proposed.fvnsVlanInstP.attributes.allocMode == 'static' + - cm_add_static_vlan_pool_descr.proposed.fvnsVlanInstP.attributes.descr == nm_add_static_vlan_pool_descr.proposed.fvnsVlanInstP.attributes.descr == 'Ansible test static vlan pool' + - cm_add_static_vlan_pool_descr.proposed.fvnsVlanInstP.attributes.name == nm_add_static_vlan_pool_descr.proposed.fvnsVlanInstP.attributes.name == 'anstest' + - cm_add_static_vlan_pool_descr.previous == nm_add_static_vlan_pool_descr.previous == cm_add_static_vlan_pool_descr.current == nm_add_static_vlan_pool.current + - nm_add_static_vlan_pool_descr.current.0.fvnsVlanInstP.attributes.allocMode == 'static' + - nm_add_static_vlan_pool_descr.current.0.fvnsVlanInstP.attributes.descr == 'Ansible test static vlan pool' + - nm_add_static_vlan_pool_descr.current.0.fvnsVlanInstP.attributes.dn == 'uni/infra/vlanns-[anstest]-static' + - nm_add_static_vlan_pool_descr.current.0.fvnsVlanInstP.attributes.name == 'anstest' + +- name: Change description of static vlan pool again (check_mode) + cisco.aci.aci_vlan_pool: + <<: *static_vlan_pool_present + description: Ansible test static vlan pool + check_mode: yes + register: cm_add_static_vlan_pool_descr_again + +- name: Change description of static vlan pool again (normal mode) + cisco.aci.aci_vlan_pool: + <<: *static_vlan_pool_present + description: Ansible test static vlan pool + register: nm_add_static_vlan_pool_descr_again + +- name: Verify add_static_vlan_pool_descr_again + assert: + that: + - cm_add_static_vlan_pool_descr_again is not changed + - nm_add_static_vlan_pool_descr_again is not changed + - cm_add_static_vlan_pool_descr_again.current == nm_add_static_vlan_pool_descr_again.current == nm_add_static_vlan_pool_descr.current + + +# ADD VLAN POOL AGAIN +- name: Add static vlan pool again with no description (check_mode) + cisco.aci.aci_vlan_pool: *static_vlan_pool_present + check_mode: yes + register: cm_add_static_vlan_pool_again_no_descr + +- name: Add static vlan pool again with no description (normal mode) + cisco.aci.aci_vlan_pool: *static_vlan_pool_present + register: nm_add_static_vlan_pool_again_no_descr + +- name: Verify add_static_vlan_pool_again_no_descr + assert: + that: + - cm_add_static_vlan_pool_again_no_descr is not changed + - nm_add_static_vlan_pool_again_no_descr is not changed + - cm_add_static_vlan_pool_again_no_descr.current == nm_add_static_vlan_pool_again_no_descr.current == nm_add_static_vlan_pool_descr.current + + +# QUERY ALL VLAN POOLS +- name: Query all static vlan pools (check_mode) + cisco.aci.aci_vlan_pool: &static_vlan_pool_query + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_static_vlan_pools + +- name: Query all static vlan pools (normal mode) + cisco.aci.aci_vlan_pool: *static_vlan_pool_query + register: nm_query_all_static_vlan_pools + +- name: Verify query_all_static_vlan_pools + assert: + that: + - cm_query_all_static_vlan_pools is not changed + - nm_query_all_static_vlan_pools is not changed + - cm_query_all_static_vlan_pools == nm_query_all_static_vlan_pools + - cm_query_all_static_vlan_pools.current|length >= 1 + + +# QUERY A VLAN POOL +- name: Query our static vlan pool + cisco.aci.aci_vlan_pool: + <<: *static_vlan_pool_query + pool: anstest + pool_allocation_mode: static + check_mode: yes + register: cm_query_static_vlan_pool + +- name: Query our static vlan pool + cisco.aci.aci_vlan_pool: + <<: *static_vlan_pool_query + pool: anstest + pool_allocation_mode: static + register: nm_query_static_vlan_pool + +- name: Verify query_static_vlan_pool + assert: + that: + - cm_query_static_vlan_pool is not changed + - nm_query_static_vlan_pool is not changed + - cm_query_static_vlan_pool == nm_query_static_vlan_pool + - nm_query_static_vlan_pool.current.0.fvnsVlanInstP.attributes.allocMode == 'static' + - nm_query_static_vlan_pool.current.0.fvnsVlanInstP.attributes.descr == 'Ansible test static vlan pool' + - nm_query_static_vlan_pool.current.0.fvnsVlanInstP.attributes.dn == 'uni/infra/vlanns-[anstest]-static' + - nm_query_static_vlan_pool.current.0.fvnsVlanInstP.attributes.name == 'anstest' + + +# REMOVE VLAN POOL +- name: Remove static vlan pool (check_mode) + cisco.aci.aci_vlan_pool: *static_vlan_pool_absent + check_mode: yes + register: cm_remove_static_vlan_pool + +- name: Remove static vlan pool (normal mode) + cisco.aci.aci_vlan_pool: *static_vlan_pool_absent + register: nm_remove_static_vlan_pool + +- name: Verify remove_static_vlan_pool + assert: + that: + - cm_remove_static_vlan_pool is changed + - nm_remove_static_vlan_pool is changed + - cm_remove_static_vlan_pool.current.0.fvnsVlanInstP.attributes.allocMode == cm_remove_static_vlan_pool.previous.0.fvnsVlanInstP.attributes.allocMode == nm_remove_static_vlan_pool.previous.0.fvnsVlanInstP.attributes.allocMode == 'static' + - cm_remove_static_vlan_pool.current.0.fvnsVlanInstP.attributes.descr == cm_remove_static_vlan_pool.previous.0.fvnsVlanInstP.attributes.descr == nm_remove_static_vlan_pool.previous.0.fvnsVlanInstP.attributes.descr == 'Ansible test static vlan pool' + - cm_remove_static_vlan_pool.current.0.fvnsVlanInstP.attributes.dn == cm_remove_static_vlan_pool.previous.0.fvnsVlanInstP.attributes.dn == nm_remove_static_vlan_pool.previous.0.fvnsVlanInstP.attributes.dn == 'uni/infra/vlanns-[anstest]-static' + - cm_remove_static_vlan_pool.current.0.fvnsVlanInstP.attributes.name == cm_remove_static_vlan_pool.previous.0.fvnsVlanInstP.attributes.name == nm_remove_static_vlan_pool.previous.0.fvnsVlanInstP.attributes.name == 'anstest' + - nm_remove_static_vlan_pool.current == [] + +- name: Remove static vlan pool again (check_mode) + cisco.aci.aci_vlan_pool: *static_vlan_pool_absent + check_mode: yes + register: cm_remove_static_vlan_pool_again + +- name: Remove static vlan pool again (normal mode) + cisco.aci.aci_vlan_pool: *static_vlan_pool_absent + register: nm_remove_static_vlan_pool_again + +- name: Verify remove_static_vlan_pool_again + assert: + that: + - cm_remove_static_vlan_pool_again is not changed + - nm_remove_static_vlan_pool_again is not changed + - cm_remove_static_vlan_pool_again.proposed == nm_remove_static_vlan_pool_again.proposed == {} + - cm_remove_static_vlan_pool_again.sent == nm_remove_static_vlan_pool_again.sent == {} + - cm_remove_static_vlan_pool_again.previous == nm_remove_static_vlan_pool_again.previous == [] + - cm_remove_static_vlan_pool_again.current == nm_remove_static_vlan_pool_again.current == [] + + +# QUERY NON-EXISTING VLAN POOL +- name: Query non-existing static vlan pool (check_mode) + cisco.aci.aci_vlan_pool: + <<: *static_vlan_pool_query + pool: anstest + pool_allocation_mode: static + check_mode: yes + register: cm_query_non_static_vlan_pool + +- name: Query non-existing static vlan pool (normal mode) + cisco.aci.aci_vlan_pool: + <<: *static_vlan_pool_query + pool: anstest + pool_allocation_mode: static + register: nm_query_non_static_vlan_pool + +# TODO: Implement more tests +- name: Verify query_non_static_vlan_pool + assert: + that: + - cm_query_non_static_vlan_pool is not changed + - nm_query_non_static_vlan_pool is not changed + - cm_remove_static_vlan_pool_again.previous == nm_remove_static_vlan_pool_again.previous == [] + - cm_remove_static_vlan_pool_again.current == nm_remove_static_vlan_pool_again.current == [] + + +# PROVOKE ERRORS +- name: Error when required parameter is missing + cisco.aci.aci_vlan_pool: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: present + ignore_errors: yes + register: error_on_missing_required_param + +- name: Verify error_on_missing_required_param + assert: + that: + - error_on_missing_required_param is failed + - 'error_on_missing_required_param.msg == "state is present but all of the following are missing: pool"' + +- name: Error when together parameter is missing + cisco.aci.aci_vlan_pool: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + pool: anstest + state: present + ignore_errors: yes + register: error_on_missing_together_param + +- name: Verify error_on_missing_together_param + assert: + that: + - error_on_missing_together_param is failed + - error_on_missing_together_param.msg == "ACI requires the 'pool_allocation_mode' when 'pool' is provided" diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool_encap_block/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool_encap_block/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool_encap_block/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool_encap_block/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool_encap_block/tasks/main.yml new file mode 100644 index 00000000..913882e1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vlan_pool_encap_block/tasks/main.yml @@ -0,0 +1,384 @@ +# Test code for the ACI modules + +# Copyright: (c) 2017, Jacob McGill (jmcgill298) +# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com> +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an aci apic host, aci username and aci password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Ensure vlan pool exists for tests to kick off + cisco.aci.aci_vlan_pool: + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + state: absent + pool: anstest + allocation_mode: static + description: Ansible Test + +- name: Ensure vlan pool exists for tests to kick off + cisco.aci.aci_vlan_pool: &aci_pool_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: present + pool: anstest + allocation_mode: static + description: Ansible Test + register: pool_present + +- name: Create vlan pool encap block - check mode works + cisco.aci.aci_vlan_pool_encap_block: &aci_encap_block_present + <<: *aci_pool_present + block_name: anstest + block_start: 20 + block_end: 40 + pool: anstest + pool_allocation_mode: static + allocation_mode: inherit + description: Ansible Test + check_mode: yes + register: encap_block_present_check_mode + +- name: Present assertions + assert: + that: + - encap_block_present_check_mode is changed + - encap_block_present_check_mode.sent.fvnsEncapBlk.attributes.allocMode == 'inherit' + - encap_block_present_check_mode.sent.fvnsEncapBlk.attributes.descr == 'Ansible Test' + - encap_block_present_check_mode.sent.fvnsEncapBlk.attributes.from == 'vlan-20' + - encap_block_present_check_mode.sent.fvnsEncapBlk.attributes.to == 'vlan-40' + - encap_block_present_check_mode.sent.fvnsEncapBlk.attributes.name == 'anstest' + +- name: Create vlan pool encap_block - creation works + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_present + register: encap_block_present + +- name: Present assertions + assert: + that: + - encap_block_present is changed + - encap_block_present.previous == [] + - encap_block_present.sent == encap_block_present_check_mode.sent + - encap_block_present.sent == encap_block_present.proposed + +- name: Create vlan pool range - idempotency works + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_present + register: encap_block_present_idempotent + +- name: Present assertions + assert: + that: + - encap_block_present_idempotent is not changed + - encap_block_present_idempotent.previous.0.fvnsEncapBlk.attributes.name == "anstest" + +- name: Update vlan pool range - update works + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_present + description: Ansible Test Update + allocation_mode: inherit + register: encap_block_present_update + +- name: Present assertions + assert: + that: + - encap_block_present_update is changed + - encap_block_present_update.previous != [] + - encap_block_present_update.sent != encap_block_present.sent + +- name: Create vlan pool range - used for query + cisco.aci.aci_vlan_pool_encap_block: &aci_encap_block_present_2 + <<: *aci_encap_block_present + block_name: anstest_2 + block_start: 50 + block_end: 55 + register: encap_block_present_2 + +- name: Present assertions + assert: + that: + - encap_block_present_2 is changed + - encap_block_present_2.previous == [] + +- name: Invalid encap_block_start - error message works + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_present + block_start: 0 + ignore_errors: yes + register: encap_block_start_low + +- name: Present assertions + assert: + that: + - encap_block_start_low is failed + - encap_block_start_low.msg == "vlan pools must have 'block_start' and 'block_end' values between 1 and 4094" + +- name: Invalid encap_block_start - error message works + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_present + block_start: 4096 + ignore_errors: yes + register: encap_block_start_high + +- name: Present assertions + assert: + that: + - encap_block_start_high is failed + - encap_block_start_high.msg == "vlan pools must have 'block_start' and 'block_end' values between 1 and 4094" + +- name: Invalid encap_block_end - error message works + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_present + block_end: 0 + ignore_errors: yes + register: encap_block_end_low + +- name: Present assertions + assert: + that: + - encap_block_end_low is failed + - encap_block_end_low.msg == "vlan pools must have 'block_start' and 'block_end' values between 1 and 4094" + +- name: Invalid encap_block_end - error message works + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_present + block_end: 4096 + ignore_errors: yes + register: encap_block_end_high + +- name: Present assertions + assert: + that: + - encap_block_end_high is failed + - encap_block_end_high.msg == "vlan pools must have 'block_start' and 'block_end' values between 1 and 4094" + +- name: Range start higher than range end - error message works + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_present + block_start: 1000 + ignore_errors: yes + register: encap_block_start_end + +- name: Present assertions + assert: + that: + - encap_block_start_end is failed + - encap_block_start_end.msg == "The 'block_start' must be less than or equal to the 'block_end'" + +- name: Missing required param - error message works + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_pool_present + ignore_errors: yes + register: encap_block_present_missing_param + +- name: Present assertions + assert: + that: + - encap_block_present_missing_param is failed + - 'encap_block_present_missing_param.msg == "state is present but all of the following are missing: block_end, block_name, block_start"' + +- name: Missing required param - error message works + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_present + pool_allocation_mode: "{{ fake_var | default(omit) }}" + ignore_errors: yes + register: encap_block_present_allocation + +- name: Present assertions + assert: + that: + - encap_block_present_allocation is failed + - encap_block_present_allocation.msg == "ACI requires the 'pool_allocation_mode' when 'pool' is provided" + +- name: Query specific vlan pool range + cisco.aci.aci_vlan_pool_encap_block: &aci_encap_block_query + <<: *aci_encap_block_present + state: query + register: encap_block_query + +- name: Query assertions + assert: + that: + - encap_block_query is not changed + - encap_block_query.url.endswith("infra/vlanns-[anstest]-static/from-[vlan-20]-to-[vlan-40].json") + - encap_block_query.current | length == 1 + - encap_block_query.current.0.fvnsEncapBlk.attributes.name == "anstest" + +- name: Query vlan pool range - from, to, and name are filtered + cisco.aci.aci_vlan_pool_encap_block: &aci_encap_block_query_filter + <<: *aci_encap_block_query + pool: "{{ fake_var | default(omit) }}" + register: encap_block_query_from_to_name + +- name: Query assertions + assert: + that: + - encap_block_query_from_to_name is not changed + - encap_block_query_from_to_name.url.endswith("class/fvnsEncapBlk.json") + - '"eq(fvnsEncapBlk.from,\"vlan-20\")" in encap_block_query_from_to_name.filter_string' + - '"eq(fvnsEncapBlk.name,\"anstest\")" in encap_block_query_from_to_name.filter_string' + - '"eq(fvnsEncapBlk.to,\"vlan-40\")" in encap_block_query_from_to_name.filter_string' + - encap_block_query_from_to_name.current.0.fvnsEncapBlk.attributes.name == "anstest" + - encap_block_query_from_to_name.current.0.fvnsEncapBlk.attributes.from == "vlan-20" + - encap_block_query_from_to_name.current.0.fvnsEncapBlk.attributes.to == "vlan-40" + +- name: Query vlan pool range - from and name are filtered + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_query_filter + block_end: "{{ fake_var | default(omit) }}" + register: encap_block_query_from_name + +- name: Query assertions + assert: + that: + - encap_block_query_from_name is not changed + - encap_block_query_from_name.url.endswith("class/fvnsEncapBlk.json") + - '"eq(fvnsEncapBlk.from,\"vlan-20\")" in encap_block_query_from_name.filter_string' + - '"eq(fvnsEncapBlk.name,\"anstest\")" in encap_block_query_from_name.filter_string' + - encap_block_query_from_name.current.0.fvnsEncapBlk.attributes.name == "anstest" + - encap_block_query_from_name.current.0.fvnsEncapBlk.attributes.from == "vlan-20" + +- name: Query vlan pool range - to and name are filtered + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_query_filter + block_start: "{{ fake_var | default(omit) }}" + register: encap_block_query_to_name + +- name: Query assertions + assert: + that: + - encap_block_query_to_name is not changed + - encap_block_query_to_name.url.endswith("class/fvnsEncapBlk.json") + - '"eq(fvnsEncapBlk.name,\"anstest\")" in encap_block_query_to_name.filter_string' + - '"eq(fvnsEncapBlk.to,\"vlan-40\")" in encap_block_query_to_name.filter_string' + - encap_block_query_to_name.current.0.fvnsEncapBlk.attributes.name == "anstest" + - encap_block_query_to_name.current.0.fvnsEncapBlk.attributes.to == "vlan-40" + +- name: Query vlan pool range - name is filtered + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_query_filter + block_start: "{{ fake_var | default(omit) }}" + block_end: "{{ fake_var | default(omit) }}" + register: encap_block_query_name + +- name: Query assertions + assert: + that: + - encap_block_query_name is not changed + - encap_block_query_name.url.endswith("class/fvnsEncapBlk.json") + - '"eq(fvnsEncapBlk.name,\"anstest\")" in encap_block_query_name.filter_string' + - encap_block_query_name.current.0.fvnsEncapBlk.attributes.name == "anstest" + +- name: Query vlan pool range - from and to are filtered + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_query_filter + block_name: "{{ fake_var | default(omit) }}" + register: encap_block_query_from_to + +- name: Query assertions + assert: + that: + - encap_block_query_from_to is not changed + - encap_block_query_from_to.url.endswith("class/fvnsEncapBlk.json") + - '"eq(fvnsEncapBlk.from,\"vlan-20\")" in encap_block_query_from_to.filter_string' + - '"eq(fvnsEncapBlk.to,\"vlan-40\")" in encap_block_query_from_to.filter_string' + - encap_block_query_from_to.current.0.fvnsEncapBlk.attributes.from == "vlan-20" + - encap_block_query_from_to.current.0.fvnsEncapBlk.attributes.to == "vlan-40" + +- name: Query all ranges in a vlan pool + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_pool_present + state: query + pool_allocation_mode: static + register: encap_block_query_pool + +- name: Query assertions + assert: + that: + - encap_block_query_pool is not changed + - encap_block_query_pool.current | length == 1 + - encap_block_query_pool.current.0.fvnsVlanInstP.attributes.name == "anstest" + - encap_block_query_pool.current.0.fvnsVlanInstP.children | length > 1 + - encap_block_query_pool.url.endswith("infra/vlanns-[anstest]-static.json") + +- name: Query all ranges + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_pool_present + state: query + pool: "{{ fake_var | default(omit) }}" + register: encap_block_query_all + +- name: Query assertions + assert: + that: + - encap_block_query_all is not changed + - encap_block_query_all.current | length > 1 + - encap_block_query_all.current.0.fvnsEncapBlk is defined + - encap_block_query_all.url.endswith("class/fvnsEncapBlk.json") + +- name: Delete vlan pool range - deletion works + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_present + state: absent + register: delete_range + +- name: Absent assertions + assert: + that: + - delete_range is changed + - delete_range.proposed == {} + - delete_range.previous.0.fvnsEncapBlk.attributes.name == "anstest" + +- name: Delete vlan pool range - check mode works + cisco.aci.aci_vlan_pool_encap_block: &aci_encap_block_absent + <<: *aci_encap_block_present_2 + state: absent + check_mode: yes + register: delete_check_mode + +- name: Absent assertions + assert: + that: + - delete_check_mode is changed + - delete_check_mode.previous != [] + +- name: Delete vlan pool range - deletion works + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_absent + register: delete_encap_block_2 + +- name: Absent assertions + assert: + that: + - delete_encap_block_2 is changed + - delete_encap_block_2.previous == delete_check_mode.previous + +- name: Delete vlan pool range again - idempotency works + cisco.aci.aci_vlan_pool_encap_block: + <<: *aci_encap_block_absent + register: delete_idempotent + +- name: Absent assertions + assert: + that: + - delete_idempotent is not changed + - delete_idempotent.previous == [] + +- name: Cleanup vlan pool + cisco.aci.aci_vlan_pool: + <<: *aci_pool_present + state: absent + when: pool_present is changed diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vmm_credential/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vmm_credential/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vmm_credential/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vmm_credential/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vmm_credential/tasks/main.yml new file mode 100644 index 00000000..b76c4563 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vmm_credential/tasks/main.yml @@ -0,0 +1,12 @@ +# Test code for the ACI modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- include_tasks: vmware.yml + when: vmware is not defined or vmware
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vmm_credential/tasks/vmware.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vmm_credential/tasks/vmware.yml new file mode 100644 index 00000000..c6029acd --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vmm_credential/tasks/vmware.yml @@ -0,0 +1,250 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Remove VMM domain +- name: Remove VMM domain (normal mode) + cisco.aci.aci_domain: &domain_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: vmm_dom + domain_type: vmm + vm_provider: vmware + state: absent + register: nm_remove_domain + +# ADD VMM domain for testing +- name: Add VMM domain (normal mode) + cisco.aci.aci_domain: &domain_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: vmm_dom + domain_type: vmm + vm_provider: vmware + state: present + register: nm_add_domain + +- name: Verify add_domain + assert: + that: + - nm_add_domain is changed + +# REMOVE credential +- name: Remove credential (check mode) + cisco.aci.aci_vmm_credential: &credential_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + name: vmm_cred + description: my_new_cred + domain: vmm_dom + credential_username: myUsername + credential_password: mySecretPassword + vm_provider: vmware + state: absent + check_mode: yes + register: cm_remove_credential + +- name: Remove vmware VMM credential (normal mode) + cisco.aci.aci_vmm_credential: *credential_absent + register: nm_remove_credential + +- name: Verify remove_credential + assert: + that: + - cm_remove_credential is not changed + - nm_remove_credential is not changed + +# ADD credential +- name: Add vmware VMM credential (check mode) + cisco.aci.aci_vmm_credential: &credential_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + name: vmm_cred + description: my_new_cred + domain: vmm_dom + credential_username: myUsername + credential_password: mySecretPassword + vm_provider: vmware + state: present + check_mode: yes + register: cm_add_credential + +# NOTE: Setting password is not idempotent +- name: Add vmware VMM credential (normal mode) + cisco.aci.aci_vmm_credential: *credential_present + register: nm_add_credential + +# NOTE: Setting password is not idempotent +- name: Add vmware VMM credential again (check mode) + cisco.aci.aci_vmm_credential: *credential_present + check_mode: yes + register: cm_add_credential_again + +- name: Verify add_credential + assert: + that: + - cm_add_credential is changed + - nm_add_credential is changed + - cm_add_credential.sent.vmmUsrAccP.attributes.descr == nm_add_credential.sent.vmmUsrAccP.attributes.descr == 'my_new_cred' + - cm_add_credential.sent.vmmUsrAccP.attributes.name == nm_add_credential.sent.vmmUsrAccP.attributes.name == 'vmm_cred' + - cm_add_credential.sent.vmmUsrAccP.attributes.pwd == nm_add_credential.sent.vmmUsrAccP.attributes.pwd == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER' + - cm_add_credential.sent.vmmUsrAccP.attributes.usr == nm_add_credential.sent.vmmUsrAccP.attributes.usr == 'myUsername' + - cm_add_credential.proposed.vmmUsrAccP.attributes.descr == nm_add_credential.proposed.vmmUsrAccP.attributes.descr == 'my_new_cred' + - cm_add_credential.proposed.vmmUsrAccP.attributes.name == nm_add_credential.proposed.vmmUsrAccP.attributes.name == 'vmm_cred' + - cm_add_credential.proposed.vmmUsrAccP.attributes.pwd == nm_add_credential.proposed.vmmUsrAccP.attributes.pwd == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER' + - cm_add_credential.proposed.vmmUsrAccP.attributes.usr == nm_add_credential.proposed.vmmUsrAccP.attributes.usr == 'myUsername' + - cm_add_credential.current == cm_add_credential.previous == nm_add_credential.previous == [] + - nm_add_credential.current.0.vmmUsrAccP.attributes.dn == 'uni/vmmp-VMware/dom-vmm_dom/usracc-vmm_cred' + - nm_add_credential.current.0.vmmUsrAccP.attributes.name == 'vmm_cred' + +# MODIFY credential +- name: Modify vmware VMM credential (check mode) + cisco.aci.aci_vmm_credential: &credential_mod + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + name: vmm_cred + description: my_updated_descr + domain: vmm_dom + credential_username: myNewUsername + credential_password: myNewSecretPassword + vm_provider: vmware + state: present + check_mode: yes + register: cm_mod_credential + +- name: Modify vmware VMM credential (normal mode) + cisco.aci.aci_vmm_credential: *credential_mod + register: nm_mod_credential + +- name: Verify mod_credential + assert: + that: + - cm_mod_credential is changed + - nm_mod_credential is changed + - cm_mod_credential.sent.vmmUsrAccP.attributes.descr == nm_mod_credential.sent.vmmUsrAccP.attributes.descr == 'my_updated_descr' + - cm_mod_credential.sent.vmmUsrAccP.attributes.pwd == nm_mod_credential.sent.vmmUsrAccP.attributes.pwd == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER' + - cm_mod_credential.sent.vmmUsrAccP.attributes.usr == nm_mod_credential.sent.vmmUsrAccP.attributes.usr == 'myNewUsername' + - cm_mod_credential.proposed.vmmUsrAccP.attributes.descr == nm_mod_credential.proposed.vmmUsrAccP.attributes.descr == 'my_updated_descr' + - cm_mod_credential.proposed.vmmUsrAccP.attributes.name == nm_mod_credential.proposed.vmmUsrAccP.attributes.name == 'vmm_cred' + - cm_mod_credential.proposed.vmmUsrAccP.attributes.pwd == nm_mod_credential.proposed.vmmUsrAccP.attributes.pwd == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER' + - cm_mod_credential.proposed.vmmUsrAccP.attributes.usr == nm_mod_credential.proposed.vmmUsrAccP.attributes.usr == 'myNewUsername' + - nm_mod_credential.current.0.vmmUsrAccP.attributes.dn == 'uni/vmmp-VMware/dom-vmm_dom/usracc-vmm_cred' + - nm_mod_credential.current.0.vmmUsrAccP.attributes.name == 'vmm_cred' + +- name: Query existing vmware VMM credential (check mode) + cisco.aci.aci_vmm_credential: &query_existing_cred + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + name: vmm_cred + domain: vmm_dom + vm_provider: vmware + state: query + check_mode: yes + register: cm_query_credential + +- name: Query existing vmware VMM credential (normal mode) + cisco.aci.aci_vmm_credential: *query_existing_cred + register: nm_query_credential + +- name: Query non-existent vmware VMM credential + cisco.aci.aci_vmm_credential: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + name: vmm_fake_cred + domain: vmm_dom + vm_provider: vmware + state: query + register: nm_query_fake_credential + +- name: Query all vmware VMM credentials (check mode) + cisco.aci.aci_vmm_credential: &query_all_creds + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + vm_provider: vmware + state: query + check_mode: yes + register: cm_query_all_credential + +- name: Query all vmware VMM credentials (normal mode) + cisco.aci.aci_vmm_credential: *query_all_creds + register: nm_query_all_credential + +- name: Verify query_credential + assert: + that: + - cm_query_credential is not changed + - nm_query_credential is not changed + - nm_query_fake_credential is not changed + - cm_query_all_credential is not changed + - nm_query_all_credential is not changed + - cm_query_credential.current.0.vmmUsrAccP.attributes.name == 'vmm_cred' + - nm_query_credential.current.0.vmmUsrAccP.attributes.name == 'vmm_cred' + - nm_query_fake_credential.current == [] + - cm_query_all_credential.current.0.vmmUsrAccP.attributes.name == 'vmm_cred' + - nm_query_all_credential.current.0.vmmUsrAccP.attributes.name == 'vmm_cred' + +- name: Remove credential (check_mode) + cisco.aci.aci_vmm_credential: *credential_absent + check_mode: yes + register: cm_remove_credential_again + +- name: Remove credential (normal_mode) + cisco.aci.aci_vmm_credential: *credential_absent + register: nm_remove_credential_again + +- name: Remove credential (normal_mode) + cisco.aci.aci_vmm_credential: *credential_absent + register: nm_remove_credential_final + +- name: Verify remove_credential + assert: + that: + - cm_remove_credential_again is changed + - nm_remove_credential_again is changed + - nm_remove_credential_final is not changed + +# Remove VMM domain after testing +- name: Remove VMM domain (normal_mode) + cisco.aci.aci_domain: *domain_absent + register: nm_remove_domain_again
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vrf/aliases b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vrf/aliases new file mode 100644 index 00000000..209b793f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vrf/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vrf/tasks/main.yml b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vrf/tasks/main.yml new file mode 100644 index 00000000..838bb8c7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/integration/targets/aci_vrf/tasks/main.yml @@ -0,0 +1,191 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Jacob McGill (@jmcgill298) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: ensure tenant exists for tests to kick off + cisco.aci.aci_tenant: &aci_tenant_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + state: present + tenant: anstest + register: tenant_present + +- name: ensure vrf does not exist yet for tests to kick off + cisco.aci.aci_vrf: + <<: *aci_tenant_present + vrf: "{{ item }}" + state: absent + with_items: + - anstest + - anstest2 + +- name: create vrf - check mode works + cisco.aci.aci_vrf: &aci_vrf_present + <<: *aci_tenant_present + vrf: anstest + description: Ansible Test + check_mode: yes + register: vrf_present_check_mode + +- name: create vrf - creation works + cisco.aci.aci_vrf: + <<: *aci_vrf_present + register: vrf_present + +- name: create vrf again - idempotency works + cisco.aci.aci_vrf: + <<: *aci_vrf_present + register: vrf_present_idempotent + +- name: update vrf - update works + cisco.aci.aci_vrf: + <<: *aci_vrf_present + description: Ansible Test Update + policy_control_preference: unenforced + register: vrf_update + +- name: create another vrf - check more params + cisco.aci.aci_vrf: + <<: *aci_vrf_present + vrf: anstest2 + policy_control_direction: egress + preferred_group: enabled + match_type: all + register: vrf_present_2 + +- name: create vrf without all necessary params - failure message works + cisco.aci.aci_vrf: + <<: *aci_vrf_present + tenant: "{{ fake_var | default(omit) }}" + ignore_errors: yes + register: vrf_present_missing_param + +- name: present asserts + assert: + that: + - vrf_present_check_mode is changed + - vrf_present_check_mode.sent.fvCtx.attributes.descr == 'Ansible Test' + - vrf_present_check_mode.sent.fvCtx.attributes.name == 'anstest' + - vrf_present is changed + - vrf_present.sent == vrf_present_check_mode.sent + - vrf_present.previous == [] + - vrf_present_idempotent is not changed + - vrf_present_idempotent.previous != [] + - vrf_update is changed + - vrf_update.previous != [] + - vrf_update.sent != vrf_update.proposed + - vrf_update.sent.fvCtx.attributes.descr == 'Ansible Test Update' + - vrf_update.sent.fvCtx.attributes.pcEnfPref == 'unenforced' + - vrf_present_2.sent.fvCtx.attributes.name == 'anstest2' + - vrf_present_2.sent.fvCtx.attributes.pcEnfDir == 'egress' + - vrf_present_2.sent.fvCtx.attributes.descr == 'Ansible Test' + - vrf_present_2.current.0.fvCtx.children.0.vzAny.attributes.matchT == 'All' + - vrf_present_2.current.0.fvCtx.children.0.vzAny.attributes.prefGrMemb == 'enabled' + - vrf_present_missing_param is failed + - vrf_present_missing_param.msg == 'state is present but all of the following are missing{{":"}} tenant' + +- name: get all vrf + cisco.aci.aci_vrf: &aci_query + <<: *aci_tenant_present + state: query + tenant: "{{ fake_var | default(omit) }}" + register: query_all + +- name: get all in tenant + cisco.aci.aci_vrf: + <<: *aci_query + tenant: anstest + register: query_tenant + +- name: get all with name + cisco.aci.aci_vrf: + <<: *aci_query + vrf: anstest + register: query_vrf_vrf + +- name: get vrf + cisco.aci.aci_vrf: + <<: *aci_vrf_present + state: query + register: query_vrf + +- name: query asserts + assert: + that: + - query_all is not changed + - query_all.current | length > 1 + - query_all.current.0.fvCtx is defined + - '"class/fvCtx.json" in query_all.url' + - query_tenant is not changed + - query_tenant.current | length == 1 + - query_tenant.current.0.fvTenant.children | length == 2 + - query_tenant.current.0.fvTenant.attributes.name == "anstest" + - '"rsp-subtree-class=fvCtx" in query_tenant.filter_string' + - '"tn-anstest.json" in query_tenant.url' + - query_vrf_vrf is not changed + - query_vrf_vrf.current != [] + - query_vrf_vrf.current.0.fvCtx.attributes.name == "anstest" + - '"query-target-filter=eq(fvCtx.name,\"anstest\")" in query_vrf_vrf.filter_string' + - '"class/fvCtx.json" in query_vrf_vrf.url' + - query_vrf is not changed + - query_vrf.current | length == 1 + - '"tn-anstest/ctx-anstest.json" in query_vrf.url' + +- name: delete vrf - check mode works + cisco.aci.aci_vrf: &aci_vrf_absent + <<: *aci_vrf_present + state: absent + check_mode: yes + register: vrf_absent_check_mode + +- name: delete vrf - delete works + cisco.aci.aci_vrf: + <<: *aci_vrf_absent + register: vrf_absent + +- name: delete vrf again - idempotency works + cisco.aci.aci_vrf: + <<: *aci_vrf_absent + register: vrf_absent_idempotent + +- name: delete vrf - cleanup + cisco.aci.aci_vrf: + <<: *aci_vrf_absent + name: anstest2 + +- name: delete vrf missing param - fails properly + cisco.aci.aci_vrf: + <<: *aci_vrf_absent + vrf: "{{ fakevar | default(omit) }}" + ignore_errors: yes + register: vrf_absent_missing_param + +- name: asserts for deletion task + assert: + that: + - vrf_absent_check_mode is changed + - vrf_absent_check_mode.previous != [] + - vrf_absent_check_mode.proposed == {} + - vrf_absent is changed + - vrf_absent.previous == vrf_absent_check_mode.previous + - vrf_absent_idempotent is not changed + - vrf_absent_idempotent.previous == [] + - vrf_absent_missing_param is failed + - 'vrf_absent_missing_param.msg == "state is absent but all of the following are missing: vrf"' + +- name: delete tenant - cleanup before ending tests + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent + when: tenant_present is changed
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/sanity/ignore-2.10.txt b/collections-debian-merged/ansible_collections/cisco/aci/tests/sanity/ignore-2.10.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/sanity/ignore-2.10.txt diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/sanity/ignore-2.9.txt b/collections-debian-merged/ansible_collections/cisco/aci/tests/sanity/ignore-2.9.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/sanity/ignore-2.9.txt diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/sanity/requirements.txt b/collections-debian-merged/ansible_collections/cisco/aci/tests/sanity/requirements.txt new file mode 100644 index 00000000..3e3a9669 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/sanity/requirements.txt @@ -0,0 +1,4 @@ +packaging # needed for update-bundled and changelog +sphinx ; python_version >= '3.5' # docs build requires python 3+ +sphinx-notfound-page ; python_version >= '3.5' # docs build requires python 3+ +straight.plugin ; python_version >= '3.5' # needed for hacking/build-ansible.py which will host changelog generation and requires python 3+ diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/__init__.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/compat/__init__.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/compat/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/compat/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/compat/builtins.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/compat/builtins.py new file mode 100644 index 00000000..f60ee678 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/compat/builtins.py @@ -0,0 +1,33 @@ +# (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +# +# Compat for python2.7 +# + +# One unittest needs to import builtins via __import__() so we need to have +# the string that represents it +try: + import __builtin__ +except ImportError: + BUILTINS = 'builtins' +else: + BUILTINS = '__builtin__' diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/compat/mock.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/compat/mock.py new file mode 100644 index 00000000..0972cd2e --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/compat/mock.py @@ -0,0 +1,122 @@ +# (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +''' +Compat module for Python3.x's unittest.mock module +''' +import sys + +# Python 2.7 + +# Note: Could use the pypi mock library on python3.x as well as python2.x. It +# is the same as the python3 stdlib mock library + +try: + # Allow wildcard import because we really do want to import all of mock's + # symbols into this compat shim + # pylint: disable=wildcard-import,unused-wildcard-import + from unittest.mock import * +except ImportError: + # Python 2 + # pylint: disable=wildcard-import,unused-wildcard-import + try: + from mock import * + except ImportError: + print('You need the mock library installed on python2.x to run tests') + + +# Prior to 3.4.4, mock_open cannot handle binary read_data +if sys.version_info >= (3,) and sys.version_info < (3, 4, 4): + file_spec = None + + def _iterate_read_data(read_data): + # Helper for mock_open: + # Retrieve lines from read_data via a generator so that separate calls to + # readline, read, and readlines are properly interleaved + sep = b'\n' if isinstance(read_data, bytes) else '\n' + data_as_list = [l + sep for l in read_data.split(sep)] + + if data_as_list[-1] == sep: + # If the last line ended in a newline, the list comprehension will have an + # extra entry that's just a newline. Remove this. + data_as_list = data_as_list[:-1] + else: + # If there wasn't an extra newline by itself, then the file being + # emulated doesn't have a newline to end the last line remove the + # newline that our naive format() added + data_as_list[-1] = data_as_list[-1][:-1] + + for line in data_as_list: + yield line + + def mock_open(mock=None, read_data=''): + """ + A helper function to create a mock to replace the use of `open`. It works + for `open` called directly or used as a context manager. + + The `mock` argument is the mock object to configure. If `None` (the + default) then a `MagicMock` will be created for you, with the API limited + to methods or attributes available on standard file handles. + + `read_data` is a string for the `read` methoddline`, and `readlines` of the + file handle to return. This is an empty string by default. + """ + def _readlines_side_effect(*args, **kwargs): + if handle.readlines.return_value is not None: + return handle.readlines.return_value + return list(_data) + + def _read_side_effect(*args, **kwargs): + if handle.read.return_value is not None: + return handle.read.return_value + return type(read_data)().join(_data) + + def _readline_side_effect(): + if handle.readline.return_value is not None: + while True: + yield handle.readline.return_value + for line in _data: + yield line + + global file_spec + if file_spec is None: + import _io + file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) + + if mock is None: + mock = MagicMock(name='open', spec=open) + + handle = MagicMock(spec=file_spec) + handle.__enter__.return_value = handle + + _data = _iterate_read_data(read_data) + + handle.write.return_value = None + handle.read.return_value = None + handle.readline.return_value = None + handle.readlines.return_value = None + + handle.read.side_effect = _read_side_effect + handle.readline.side_effect = _readline_side_effect() + handle.readlines.side_effect = _readlines_side_effect + + mock.return_value = handle + return mock diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/compat/unittest.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/compat/unittest.py new file mode 100644 index 00000000..98f08ad6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/compat/unittest.py @@ -0,0 +1,38 @@ +# (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +''' +Compat module for Python2.7's unittest module +''' + +import sys + +# Allow wildcard import because we really do want to import all of +# unittests's symbols into this compat shim +# pylint: disable=wildcard-import,unused-wildcard-import +if sys.version_info < (2, 7): + try: + # Need unittest2 on python2.6 + from unittest2 import * + except ImportError: + print('You need unittest2 installed on python2.6.x to run tests') +else: + from unittest import * diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/__init__.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/loader.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/loader.py new file mode 100644 index 00000000..0ee47fbb --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/loader.py @@ -0,0 +1,116 @@ +# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os + +from ansible.errors import AnsibleParserError +from ansible.parsing.dataloader import DataLoader +from ansible.module_utils._text import to_bytes, to_text + + +class DictDataLoader(DataLoader): + + def __init__(self, file_mapping=None): + file_mapping = {} if file_mapping is None else file_mapping + assert type(file_mapping) == dict + + super(DictDataLoader, self).__init__() + + self._file_mapping = file_mapping + self._build_known_directories() + self._vault_secrets = None + + def load_from_file(self, path, cache=True, unsafe=False): + path = to_text(path) + if path in self._file_mapping: + return self.load(self._file_mapping[path], path) + return None + + # TODO: the real _get_file_contents returns a bytestring, so we actually convert the + # unicode/text it's created with to utf-8 + def _get_file_contents(self, path): + path = to_text(path) + if path in self._file_mapping: + return (to_bytes(self._file_mapping[path]), False) + else: + raise AnsibleParserError("file not found: %s" % path) + + def path_exists(self, path): + path = to_text(path) + return path in self._file_mapping or path in self._known_directories + + def is_file(self, path): + path = to_text(path) + return path in self._file_mapping + + def is_directory(self, path): + path = to_text(path) + return path in self._known_directories + + def list_directory(self, path): + ret = [] + path = to_text(path) + for x in (list(self._file_mapping.keys()) + self._known_directories): + if x.startswith(path): + if os.path.dirname(x) == path: + ret.append(os.path.basename(x)) + return ret + + def is_executable(self, path): + # FIXME: figure out a way to make paths return true for this + return False + + def _add_known_directory(self, directory): + if directory not in self._known_directories: + self._known_directories.append(directory) + + def _build_known_directories(self): + self._known_directories = [] + for path in self._file_mapping: + dirname = os.path.dirname(path) + while dirname not in ('/', ''): + self._add_known_directory(dirname) + dirname = os.path.dirname(dirname) + + def push(self, path, content): + rebuild_dirs = False + if path not in self._file_mapping: + rebuild_dirs = True + + self._file_mapping[path] = content + + if rebuild_dirs: + self._build_known_directories() + + def pop(self, path): + if path in self._file_mapping: + del self._file_mapping[path] + self._build_known_directories() + + def clear(self): + self._file_mapping = dict() + self._known_directories = [] + + def get_basedir(self): + return os.getcwd() + + def set_vault_secrets(self, vault_secrets): + self._vault_secrets = vault_secrets diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/path.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/path.py new file mode 100644 index 00000000..044585fe --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/path.py @@ -0,0 +1,8 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.cisco.aci.tests.unit.compat.mock import MagicMock +from ansible.utils.path import unfrackpath + + +mock_unfrackpath_noop = MagicMock(spec_set=unfrackpath, side_effect=lambda x, *args, **kwargs: x) diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/procenv.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/procenv.py new file mode 100644 index 00000000..f5a5891f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/procenv.py @@ -0,0 +1,90 @@ +# (c) 2016, Matt Davis <mdavis@ansible.com> +# (c) 2016, Toshio Kuratomi <tkuratomi@ansible.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import sys +import json + +from contextlib import contextmanager +from io import BytesIO, StringIO +from ansible_collections.cisco.aci.tests.unit.compat import unittest +from ansible.module_utils.six import PY3 +from ansible.module_utils._text import to_bytes + + +@contextmanager +def swap_stdin_and_argv(stdin_data='', argv_data=tuple()): + """ + context manager that temporarily masks the test runner's values for stdin and argv + """ + real_stdin = sys.stdin + real_argv = sys.argv + + 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 + sys.argv = argv_data + + yield + finally: + sys.stdin = real_stdin + sys.argv = real_argv + + +@contextmanager +def swap_stdout(): + """ + context manager that temporarily replaces stdout for tests that need to verify output + """ + old_stdout = sys.stdout + + if PY3: + fake_stream = StringIO() + else: + fake_stream = BytesIO() + + try: + sys.stdout = fake_stream + + yield fake_stream + finally: + sys.stdout = old_stdout + + +class ModuleTestCase(unittest.TestCase): + def setUp(self, module_args=None): + if module_args is None: + module_args = {'_ansible_remote_tmp': '/tmp', '_ansible_keep_remote_files': False} + + args = json.dumps(dict(ANSIBLE_MODULE_ARGS=module_args)) + + # unittest doesn't have a clean place to use a context manager, so we have to enter/exit manually + self.stdin_swap = swap_stdin_and_argv(stdin_data=args) + self.stdin_swap.__enter__() + + def tearDown(self): + # unittest doesn't have a clean place to use a context manager, so we have to enter/exit manually + self.stdin_swap.__exit__(None, None, None) diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/vault_helper.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/vault_helper.py new file mode 100644 index 00000000..dcce9c78 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/vault_helper.py @@ -0,0 +1,39 @@ +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.module_utils._text import to_bytes + +from ansible.parsing.vault import VaultSecret + + +class TextVaultSecret(VaultSecret): + '''A secret piece of text. ie, a password. Tracks text encoding. + + The text encoding of the text may not be the default text encoding so + we keep track of the encoding so we encode it to the same bytes.''' + + def __init__(self, text, encoding=None, errors=None, _bytes=None): + super(TextVaultSecret, self).__init__() + self.text = text + self.encoding = encoding or 'utf-8' + self._bytes = _bytes + self.errors = errors or 'strict' + + @property + def bytes(self): + '''The text encoded with encoding, unless we specifically set _bytes.''' + return self._bytes or to_bytes(self.text, encoding=self.encoding, errors=self.errors) diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/yaml_helper.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/yaml_helper.py new file mode 100644 index 00000000..1ef17215 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/mock/yaml_helper.py @@ -0,0 +1,124 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import io +import yaml + +from ansible.module_utils.six import PY3 +from ansible.parsing.yaml.loader import AnsibleLoader +from ansible.parsing.yaml.dumper import AnsibleDumper + + +class YamlTestUtils(object): + """Mixin class to combine with a unittest.TestCase subclass.""" + def _loader(self, stream): + """Vault related tests will want to override this. + + Vault cases should setup a AnsibleLoader that has the vault password.""" + return AnsibleLoader(stream) + + def _dump_stream(self, obj, stream, dumper=None): + """Dump to a py2-unicode or py3-string stream.""" + 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""" + 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' + # obj to yaml string + string_from_object_dump = self._dump_string(obj, dumper=AnsibleDumper) + + # wrap a stream/file like StringIO around that yaml + stream_from_object_dump = io.StringIO(string_from_object_dump) + loader = self._loader(stream_from_object_dump) + # load the yaml stream to create a new instance of the object (gen 2) + obj_2 = loader.get_data() + + # dump the gen 2 objects directory to strings + string_from_object_dump_2 = self._dump_string(obj_2, + dumper=AnsibleDumper) + + # The gen 1 and gen 2 yaml strings + self.assertEqual(string_from_object_dump, string_from_object_dump_2) + # the gen 1 (orig) and gen 2 py object + self.assertEqual(obj, obj_2) + + # again! gen 3... load strings into py objects + stream_3 = io.StringIO(string_from_object_dump_2) + loader_3 = self._loader(stream_3) + obj_3 = loader_3.get_data() + + string_from_object_dump_3 = self._dump_string(obj_3, dumper=AnsibleDumper) + + self.assertEqual(obj, obj_3) + # should be transitive, but... + self.assertEqual(obj_2, obj_3) + self.assertEqual(string_from_object_dump, string_from_object_dump_3) + + def _old_dump_load_cycle(self, obj): + '''Dump the passed in object to yaml, load it back up, dump again, compare.''' + stream = io.StringIO() + + yaml_string = self._dump_string(obj, dumper=AnsibleDumper) + self._dump_stream(obj, stream, dumper=AnsibleDumper) + + yaml_string_from_stream = stream.getvalue() + + # reset stream + stream.seek(0) + + loader = self._loader(stream) + # loader = AnsibleLoader(stream, vault_password=self.vault_password) + obj_from_stream = loader.get_data() + + stream_from_string = io.StringIO(yaml_string) + loader2 = self._loader(stream_from_string) + # loader2 = AnsibleLoader(stream_from_string, vault_password=self.vault_password) + obj_from_string = loader2.get_data() + + stream_obj_from_stream = io.StringIO() + stream_obj_from_string = io.StringIO() + + 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() + + stream_obj_from_stream.seek(0) + stream_obj_from_string.seek(0) + + 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 + assert (yaml_string == yaml_string_obj_from_stream == yaml_string_obj_from_string == yaml_string_stream_obj_from_stream == + yaml_string_stream_obj_from_string) + assert obj == obj_from_stream + assert obj == obj_from_string + assert obj == yaml_string_obj_from_stream + assert obj == yaml_string_obj_from_string + assert obj == obj_from_stream == obj_from_string == yaml_string_obj_from_stream == yaml_string_obj_from_string + return {'obj': obj, + 'yaml_string': yaml_string, + 'yaml_string_from_stream': yaml_string_from_stream, + 'obj_from_stream': obj_from_stream, + 'obj_from_string': obj_from_string, + 'yaml_string_obj_from_string': yaml_string_obj_from_string} diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/module_utils/__init__.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/module_utils/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/module_utils/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/module_utils/conftest.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/module_utils/conftest.py new file mode 100644 index 00000000..fd6c443d --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/module_utils/conftest.py @@ -0,0 +1,74 @@ +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ +# see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json +import sys +from io import BytesIO + +import pytest + +import ansible.module_utils.basic +from ansible.module_utils.six import PY3, string_types +from ansible.module_utils._text import to_bytes +from ansible.module_utils.common._collections_compat import MutableMapping + + +@pytest.fixture +def stdin(mocker, request): + old_args = ansible.module_utils.basic._ANSIBLE_ARGS + ansible.module_utils.basic._ANSIBLE_ARGS = None + old_argv = sys.argv + sys.argv = ['ansible_unittest'] + + if isinstance(request.param, string_types): + args = request.param + elif isinstance(request.param, MutableMapping): + if 'ANSIBLE_MODULE_ARGS' not in request.param: + request.param = {'ANSIBLE_MODULE_ARGS': request.param} + if '_ansible_remote_tmp' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_keep_remote_files'] = False + args = json.dumps(request.param) + else: + raise Exception('Malformed data to the stdin pytest fixture') + + fake_stdin = BytesIO(to_bytes(args, errors='surrogate_or_strict')) + if PY3: + mocker.patch('ansible.module_utils.basic.sys.stdin', + mocker.MagicMock()) + mocker.patch('ansible.module_utils.basic.sys.stdin.buffer', fake_stdin) + else: + mocker.patch('ansible.module_utils.basic.sys.stdin', fake_stdin) + + yield fake_stdin + + ansible.module_utils.basic._ANSIBLE_ARGS = old_args + sys.argv = old_argv + + +@pytest.fixture +def am(stdin, request): + old_args = ansible.module_utils.basic._ANSIBLE_ARGS + ansible.module_utils.basic._ANSIBLE_ARGS = None + old_argv = sys.argv + sys.argv = ['ansible_unittest'] + + argspec = {} + if hasattr(request, 'param'): + if isinstance(request.param, dict): + argspec = request.param + + am = ansible.module_utils.basic.AnsibleModule( + argument_spec=argspec, + ) + am._name = 'ansible_unittest' + + yield am + + ansible.module_utils.basic._ANSIBLE_ARGS = old_args + sys.argv = old_argv diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/module_utils/test_aci.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/module_utils/test_aci.py new file mode 100644 index 00000000..dd181115 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/module_utils/test_aci.py @@ -0,0 +1,386 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com> +# GNU General Public License v3.0+ +# see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import sys + +from ansible_collections.cisco.aci.tests.unit.compat import unittest +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule +from ansible.module_utils.six import PY2 +from ansible.module_utils._text import to_native + +import pytest + + +class AltModule(): + params = dict( + hostname='dummy', + port=123, + protocol='https', + state='present', + ) + + +class AltACIModule(ACIModule): + def __init__(self): + self.result = dict(changed=False) + self.module = AltModule + self.params = self.module.params + + +aci = AltACIModule() + + +try: + from lxml import etree + if sys.version_info >= (2, 7): + from xmljson import cobra +except ImportError: + pytestmark = pytest.mark.skip( + "ACI Ansible modules require the lxml and xmljson Python libraries" + ) + + +class AciRest(unittest.TestCase): + + def test_invalid_aci_login(self): + self.maxDiff = None + + error = dict( + code='401', + text=( + 'Username or password is incorrect - ' + 'FAILED local authentication' + ), + ) + + imdata = [{ + 'error': { + 'attributes': { + 'code': '401', + 'text': ( + 'Username or password is incorrect - ' + 'FAILED local authentication' + ), + }, + }, + }] + + totalCount = 1 + + json_response = '{"totalCount":"1","imdata":[{"error":{"attributes":{"code":"401","text":"Username or password is incorrect - FAILED local authentication"}}}]}' # NOQA + aci.response_json(json_response) + self.assertEqual(aci.error, error) + self.assertEqual(aci.imdata, imdata) + self.assertEqual(aci.totalCount, totalCount) + + # Python 2.7+ is needed for xmljson + if sys.version_info < (2, 7): + return + + xml_response = '''<?xml version="1.0" encoding="UTF-8"?> + <imdata totalCount="1"> + <error + code="401" + text="Username or password is incorrect - FAILED local +authentication" + /> + </imdata> + ''' + aci.response_xml(xml_response) + self.assertEqual(aci.error, error) + self.assertEqual(aci.imdata, imdata) + self.assertEqual(aci.totalCount, totalCount) + + def test_valid_aci_login(self): + self.maxDiff = None + + imdata = [{ + 'aaaLogin': { + 'attributes': { + 'token': 'ZldYAsoO9d0FfAQM8xaEVWvQPSOYwpnqzhwpIC1r4MaToknJjlIuAt9+TvXqrZ8lWYIGPj6VnZkWiS8nJfaiaX/AyrdD35jsSxiP3zydh+849xym7ALCw/fFNsc7b5ik1HaMuSUtdrN8fmCEUy7Pq/QNpGEqkE8m7HaxAuHpmvXgtdW1bA+KKJu2zY1c/tem', # NOQA + 'siteFingerprint': 'NdxD72K/uXaUK0wn', + 'refreshTimeoutSeconds': '600', + 'maximumLifetimeSeconds': '86400', + 'guiIdleTimeoutSeconds': '1200', + 'restTimeoutSeconds': '90', + 'creationTime': '1500134817', + 'firstLoginTime': '1500134817', + 'userName': 'admin', + 'remoteUser': 'false', + 'unixUserId': '15374', + 'sessionId': 'o7hObsqNTfCmDGcZI5c4ng==', + 'lastName': '', + 'firstName': '', + 'version': '2.0(2f)', + 'buildTime': 'Sat Aug 20 23:07:07 PDT 2016', + 'node': 'topology/pod-1/node-1', + }, + 'children': [{ + 'aaaUserDomain': { + 'attributes': { + 'name': 'all', + 'rolesR': 'admin', + 'rolesW': 'admin', + }, + 'children': [{ + 'aaaReadRoles': { + 'attributes': {}, + }, + }, { + 'aaaWriteRoles': { + 'attributes': {}, + 'children': [{ + 'role': { + 'attributes': { + 'name': 'admin', + }, + }, + }], + }, + }], + }, + }, { + 'DnDomainMapEntry': { + 'attributes': { + 'dn': 'uni/tn-common', + 'readPrivileges': 'admin', + 'writePrivileges': 'admin', + }, + }, + }, { + 'DnDomainMapEntry': { + 'attributes': { + 'dn': 'uni/tn-infra', + 'readPrivileges': 'admin', + 'writePrivileges': 'admin', + }, + }, + }, { + 'DnDomainMapEntry': { + 'attributes': { + 'dn': 'uni/tn-mgmt', + 'readPrivileges': 'admin', + 'writePrivileges': 'admin', + }, + }, + }], + }, + }] + + totalCount = 1 + + json_response = '{"totalCount":"1","imdata":[{"aaaLogin":{"attributes":{"token":"ZldYAsoO9d0FfAQM8xaEVWvQPSOYwpnqzhwpIC1r4MaToknJjlIuAt9+TvXqrZ8lWYIGPj6VnZkWiS8nJfaiaX/AyrdD35jsSxiP3zydh+849xym7ALCw/fFNsc7b5ik1HaMuSUtdrN8fmCEUy7Pq/QNpGEqkE8m7HaxAuHpmvXgtdW1bA+KKJu2zY1c/tem","siteFingerprint":"NdxD72K/uXaUK0wn","refreshTimeoutSeconds":"600","maximumLifetimeSeconds":"86400","guiIdleTimeoutSeconds":"1200","restTimeoutSeconds":"90","creationTime":"1500134817","firstLoginTime":"1500134817","userName":"admin","remoteUser":"false","unixUserId":"15374","sessionId":"o7hObsqNTfCmDGcZI5c4ng==","lastName":"","firstName":"","version":"2.0(2f)","buildTime":"Sat Aug 20 23:07:07 PDT 2016","node":"topology/pod-1/node-1"},"children":[{"aaaUserDomain":{"attributes":{"name":"all","rolesR":"admin","rolesW":"admin"},"children":[{"aaaReadRoles":{"attributes":{}}},{"aaaWriteRoles":{"attributes":{},"children":[{"role":{"attributes":{"name":"admin"}}}]}}]}},{"DnDomainMapEntry":{"attributes":{"dn":"uni/tn-common","readPrivileges":"admin","writePrivileges":"admin"}}},{"DnDomainMapEntry":{"attributes":{"dn":"uni/tn-infra","readPrivileges":"admin","writePrivileges":"admin"}}},{"DnDomainMapEntry":{"attributes":{"dn":"uni/tn-mgmt","readPrivileges":"admin","writePrivileges":"admin"}}}]}}]}' # NOQA + aci.response_json(json_response) + self.assertEqual(aci.imdata, imdata) + self.assertEqual(aci.totalCount, totalCount) + + # Python 2.7+ is needed for xmljson + if sys.version_info < (2, 7): + return + + xml_response = '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1">\n<aaaLogin token="ZldYAsoO9d0FfAQM8xaEVWvQPSOYwpnqzhwpIC1r4MaToknJjlIuAt9+TvXqrZ8lWYIGPj6VnZkWiS8nJfaiaX/AyrdD35jsSxiP3zydh+849xym7ALCw/fFNsc7b5ik1HaMuSUtdrN8fmCEUy7Pq/QNpGEqkE8m7HaxAuHpmvXgtdW1bA+KKJu2zY1c/tem" siteFingerprint="NdxD72K/uXaUK0wn" refreshTimeoutSeconds="600" maximumLifetimeSeconds="86400" guiIdleTimeoutSeconds="1200" restTimeoutSeconds="90" creationTime="1500134817" firstLoginTime="1500134817" userName="admin" remoteUser="false" unixUserId="15374" sessionId="o7hObsqNTfCmDGcZI5c4ng==" lastName="" firstName="" version="2.0(2f)" buildTime="Sat Aug 20 23:07:07 PDT 2016" node="topology/pod-1/node-1">\n<aaaUserDomain name="all" rolesR="admin" rolesW="admin">\n<aaaReadRoles/>\n<aaaWriteRoles>\n<role name="admin"/>\n</aaaWriteRoles>\n</aaaUserDomain>\n<DnDomainMapEntry dn="uni/tn-common" readPrivileges="admin" writePrivileges="admin"/>\n<DnDomainMapEntry dn="uni/tn-infra" readPrivileges="admin" writePrivileges="admin"/>\n<DnDomainMapEntry dn="uni/tn-mgmt" readPrivileges="admin" writePrivileges="admin"/>\n</aaaLogin></imdata>\n''' # NOQA + aci.response_xml(xml_response) + self.assertEqual(aci.imdata, imdata) + self.assertEqual(aci.totalCount, totalCount) + + def test_invalid_input(self): + self.maxDiff = None + + error = dict( + code='401', + text=( + 'Username or password is incorrect - ' + 'FAILED local authentication' + ), + ) + + imdata = [{ + 'error': { + 'attributes': { + 'code': '401', + 'text': ( + 'Username or password is incorrect - ' + 'FAILED local authentication' + ), + }, + }, + }] + + totalCount = 1 + + json_response = '{"totalCount":"1","imdata":[{"error":{"attributes":{"code":"401","text":"Username or password is incorrect - FAILED local authentication"}}}]}' # NOQA + aci.response_json(json_response) + self.assertEqual(aci.error, error) + self.assertEqual(aci.imdata, imdata) + self.assertEqual(aci.totalCount, totalCount) + + # Python 2.7+ is needed for xmljson + if sys.version_info < (2, 7): + return + + xml_response = '''<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"> + <error + code="401" + text="Username or password is incorrect - FAILED local +authentication" + /> + </imdata> + ''' + aci.response_xml(xml_response) + self.assertEqual(aci.error, error) + self.assertEqual(aci.imdata, imdata) + self.assertEqual(aci.totalCount, totalCount) + + def test_empty_response(self): + self.maxDiffi = None + + if PY2: + error_text = ( + "Unable to parse output as JSON, see 'raw' output. " + "No JSON object could be decoded" + ) + else: + error_text = ( + "Unable to parse output as JSON, see 'raw' output. " + "Expecting value: line 1 column 1 (char 0)" + ) + + error = dict( + code=-1, + text=error_text, + ) + raw = '' + + json_response = '' + aci.response_json(json_response) + self.assertEqual(aci.error, error) + self.assertEqual(aci.result['raw'], raw) + + # Python 2.7+ is needed for xmljson + if sys.version_info < (2, 7): + return + + elif etree.LXML_VERSION < (3, 3, 0, 0): + error_text = ( + "Unable to parse output as XML, see 'raw' output. " + "None" + ), + elif etree.LXML_VERSION < (4, 0, 0, 0): + error_text = to_native( + ( + u"Unable to parse output as XML, see 'raw' output. " + u"None (line 0)" + ), + errors='surrogate_or_strict' + ) + elif PY2: + error_text = ( + "Unable to parse output as XML, see 'raw' output. " + "Document is empty, line 1, column 1 (line 1)" + ) + else: + error_text = None + + xml_response = '' + aci.response_xml(xml_response) + + if error_text is None: + # errors vary on Python 3.8+ for unknown reasons + # accept any of the following error messages + errors = ( + ( + "Unable to parse output as XML, see 'raw' output. " + "None (line 0)" + ), + ( + "Unable to parse output as XML, see 'raw' output. " + "Document is empty, line 1, column 1 (<string>, line 1)" + ), + ) + + for error in errors: + if error in aci.error['text']: + error_text = error + break + + error = dict( + code=-1, + text=error_text, + ) + + raw = '' + + self.assertEqual(aci.error, error) + self.assertEqual(aci.result['raw'], raw) + + def test_invalid_response(self): + self.maxDiff = None + + if sys.version_info < (2, 7): + error_text = ( + "Unable to parse output as JSON, see 'raw' output." + " Expecting object: line 1 column 8 (char 8)" + ) + elif PY2: + error_text = ( + "Unable to parse output as JSON, see 'raw' output." + " No JSON object could be decoded" + ) + else: + error_text = ( + "Unable to parse output as JSON, see 'raw' output." + " Expecting value: line 1 column 9 (char 8)" + ) + + error = dict( + code=-1, + text=error_text, + ) + + raw = '{ "aaa":' + + json_response = '{ "aaa":' + aci.response_json(json_response) + self.assertEqual(aci.error, error) + self.assertEqual(aci.result['raw'], raw) + + # Python 2.7+ is needed for xmljson + if sys.version_info < (2, 7): + return + + elif etree.LXML_VERSION < (3, 3, 0, 0): + error_text = ( + "Unable to parse output as XML, see 'raw' output. " + "Couldn't find end of Start Tag aaa line 1, line 1, column 5" + ) + elif PY2: + error_text = ( + "Unable to parse output as XML, see 'raw' output. " + "Couldn't find end of Start Tag aaa line 1, line 1, column 6 " + "(line 1)" + ) + + else: + error_text = ( + "Unable to parse output as XML, see 'raw' output. " + "Couldn't find end of Start Tag aaa line 1, line 1, column 6 " + "(<string>, line 1)" + ) + + error = dict( + code=-1, + text=error_text, + ) + + raw = '<aaa ' + + xml_response = '<aaa ' + aci.response_xml(xml_response) + self.assertEqual(aci.error, error) + self.assertEqual(aci.result['raw'], raw) diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/modules/__init__.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/modules/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/modules/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/modules/utils.py b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/modules/utils.py new file mode 100644 index 00000000..c692792f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/modules/utils.py @@ -0,0 +1,52 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json + +from ansible_collections.cisco.aci.tests.unit.compat import unittest +from ansible_collections.cisco.aci.tests.unit.compat.mock import patch +from ansible.module_utils import basic +from ansible.module_utils._text import to_bytes + + +def set_module_args(args): + if '_ansible_remote_tmp' not in args: + args['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in args: + args['_ansible_keep_remote_files'] = False + + args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class AnsibleExitJson(Exception): + pass + + +class AnsibleFailJson(Exception): + pass + + +def exit_json(*args, **kwargs): + if 'changed' not in kwargs: + kwargs['changed'] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + kwargs['failed'] = True + raise AnsibleFailJson(kwargs) + + +class ModuleTestCase(unittest.TestCase): + + def setUp(self): + self.mock_module = patch.multiple(basic.AnsibleModule, + exit_json=exit_json, + fail_json=fail_json) + self.mock_module.start() + self.mock_sleep = patch('time.sleep') + self.mock_sleep.start() + set_module_args({}) + self.addCleanup(self.mock_module.stop) + self.addCleanup(self.mock_sleep.stop) diff --git a/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/requirements.txt b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/requirements.txt new file mode 100644 index 00000000..c27ba892 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/aci/tests/unit/requirements.txt @@ -0,0 +1,10 @@ +requests +setuptools > 0.6 # pytest-xdist installed via requirements does not work with very old setuptools (sanity_ok) +unittest2 ; python_version < '2.7' +importlib ; python_version < '2.7' +netaddr +ipaddress + +# requirement for aci_rest module +xmljson +lxml
\ No newline at end of file |