diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:52:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:52:35 +0000 |
commit | 7fec0b69a082aaeec72fee0612766aa42f6b1b4d (patch) | |
tree | efb569b86ca4da888717f5433e757145fa322e08 /ansible_collections/dellemc/enterprise_sonic | |
parent | Releasing progress-linux version 7.7.0+dfsg-3~progress7.99u1. (diff) | |
download | ansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.tar.xz ansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.zip |
Merging upstream version 9.4.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/dellemc/enterprise_sonic')
564 files changed, 68749 insertions, 3095 deletions
diff --git a/ansible_collections/dellemc/enterprise_sonic/.github/CODEOWNERS b/ansible_collections/dellemc/enterprise_sonic/.github/CODEOWNERS new file mode 100644 index 000000000..5a1377bdc --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/.github/CODEOWNERS @@ -0,0 +1,14 @@ +# CODEOWNERS +# +# documentation for this file can be found at: +# https://help.github.com/en/articles/about-code-owners + +# These are the default owners for the code and will +# be requested for review when someone opens a pull request. +# The order is alphabetical for easier maintenance. +# +# Kerry Meyer (kerry_meyer@dell.com) +# Shade Talabi (Shade_Talabi@Dell.com) + +# for all files: +* @kerry-meyer @stalabi1 diff --git a/ansible_collections/dellemc/enterprise_sonic/.github/ISSUE_TEMPLATE/ask_a_question.md b/ansible_collections/dellemc/enterprise_sonic/.github/ISSUE_TEMPLATE/ask_a_question.md new file mode 100644 index 000000000..aacce06a8 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/.github/ISSUE_TEMPLATE/ask_a_question.md @@ -0,0 +1,11 @@ +--- +name: 💬 Ask a question +about: Ask usage questions here +title: "[QUESTION]:" +labels: type/question +assignees: '' + +--- +### How can the team help? + +**Details: ?**
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/.github/ISSUE_TEMPLATE/bug_report.yml b/ansible_collections/dellemc/enterprise_sonic/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..65ce40653 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,116 @@ +--- +name: 🐛 Bug report +description: Create a report to help us improve. +title: "[BUG]: " +labels: ["type/bug", "needs-triage"] +body: + - type: markdown + attributes: + value: | + Thank you for taking the time to create this bug report. To better help us investigate this bug, provide the following information. + Verify first that your issue is not already reported on GitHub + Also test if the latest release and devel branch are affected too + Complete *all* sections as described, this form is processed automatically + - type: textarea + id: Description + attributes: + label: Bug Description + description: Provide a clear and concise summary about the bug. + validations: + required: true + - type: input + id: Product + attributes: + label: Product Name + description: Product Name + placeholder: | + Dell Enterprise SONiC / SONiC-OS etc + validations: + required: true + - type: input + id: component + attributes: + label: Component or Module Name + description: Write the short name of the module, plugin, task or feature below, use your best guess if unsure + placeholder: httpapi, cliconf, sonic_bgp, etc. + validations: + required: true + - type: input + id: ansible + attributes: + label: DellEMC Enterprise SONiC Ansible Collection Version + description: Provide the output from "ansible-galaxy collection list dellemc.enterprise_sonic" + placeholder: | + dellemc.enterprise_sonic 2.0.0 + validations: + required: true + - type: input + id: Productv + attributes: + label: SONiC Software Version + description: Provide the first line of "show version" output. + placeholder: | + SONiC Software Version + validations: + required: true + - type: textarea + id: device + attributes: + label: Configuration + description: Paste verbatim output from "ansible-config dump --only-changed" + placeholder: | + "ansible-config dump --only-changed" + validations: + required: true + - type: textarea + id: STEPS + attributes: + label: Steps to Reproduce + description: Describe exactly how to reproduce the problem, using a minimal test-case + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: Enter the clear and concise description about the expected behaviour. + validations: + required: true + - type: textarea + id: actual + attributes: + label: Actual Behavior + description: Enter the clear and concise description about the actual behaviour. + validations: + required: true + - type: textarea + id: files + attributes: + label: Logs + description: Copy any relevant log output here or drop the log files in the text area. + Please be sure to remove all sensitive data such as IP addresses, host names, credentials, etc. + validations: + required: true + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: Drop any relevant screenshots in the text area. + validations: + required: false + - type: input + id: ainfo + attributes: + label: Additional Information + description: Provide any additional information about the bug. + validations: + required: false + - type: markdown + attributes: + value: | + Community Note + * Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) + to the original issue to help the community and maintainers prioritize this request + * Please do not leave "+1" or other comments that do not add relevant new information or questions, + they generate extra noise for issue followers and do not help prioritize the request + * If you are interested in working on this issue or have submitted a pull request, please leave a comment
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/.github/ISSUE_TEMPLATE/config.yml b/ansible_collections/dellemc/enterprise_sonic/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..ec4bb386b --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/.github/ISSUE_TEMPLATE/feature_request.md b/ansible_collections/dellemc/enterprise_sonic/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..c414549ee --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: 🚀 Feature Request +about: Suggest an idea for this project +title: "[FEATURE]:" +labels: type/feature-request, needs-triage +assignees: '' +--- +**Describe the solution you'd like** +Provide a clear and concise description about the new feature request. + +**Describe alternatives you've considered** +Provide a clear and concise description of any alternative solutions or features that you have considered. + +**Additional context** +Describe any relevant context or add any relevant screenshots about the feature request. + +**Community Note** + +* Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) + to the original issue to help the community and maintainers prioritize this request +* Please do not leave "+1" or other comments that do not add relevant new information or questions, + they generate extra noise for issue followers and do not help prioritize the request +* If you are interested in working on this issue or have submitted a pull request, please leave a comment
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/.github/PULL_REQUEST_TEMPLATE.md b/ansible_collections/dellemc/enterprise_sonic/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..006a4eed2 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,53 @@ +##### SUMMARY +<!--- Describe the change below, including rationale and design decisions --> + +##### GitHub Issues +List the GitHub issues impacted by this PR. If no Github issues are affected, please indicate this with "N/A". + +| GitHub Issue # | +| -------------- | +| | + + +##### ISSUE TYPE +<!--- Pick one below and delete the rest --> +- Bugfix Pull Request +- Docs Pull Request +- Feature Pull Request +- Test Pull Request + +##### COMPONENT NAME +<!--- Write the short name of the module, plugin, task or feature below --> + +##### OUTPUT +<!--- Paste the functionality test result below --> +```paste below + +``` +##### ADDITIONAL INFORMATION +<!--- Include additional information to help people understand the change here --> +<!--- A step-by-step reproduction of the problem is helpful if there is no related issue --> + +<!--- Paste verbatim command output below, e.g. before and after your change --> +```paste below + +``` +<!--- Measure the code coverage before and after the change by running the UT and ensure that the "coverage after the change" is not less than the coverage "before the change". Note that the unit testing coverage can be manually executed using the pytest tool or ansible-test tool. --> + +##### Checklist: + +- [ ] I have performed a self-review of my own code to ensure there are no formatting, linting, or security issues +- [ ] I have verified that new and existing unit tests pass locally with my changes +- [ ] I have not allowed coverage numbers to degenerate +- [ ] I have maintained at least 90% code coverage +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] I have maintained backward compatibility or have provided any relevant "breaking_changes" descriptions in a "fragment" file in the "changelogs/fragments" directory of this repository. +- [ ] I have provided a summary for this PR in valid "fragment" file format in the "changelogs/fragments" directory of this repository branch. Reference : [Ansible Change Log Document](https://docs.ansible.com/ansible/devel/community/development_process.html#changelogs-how-to) + +##### How Has This Been Tested? +Please describe the tests that you ran to verify your changes. Please also list any relevant details for your test configuration + +- [ ] Test A +- [ ] Test B diff --git a/ansible_collections/dellemc/enterprise_sonic/.github/workflows/ansible-test.yml b/ansible_collections/dellemc/enterprise_sonic/.github/workflows/ansible-test.yml index c2ae1dd09..3a5bbd9f6 100644 --- a/ansible_collections/dellemc/enterprise_sonic/.github/workflows/ansible-test.yml +++ b/ansible_collections/dellemc/enterprise_sonic/.github/workflows/ansible-test.yml @@ -7,36 +7,10 @@ on: # This ensures that even if there haven't been commits that we are still testing against latest version of ansible-test for each ansible-base version schedule: - cron: '0 6 * * *' + jobs: + changelog: + uses: ansible-network/github_actions/.github/workflows/changelog.yml@main + if: (github.event_name != 'schedule' && github.event_name != 'push' && !contains(github.event.pull_request.labels.*.name, 'new_resource_module')) sanity: - name: Sanity (${{ matrix.ansible }}) - strategy: - matrix: - ansible: - - stable-2.9 - - stable-2.10 - - stable-2.11 - - stable-2.12 - - stable-2.13 - - devel - runs-on: ubuntu-latest - steps: - - - name: Check out code - uses: actions/checkout@v1 - with: - path: ansible_collections/dellemc/enterprise_sonic - - - name: Set up Python 3.9 - uses: actions/setup-python@v1 - with: - python-version: 3.9 - - - name: Install ansible-base (${{ matrix.ansible }}) - run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check - - - name: Install ansible_collections.ansible.netcommon - run: ansible-galaxy collection install ansible.netcommon -p ../../ - - - name: Run sanity tests - run: ansible-test sanity --docker -v --color + uses: ansible-network/github_actions/.github/workflows/sanity.yml@main diff --git a/ansible_collections/dellemc/enterprise_sonic/.github/workflows/code-coverage.yml b/ansible_collections/dellemc/enterprise_sonic/.github/workflows/code-coverage.yml new file mode 100644 index 000000000..1f23d7a6e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/.github/workflows/code-coverage.yml @@ -0,0 +1,63 @@ +name: Code coverage +on: + push: + pull_request: + +jobs: + codecoverage: + env: + PY_COLORS: "1" + source_directory: "./source" + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + ansible-version: + - stable-2.14 + python-version: + - "3.10" + runs-on: ${{ matrix.os }} + + name: "Code coverage py${{ matrix.python-version }} / ${{ matrix.os }} / ${{ matrix.ansible-version }}" + steps: + - name: Checkout the collection repository + uses: actions/checkout@v3 + with: + path: ${{ env.source_directory }} + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: "0" + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install ansible-core (${{ matrix.ansible-version }}) + run: python3 -m pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible-version }}.tar.gz --disable-pip-version-check + + - name: Read collection metadata from galaxy.yml + id: identify + uses: ansible-network/github_actions/.github/actions/identify_collection@main + with: + source_path: ${{ env.source_directory }} + + - name: Build and install the collection + uses: ansible-network/github_actions/.github/actions/build_install_collection@main + with: + install_python_dependencies: true + source_path: ${{ env.source_directory }} + collection_path: ${{ steps.identify.outputs.collection_path }} + tar_file: ${{ steps.identify.outputs.tar_file }} + + - name: Print the ansible version + run: ansible --version + + - name: Print the python dependencies + run: python3 -m pip list + + - name: Run code coverage tests + run: | + ansible-test units --python ${{ matrix.python-version }} --coverage --requirements + ansible-test coverage report + working-directory: ${{ steps.identify.outputs.collection_path }} diff --git a/ansible_collections/dellemc/enterprise_sonic/CHANGELOG.rst b/ansible_collections/dellemc/enterprise_sonic/CHANGELOG.rst new file mode 100644 index 000000000..d3ecd6f70 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/CHANGELOG.rst @@ -0,0 +1,390 @@ +====================================== +Dellemc.Enterprise_Sonic Release Notes +====================================== + +.. contents:: Topics + + +v2.4.0 +====== + +Release Summary +--------------- + +| Release Date: 2024-0108 +| This release provides an Ansible compliance change required on top of the changes included in +| the 2.3.0 release of the enterprise_sonic Ansible network resource module collection. +| It addresses an issue raised by the Ansible core team with the content of the 2.3.0 release, +| and provides accompanying documentation changes in the README file. Additional details are +| described below. +| 1) Update the "requires_ansible" version in the meta/runtime.yml file for this collection +| to the oldest supported version of ansible-core. (This was recently changed by Redhat/Ansible +| to version "2.14.0".) +| 2) Update the README file "Recommended version" values for Ansible and Python in accordance +| with the previous change item to modify the oldest supported version of ansible-core which, +| in turn, requires a Python version >= "3.9". +| 3) Update the list of resource modules in the README file to include all currently available +| resource modules for this collection. + + +Bugfixes +-------- + +- requirements - Update requires_ansible version in meta/runtime.yml to the oldest supported version (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/321). + +v2.3.0 +====== + +Release Summary +--------------- + +| Release Date: 2024-0103 +| This release provides the functionality enhancements listed below, along with fixes for +| problems found in regression testing or reported by users. The main functionality enhancements +| provided are the following items. +| 1) Complete the support for "replaced" and "overridden" state handling for all resource modules except for the bgp_neighbors and bgp_neighbors_af modules. +| With this release, the required support has been added for any resource modules that were not +| provided with this support for the 2.1.0 release with the two exceptions noted above. +| 2) Provide initial support for the "--check" and "--diff" mode options for playbook execution. This +| release provides the common utility support for these options for use by all resource modules. +| It also provides the specific resource module changes required for implementation of the +| functionality in many of the existing resource modules. (The "--check" and "--diff" mode support +| for the remaining resource modules is planned for inclusion in the next release.) +| 3) New resource modules for "Public Key Infrastructure", STP, and DHCP Snooping. +| 4) Support for "ranges" of vlans (e.g '2-100') in tasks for the mclag resource module. +| Please refer to the "CHANGELOG.rst" file at the top directory level of this repo for additional +| details on the contents of this release. + + +Minor Changes +------------- + +- sonic_aaa - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/304). +- sonic_aaa - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_acl_interfaces - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/306). +- sonic_acl_interfaces - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_bgp_as_paths - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/290). +- sonic_bgp_communities - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/251). +- sonic_bgp_ext_communities - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/252). +- sonic_interfaces - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/301). +- sonic_interfaces - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/314). +- sonic_interfaces - Change deleted design for interfaces module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/310). +- sonic_interfaces - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_ip_neighbor - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/285). +- sonic_ip_neighbor - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_l2_acls - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/306). +- sonic_l2_acls - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_l2_interfaces - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/303). +- sonic_l2_interfaces - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_l3_acls - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/306). +- sonic_l3_acls - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_l3_interfaces - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/241). +- sonic_lag_interfaces - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/303). +- sonic_lag_interfaces - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_logging - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/285). +- sonic_logging - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_mclag - Add VLAN range support for 'unique_ip' and 'peer_gateway' options (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/288). +- sonic_mclag - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/288). +- sonic_ntp - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/281). +- sonic_ntp - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_port_breakout - Add Ansible support for all port breakout modes now allowed in Enterprise SONiC (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/276). +- sonic_port_breakout - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/291). +- sonic_port_group - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/284). +- sonic_port_group - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_radius_server - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/279). +- sonic_radius_server - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_static_routes - Add playbook check and diff modes support for static routes resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/313). +- sonic_static_routes - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_system - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/284). +- sonic_system - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_tacacs_server - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/281). +- sonic_tacacs_server - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_users - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/304). +- sonic_users - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_vlans - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/301). +- sonic_vlans - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- sonic_vrfs - Add mgmt VRF replaced state handling to sonic_vrfs module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/298). +- sonic_vrfs - Add mgmt VRF support to sonic_vrfs module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/293). +- sonic_vrfs - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/285). +- sonic_vrfs - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). +- tests - Add UTs for BFD, COPP, and MAC modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/287). +- tests - Enable contiguous execution of all regression integration tests on an S5296f (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/277). +- tests - Fix the bgp CLI test base_cfg_path derivation of the bgp role_path by avoiding relative pathing from the possibly external playbook_dir (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/283). + +Bugfixes +-------- + +- sonic_bgp_communities - Fix incorrect "facts" handling for parsing of a BGP community list configured with an empty "members" list (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/319). +- sonic_bgp_neighbors - Fix prefix-limit issue (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/289). +- sonic_interfaces - Add warnings when speed and auto_negotiate is configured at same time (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/314). +- sonic_interfaces - Fix support for standard naming interfaces (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/314). +- sonic_interfaces - Prevent configuring speed in port group interfaces (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/314). +- sonic_stp - Correct the commands list for STP delete state (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/302). + +New Modules +----------- + +- dellemc.enterprise_sonic.sonic_dhcp_snooping - Manage DHCP Snooping on SONiC +- dellemc.enterprise_sonic.sonic_pki - Manages PKI attributes of Enterprise Sonic +- dellemc.enterprise_sonic.sonic_stp - Manage STP configuration on SONiC + +v2.2.0 +====== + +Release Summary +--------------- + +| Release Date: 2023-06-01 +| This release provides Ansible compliance changes required on top of the changes included in +| the 2.1.0 release of the enterprise_sonic Ansible network resource module collection. +| It addresses two issues raised by the Ansible core team with the content of the 2.1.0 release. +| 1) Back out the "breaking_change" made in the sonic_aaa resource module to fix a functional +| shortcoming in the enterprise_sonic Ansible collection. Although the change is still needed, +| it will be deferred to a "major" release. +| 2) Re-enable installation of new Ansible Netcommon repo instances when installing the +| enterprise_sonic Ansible collection. The 2.1.0 enterprise_sonic Ansible release included a +| workaround for a bug introduced in the 5.0.0 version of the Ansible Netcommon repo. This +| workaround was implemented in the "galaxy.yml" file for the enterprise_sonic +| 2.1.0 release. New versions of Ansible Netcommon were published after the problematic 5.0.0 +| version and the revised "galaxy.yml" file for this release enables installation of these +| newer versions. + + +Minor Changes +------------- + +- galaxy_yml - Enable installation of Ansible Netcomon versions after 5.0.0 and update the enterprise_sonic release version (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/270). +- sonic_aaa - Revert breaking changes for AAA nodule (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/269). + +v2.1.0 +====== + +Release Summary +--------------- + +| Release Date: 2023-0515 +| This release provides enhanced Dell Enterprise SONiC Ansible Collection support for SONiC 4.x images. +| In addition to new resource modules to support previously existing functionality, it provides +| support for the "QinQ" (Vlan Mapping) function introduced with SONiC release 4.1. It also provides +| bug fixes and enhancements for support of features that were initially introduced in previous +| Enterprise SONiC Ansible releases. The changelog describes changes made to the modules and plugins +| included in this collection since release 2.0.0. + + +Minor Changes +------------- + +- module_utils - Change the location for importing remove_empties from the obsolete Netcommon location to the offically required Ansible library location to fix sanity errors (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/172). +- sonic_aaa - Add replaced and overridden states support for AAA resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/237). +- sonic_aaa - Add unit tests for AAA resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/198). +- sonic_api - Add unit tests for api resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/218). +- sonic_bfd, sonic_copp - Update replaced methods (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/254). +- sonic_bgp - Add rt_delay attribute to module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/244). +- sonic_bgp - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/240). +- sonic_bgp - Add unit tests for BGP resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/182). +- sonic_bgp_af - Add several attributes to support configuration of route distinguisher and route target (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/141). +- sonic_bgp_af - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/246). +- sonic_bgp_af - Add unit tests for BGP AF resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/183). +- sonic_bgp_af - Modify BGP AF resource module unit tests to adjust for changes in the resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/191). +- sonic_bgp_as_paths - Add unit tests for BGP AS paths resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/184). +- sonic_bgp_communities - Add unit tests for BGP communities resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/185). +- sonic_bgp_ext_communities - Add unit tests for BGP ext communities resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/186). +- sonic_bgp_neighbors - Add unit tests for BGP neighbors resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/187). +- sonic_bgp_neighbors - Enhance unit tests for BGP Neighbors resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/245). +- sonic_bgp_neighbors_af - Add unit tests for BGP neighbors AF resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/188). +- sonic_command - Add unit tests for command resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/219). +- sonic_config - Add unit tests for config resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/220). +- sonic_dhcp_relay - Add a common unit tests module and unit tests for dhcp relay module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/148). +- sonic_dhcp_relay - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/249). +- sonic_facts - Add unit tests for facts resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/222). +- sonic_interfaces - Add speed, auto-negotiate, advertised-speed and FEC to interface resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/128). +- sonic_interfaces - Add unit tests for interfaces resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/197). +- sonic_ip_neighbor - Add unit tests for IP neighbor resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/225). +- sonic_ip_neighbor - Change the replaced function in ip_neighbor resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/253). +- sonic_l2_interfaces - Add support for parsing configuration containing the OC Yang vlan range syntax (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/124). +- sonic_l2_interfaces - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/221). +- sonic_l2_interfaces - Add support for specifying vlan trunk ranges in Ansible playbooks (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/149). +- sonic_l2_interfaces - Add unit tests for l2_interfaces resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/200). +- sonic_l3_interfaces - Add unit tests for l3_interfaces resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/202). +- sonic_lag_interface - Add replaced and overridden states support for LAG interface resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/196). +- sonic_lag_interfaces - Add unit tests for lag_interfaces resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/203). +- sonic_logging - Add replaced and overridden states support for logging resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/150). +- sonic_logging - Add unit tests for logging resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/226). +- sonic_logging - Change logging get facts for source_interface naming (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/258). +- sonic_mclag - Add delay_restore, gateway_mac, and peer_gateway attributes to module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/145). +- sonic_ntp - Add prefer attribute to NTP resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/118). +- sonic_ntp - Add replaced and overridden states support for NTP resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/151). +- sonic_ntp - Add unit tests for NTP resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/207). +- sonic_ntp - Change NTP get facts to get default parameters (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/106). +- sonic_ntp - Change NTP key values in NTP regression test script (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/107). +- sonic_ntp - Change NTP module name (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/113). +- sonic_ntp - Change NTP module names in NTP regression test script (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/114). +- sonic_ntp - Change NTP resource module to make minpoll and maxpoll be configured together (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/129). +- sonic_port_breakout - Add unit tests for port breakout resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/229). +- sonic_port_group - Add replaced and overridden states support for port group resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/227). +- sonic_port_group - Add unit tests for port group resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/228). +- sonic_prefix_lists - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/255). +- sonic_prefix_lists - Add unit tests for prefix lists resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/209). +- sonic_radius_server - Add replaced and overridden states support for RADIUS server resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/239). +- sonic_radius_server - Add unit tests for RADIUS server resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/210). +- sonic_static_routes - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/236). +- sonic_static_routes - Add unit tests for static routes resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/212). +- sonic_system - Add replaced and overridden states support for system resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/159). +- sonic_system - Add unit tests for system resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/223). +- sonic_tacacs_server - Add replaced and overridden states support for TACACS server resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/235). +- sonic_tacacs_server - Add unit tests for TACACS server resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/208). +- sonic_users - Add replaced and overridden states support for users resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/242). +- sonic_users - Add unit tests for users resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/213). +- sonic_vlans - Add replaced and overridden states support for VLAN resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/217). +- sonic_vlans - Add unit tests for Vlans resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/214). +- sonic_vrfs - Add replaced and overridden states support for VRF resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/156). +- sonic_vrfs - Add unit tests for VRFS resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/216). +- sonic_vxlans - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/247). +- sonic_vxlans - Add unit tests for VxLans resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/215). + +Breaking Changes / Porting Guide +-------------------------------- + +- sonic_aaa - Add default_auth attribute to the argspec to replace the deleted group and local attributes. This change allows for ordered login authentication. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/195). + +Bugfixes +-------- + +- Fix regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/180). +- Fix sanity check errors in the collection caused by Ansible library changes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/160). +- install - Update the required ansible.netcommon version (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/176). +- sonic_bgp_af - Fix issue with vnis and advertise modification for a single BGP AF (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/201). +- sonic_bgp_as_paths - Fix issues with merged and deleted states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/250). +- sonic_interfaces - Fix command timeout issue (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/261). +- sonic_l3_interfaces - Fix IP address deletion issue (GitHub issue#170) (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/231). +- sonic_lag_interfaces - Fix port name issue (GitHub issue#153) (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/119). +- sonic_neighbors - Fix handling of default attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/233). +- sonic_ntp - Fix the issue (GitHub issue#205) with NTP clear all without config given (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/224). +- sonic_vlan_mapping - Remove platform checks (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/262). +- sonic_vrfs - Add tasks as a workaround to mgmt VRF bug (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/146). +- sonic_vrfs - Fix spacing issue in CLI test case (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/257). +- sonic_vrfs - Fix the issue (GitHub issue#194) with VRF when deleting interface(https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/230). +- sonic_vxlans - Remove required_together restriction for evpn_nvo and source_ip attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/130). +- workflows - Fix dependency installation issue in the code coverage workflow (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/199). + +New Modules +----------- + +- dellemc.enterprise_sonic.sonic_acl_interfaces - Manage access control list (ACL) to interface binding on SONiC +- dellemc.enterprise_sonic.sonic_bfd - Manage BFD configuration on SONiC +- dellemc.enterprise_sonic.sonic_copp - Manage CoPP configuration on SONiC +- dellemc.enterprise_sonic.sonic_dhcp_relay - Manage DHCP and DHCPv6 relay configurations on SONiC +- dellemc.enterprise_sonic.sonic_ip_neighbor - Manage IP neighbor global configuration on SONiC +- dellemc.enterprise_sonic.sonic_l2_acls - Manage Layer 2 access control lists (ACL) configurations on SONiC +- dellemc.enterprise_sonic.sonic_l3_acls - Manage Layer 3 access control lists (ACL) configurations on SONiC +- dellemc.enterprise_sonic.sonic_lldp_global - Manage Global LLDP configurations on SONiC +- dellemc.enterprise_sonic.sonic_logging - Manage logging configuration on SONiC +- dellemc.enterprise_sonic.sonic_mac - Manage MAC configuration on SONiC +- dellemc.enterprise_sonic.sonic_port_group - Manages port group configuration on SONiC +- dellemc.enterprise_sonic.sonic_route_maps - route map configuration handling for SONiC +- dellemc.enterprise_sonic.sonic_vlan_mapping - Configure vlan mappings on SONiC + +v2.0.0 +====== + +Release Summary +--------------- + +This release provides Dell SONiC Enterprise Ansible Collection support for SONiC 4.x images. It is the first release for the 2.x branch of the collection. Subsequent enhancements for support of SONiC 4.x images will also be provided as needed on the 2.x branch. This release also contains bugfixes and enhancements to supplement the Ansible functionality provided previously for SONiC 3.x images. The changelog describes changes made to the modules and plugins included in this collection since release 1.1.0. + + +Minor Changes +------------- + +- Add an execution-environment.yml file to the "meta" directory to enable use of Ansible execution environment infrastructure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/88). +- bgp_af - Add support for BGP options to configure usage and advertisement of vxlan primary IP address related attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/62). +- bgp_as_paths - Update module examples with 'permit' attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102). +- bgp_neighbors - Add BGP peer group support for multiple attributes. The added attributes correspond to the same set of attributes added for BGP neighbors with PR 72 (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/81). +- bgp_neighbors - Add an auth_pwd dictionary and nbr_description attribute to the argspec (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/67). +- bgp_neighbors - Add prefix-list related peer-group attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101). +- bgp_neighbors - Add support for multiple attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/72). +- bgp_neighbors_af - Add prefix-list related neighbor attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101). +- playbook - Update examples to reflect module changes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102). +- sonic_vxlans - Add configuration capability for the primary IP address of a vxlan vtep to facilitate vxlan path redundundancy (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/58). +- vlans - Add support for the vlan "description" attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/98). +- workflow - Add stable-2.13 to the sanity test matrix (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90). + +Breaking Changes / Porting Guide +-------------------------------- + +- bgp_af - Add the route_advertise_list dictionary to the argspec to replace the deleted, obsolete advertise_prefix attribute used for SONiC 3.x images on the 1.x branch of this collection. This change corresponds to a SONiC 4.0 OC YANG REST compliance change for the BGP AF REST API. It enables specification of a route map in conjunction with each route advertisement prefix (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/63). +- bgp_af - Remove the obsolete 'advertise_prefix' attribute from argspec and config code. This and subsequent co-req replacement with the new route advertise list argument structure require corresponding changes in playbooks previoulsly used for configuring route advertise prefixes for SONiC 3.x images. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60). +- bgp_neighbors - Replace the previously defined standalone "bfd" attribute with a bfd dictionary containing multiple attributes. This change corresponds to the revised SONiC 4.x implementation of OC YANG compatible REST APIs. Playbooks previously using the bfd attributes for SONiC 3.x images must be modified for use on SONiC 4.0 images to use the new definition for the bfd attribute argspec structure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/72). +- bgp_neighbors - Replace, for BGP peer groups, the previously defined standalone "bfd" attribute with a bfd dictionary containing multiple attributes. This change corresponds to the revised SONiC 4.x implementation of OC YANG compatible REST APIs. Playbooks previously using the bfd attributes for SONiC 3.x images must be modified for use on SONiC 4.0 images to use the new definition for the bfd attribute argspec structure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/81). + +Bugfixes +-------- + +- Fixed regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/103). +- Fixed regression test sequencing and other regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/85). +- bgp_neighbors - Remove string conversion of timer attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60). +- port_breakout - Fixed a bug in formulation of port breakout REST APIs (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/88). +- sonic - Fix a bug in handling of interface names in standard interface naming mode (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/103). +- sonic_aaa - Fix a bug in facts gathering by providing required conditional branching (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90). +- sonic_aaa - Modify regression test sequencing to enable correct testing of the functionality for this module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/78). +- sonic_command - Fix bugs in handling of CLI commands involving a prompt and answer sequence (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/76/files). +- users - Fixed a bug in facts gathering (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90). +- vxlan - update Vxlan test cases to comply with SONiC behavior (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/105). + +New Modules +----------- + +- dellemc.enterprise_sonic.sonic_ntp - Manage NTP configuration on SONiC. +- dellemc.enterprise_sonic.sonic_prefix_lists - prefix list configuration handling for SONiC +- dellemc.enterprise_sonic.sonic_static_routes - Manage static routes configuration on SONiC + +v1.1.0 +====== + +New Modules +----------- + +- dellemc.enterprise_sonic.sonic_aaa - Manage AAA and its parameters +- dellemc.enterprise_sonic.sonic_radius_server - Manage RADIUS server and its parameters +- dellemc.enterprise_sonic.sonic_system - Configure system parameters +- dellemc.enterprise_sonic.sonic_tacacs_server - Manage TACACS server and its parameters + +v1.0.0 +====== + +New Plugins +----------- + +Cliconf +~~~~~~~ + +- dellemc.enterprise_sonic.sonic - Use sonic cliconf to run command on Dell OS10 platform + +Httpapi +~~~~~~~ + +- dellemc.enterprise_sonic.sonic - HttpApi Plugin for devices supporting Restconf SONIC API + +New Modules +----------- + +- dellemc.enterprise_sonic.sonic_api - Manages REST operations on devices running Enterprise SONiC +- dellemc.enterprise_sonic.sonic_bgp - Manage global BGP and its parameters +- dellemc.enterprise_sonic.sonic_bgp_af - Manage global BGP address-family and its parameters +- dellemc.enterprise_sonic.sonic_bgp_as_paths - Manage BGP autonomous system path (or as-path-list) and its parameters +- dellemc.enterprise_sonic.sonic_bgp_communities - Manage BGP community and its parameters +- dellemc.enterprise_sonic.sonic_bgp_ext_communities - Manage BGP extended community-list and its parameters +- dellemc.enterprise_sonic.sonic_bgp_neighbors - Manage a BGP neighbor and its parameters +- dellemc.enterprise_sonic.sonic_bgp_neighbors_af - Manage the BGP neighbor address-family and its parameters +- dellemc.enterprise_sonic.sonic_command - Runs commands on devices running Enterprise SONiC +- dellemc.enterprise_sonic.sonic_config - Manages configuration sections on devices running Enterprise SONiC +- dellemc.enterprise_sonic.sonic_interfaces - Configure Interface attributes on interfaces such as, Eth, LAG, VLAN, and loopback. (create a loopback interface if it does not exist.) +- dellemc.enterprise_sonic.sonic_l2_interfaces - Configure interface-to-VLAN association that is based on access or trunk mode +- dellemc.enterprise_sonic.sonic_l3_interfaces - Configure the IPv4 and IPv6 parameters on Interfaces such as, Eth, LAG, VLAN, and loopback +- dellemc.enterprise_sonic.sonic_lag_interfaces - Manage link aggregation group (LAG) interface parameters +- dellemc.enterprise_sonic.sonic_mclag - Manage multi chassis link aggregation groups domain (MCLAG) and its parameters +- dellemc.enterprise_sonic.sonic_port_breakout - Configure port breakout settings on physical interfaces +- dellemc.enterprise_sonic.sonic_users - Manage users and its parameters +- dellemc.enterprise_sonic.sonic_vlans - Manage VLAN and its parameters +- dellemc.enterprise_sonic.sonic_vrfs - Manage VRFs and associate VRFs to interfaces such as, Eth, LAG, VLAN, and loopback +- dellemc.enterprise_sonic.sonic_vxlans - Manage VxLAN EVPN and its parameters diff --git a/ansible_collections/dellemc/enterprise_sonic/FILES.json b/ansible_collections/dellemc/enterprise_sonic/FILES.json index 39a1d6342..f16263808 100644 --- a/ansible_collections/dellemc/enterprise_sonic/FILES.json +++ b/ansible_collections/dellemc/enterprise_sonic/FILES.json @@ -25,14 +25,14 @@ "name": "tests/regression/test.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "47a1db3b7c5d49f80307023f42c78c5efdf5efa0b1f507f1106b96179b64dc4c", + "chksum_sha256": "b59c33c40ac775fc7177021955d7dedd56df9564df5ab5c1792ac712e6350064", "format": 1 }, { "name": "tests/regression/hosts", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "22f4ec1024a7ff0f9d471735392d71eac3378f5c7ddc7cf1e0c42a958def8e78", + "chksum_sha256": "b449f2e1d0b054fa1a08d6bbc746a4b4abbf1f98b76c538d11188abee20a2494", "format": 1 }, { @@ -99,28 +99,147 @@ "format": 1 }, { - "name": "tests/regression/roles/sonic_bgp_neighbors/defaults/main copy.yml", + "name": "tests/regression/roles/sonic_bgp_neighbors/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8d53284cc72caddd5bc84a507bb7330af983d44c35427ac7fbe623a29aa7d47f", + "chksum_sha256": "96931ead58f28114732ef53d9e1ac7fabeaf40c3993686c5b75732305df07b12", "format": 1 }, { - "name": "tests/regression/roles/sonic_bgp_neighbors/defaults/main.yml", + "name": "tests/regression/roles/sonic_bgp_neighbors/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_bgp_neighbors/meta/main.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6b8363d6259e43af603abde938ed159d1a3a083cbef49fa37095bdc779133921", + "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd", "format": 1 }, { - "name": "tests/regression/roles/sonic_bgp_neighbors/meta", + "name": "tests/regression/roles/sonic_bfd", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/regression/roles/sonic_bgp_neighbors/meta/main.yaml", + "name": "tests/regression/roles/sonic_bfd/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_bfd/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2ba37c792a7724e9bec876a003c1951a8d25f366b2978e69c0ea1a329099874a", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_bfd/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "784facd1ffa9792649d0bf05dcc4aed644595590c628537b97459bf0b1ecea3c", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_bfd/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5128608a2f36b2073e023aabb3784b767bd90b3dd69fce9a124c61b9ebbf4ea3", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_bfd/tasks/tasks_template_del.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "df2ec9b1feb6098f03f1d0dc64ac974197b1d0b0b3a39b87a84f2be87a8f53e2", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_bfd/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_bfd/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7c3ac5d4ed7222f6485a3ac746527a4467cfbf966e65284f7ed4a3f6d2403e24", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_bfd/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_bfd/meta/main.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b16570e34d6f129bbec985a4ed0852051e0bb2a4007e8a9a73120f9a1d3ae876", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_ip_neighbor", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_ip_neighbor/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_ip_neighbor/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d130603ee0811b08d8d857272c3e80e5c5d48af76904b3cbebd595e8c46aeaa7", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_ip_neighbor/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "34113182d1947b1e123ed123acc25e7a86c20a66ee2f248f3467bd9804ccf07a", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_ip_neighbor/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_ip_neighbor/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "81d2c5ac604ced07b4b832048716a51fd8cb2e155c11f52415899d3a05f2e45f", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_ip_neighbor/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_ip_neighbor/meta/main.yaml", "ftype": "file", "chksum_type": "sha256", "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd", @@ -172,7 +291,7 @@ "name": "tests/regression/roles/sonic_lag_interfaces/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "7198a5cabb43600d45494873192676174231f617e4bd15e13ba4e8f10b6944dc", + "chksum_sha256": "f327aa7319766dab5cf7bea44ec96494908930d205b60d08db4f80957c059085", "format": 1 }, { @@ -347,7 +466,7 @@ "name": "tests/regression/roles/sonic_bgp_af/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "cf66c8d3e6ab8abfe99759ba204cf9bc33e8108248489ad13dfcbc8b6e8b1484", + "chksum_sha256": "b62b37272161b4ddd12dabc2e373e14e479537b8860a73a73d38d82eab449db4", "format": 1 }, { @@ -365,6 +484,76 @@ "format": 1 }, { + "name": "tests/regression/roles/sonic_dhcp_relay", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_relay/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_relay/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c27ff568fed008dea00a2b6949da7c6a755c5a451b50b29b217ab00be056a368", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_relay/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3e06cd628bdd1aa09a2388e9df767eac0bb5844b90a86216270bcf5d3b93db42", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_relay/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b9566df6886312aa83c5888a95d4f5e39d8f3397953fa2e9c0c5a59f6e0de748", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_relay/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "903c93343b0c14238a167f67e178d767b86fc2c0380b0b046d16f4b2add7cae2", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_relay/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_relay/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "94bf41ab768d299355fe3384854cd92ce500a8ecc9b8bbb390cce687d0e454f3", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_relay/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_relay/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "af0aad24caea6100f1eaf28dd660a9deb60f86870295a92c77ba0dc0ee9b92b1", + "format": 1 + }, + { "name": "tests/regression/roles/sonic_interfaces", "ftype": "dir", "chksum_type": null, @@ -382,7 +571,7 @@ "name": "tests/regression/roles/sonic_interfaces/tasks/tasks_template.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9d9e4c73c7952f36475c79fbf88ed2e5866588bafe4acec029dfc50071b75e5a", + "chksum_sha256": "0a7e039308eca14d5eeb7f856f663aab56621275ecff63fb132260f59397c212", "format": 1 }, { @@ -410,7 +599,7 @@ "name": "tests/regression/roles/sonic_interfaces/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5e3c20b43609a7a0a867ef1f51dd5b891e6e8b891f5f07d04aada1883d1b6c96", + "chksum_sha256": "0f8a93d53647d2a8a4c91c8fb9d62a39e903ba7bf3d377e186a8666659f41e77", "format": 1 }, { @@ -428,6 +617,69 @@ "format": 1 }, { + "name": "tests/regression/roles/sonic_copp", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_copp/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_copp/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f9ce82484cecdba7d6d3426c23da85cda523b37cbcd9b853643437a3431b141d", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_copp/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0f8d499c11967160cbab26e5f4508f3c408132a6f75cdbb4f899eec69c9d3cb7", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_copp/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2f9ea6a1efb1752d1b02311b9294762286fb8a394a805c08adc254e6ba293bd0", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_copp/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_copp/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7746cee5fb3de5273d9cfb523635aa5ad277f61d21f1557373cf21e009486dfd", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_copp/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_copp/meta/main.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b16570e34d6f129bbec985a4ed0852051e0bb2a4007e8a9a73120f9a1d3ae876", + "format": 1 + }, + { "name": "tests/regression/roles/sonic_tacacs_server", "ftype": "dir", "chksum_type": null, @@ -501,7 +753,7 @@ "name": "tests/regression/roles/sonic_tacacs_server/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f2800620b593938fecb1b8a7354c680bcc3f1fcbb59381ca25889d2424b82198", + "chksum_sha256": "71f69483df452a9a49c621a0e5d1bd6969f94e5f8a904182f0292304af1ce4ba", "format": 1 }, { @@ -519,6 +771,153 @@ "format": 1 }, { + "name": "tests/regression/roles/sonic_lldp_global", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_lldp_global/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_lldp_global/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4f7db7fe6a82ee960e18bec865faa1062973cdfa30f9ec0b21c6b81edba890e6", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_lldp_global/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f0f93f0b020d3d1680d2e64b5337b77d643997c8704f39e5f447473298231668", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_lldp_global/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2ad002966c04a93d1cbda87ccb6a304bbb78ae20b4ff2eccbd30be279ee4b116", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_lldp_global/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "885eb873521b39d983538049ca493bc3f71b6e0f5fa220202bebe4faa356c9a4", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_lldp_global/tasks/tasks_template_del.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "18ae30030b9a4aba7f28bb37e405852600fdce4597f65a155f837b153670bca0", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_lldp_global/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_lldp_global/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0d2651aa73462357d6a8689016231f1e83d3f90629e0c57ff38a06116c7c7f13", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_lldp_global/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_lldp_global/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "af0aad24caea6100f1eaf28dd660a9deb60f86870295a92c77ba0dc0ee9b92b1", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l2_acls", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l2_acls/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l2_acls/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9757a1b1fe3a0bca2fd41e679cea17d540be146945a3ec76409e86ef0d58580d", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l2_acls/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "193bd7ccf62c0e7a3a6bd118df9644e87be8f763508afaec9c3f560baa7eb505", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l2_acls/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c1d91d6f3391c839f0d4ae26f43946678fd58ffbe6247220a408f3b7eaaf231b", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l2_acls/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4020500110e677dd3b356d91a7125b7bc2a31273b3649d8f510b12d7fd95fa0b", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l2_acls/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l2_acls/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cd4fc37c2effbf6cb374040a6b05e07aba468c412e06c0a0158a1b2bb2d04881", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l2_acls/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l2_acls/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "af0aad24caea6100f1eaf28dd660a9deb60f86870295a92c77ba0dc0ee9b92b1", + "format": 1 + }, + { "name": "tests/regression/roles/sonic_command", "ftype": "dir", "chksum_type": null, @@ -669,7 +1068,7 @@ "name": "tests/regression/roles/sonic_bgp_as_paths/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9f72f840292265f4c4a9d96c0ec18aec1f73302bcd870b4b351ed319286eddd3", + "chksum_sha256": "6fc80ce5ba488d6713de614989f50f39e9bdaf770d42ef564c68daf9c442ee97", "format": 1 }, { @@ -732,7 +1131,7 @@ "name": "tests/regression/roles/sonic_vlans/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9f64b5c536fe544240ce9c38f0cb6cd324c5759d627a5c290bf0347fd2740512", + "chksum_sha256": "b494021c45cd48e08b01e9ff35ff6544402e846e0aa040f784d00cf791b648e1", "format": 1 }, { @@ -802,7 +1201,7 @@ "name": "tests/regression/roles/sonic_prefix_lists/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "04127c5fbb729e1e955d2aeaadb9ec67d2df28afaa7cd9918a836942fbdaed9a", + "chksum_sha256": "646f761fa8c6d1fb18d576d19bab65bab0a48a4327ae4d8865c212725309c662", "format": 1 }, { @@ -914,7 +1313,7 @@ "name": "tests/regression/roles/common/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3f82f73da84954bb7f19f2dceb7105bb98e34f83943514a99533dcde30707c6c", + "chksum_sha256": "2a818cf45804f22099e39cdebe416c89acc29d1177e2ac34c6e7953828514f15", "format": 1 }, { @@ -932,6 +1331,76 @@ "format": 1 }, { + "name": "tests/regression/roles/sonic_l3_acls", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l3_acls/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l3_acls/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "45862482bfc8612b782ac80b2011df8e3aef9fe0991c87c31b6d981ea6da1d1a", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l3_acls/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3fefb47c57b7eea6e953664778ebb024a47d5ebb131f891c80b50e6b5a9e27e5", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l3_acls/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c1d91d6f3391c839f0d4ae26f43946678fd58ffbe6247220a408f3b7eaaf231b", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l3_acls/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "970a3b340a7ad61f92b8433e145e786ff075d3828821db2b5ffb9d6ffb775207", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l3_acls/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l3_acls/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "aaa7417efbeed43ec3285569e4b5f9eac229a530311250a200b0e249e54653de", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l3_acls/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_l3_acls/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "af0aad24caea6100f1eaf28dd660a9deb60f86870295a92c77ba0dc0ee9b92b1", + "format": 1 + }, + { "name": "tests/regression/roles/sonic_ntp", "ftype": "dir", "chksum_type": null, @@ -984,7 +1453,7 @@ "name": "tests/regression/roles/sonic_ntp/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f86c7979a8ba1918a426d3a04d8eea9ca1d1f0bb0e9470e9be5a1a426b0e1aa4", + "chksum_sha256": "dd02c49de6170dd9e5f1b32004c8224b986d1d3a60c071a4ad5fba8b0f53c31a", "format": 1 }, { @@ -1075,7 +1544,7 @@ "name": "tests/regression/roles/sonic_radius_server/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5f4b8ef73168fc7cdbf0582a176198e4034c0c8301120a9e65a170c7e394bde6", + "chksum_sha256": "8e97b8f8db857938ce1b1d31c02b427ef03e2b9acce6b32de5f09cfe430d0fbd", "format": 1 }, { @@ -1159,7 +1628,7 @@ "name": "tests/regression/roles/sonic_vxlan/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ebd7db1ebfdb114eb0280e5ff41adaffff92caa2df2f74cc7dc98a33214d36ad", + "chksum_sha256": "98960e6d041d674de21efac8be60319c82f32bd6f62c13349fa65ab2ac9aa01a", "format": 1 }, { @@ -1191,6 +1660,13 @@ "format": 1 }, { + "name": "tests/regression/roles/sonic_mclag/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f0950d1798fd0b6ae3c49dd051c9e6049a8a796f221c14be8d602e2ce015d263", + "format": 1 + }, + { "name": "tests/regression/roles/sonic_mclag/tasks/tasks_template.yaml", "ftype": "file", "chksum_type": "sha256", @@ -1208,7 +1684,7 @@ "name": "tests/regression/roles/sonic_mclag/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "59f11ea9543380e101f9d2d9bd11d47f0ec0225aac74b706647b7bb6babad758", + "chksum_sha256": "df17fe35195f652a957121492f407dd281f1759e04250c89e80c8370b1e84fba", "format": 1 }, { @@ -1229,7 +1705,7 @@ "name": "tests/regression/roles/sonic_mclag/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1a3fdc2249981d95b911544645bc81439e0bab922c06eb72d9a53457122539ca", + "chksum_sha256": "67b8c0dcf572a867ffb049254f897c9980a18951de2fd96ed28cf272f17c3352", "format": 1 }, { @@ -1296,17 +1772,10 @@ "format": 1 }, { - "name": "tests/regression/roles/sonic_users/tasks/cli_tests.yaml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "37f946b76a42a134a1e8bd8bd337760512e44038b418be2d3943a2c05459f6f2", - "format": 1 - }, - { "name": "tests/regression/roles/sonic_users/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "895f8900d570fdfc3c187e09c688ceefb21cd13bcd8af8b78beb2dc7b59c5a4b", + "chksum_sha256": "4159d2c33b2349ee35482a7af04584f302c6033466e8c65c4c86d5c20c0d92f9", "format": 1 }, { @@ -1320,7 +1789,7 @@ "name": "tests/regression/roles/sonic_users/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "29e2bb2399e1576c889e4d1a2c915773ca57c3b0befa939be5c1f30bd422f6c1", + "chksum_sha256": "6bd93f27731271ea90d1bac60a29523020ee18ea8873c1488f951f704cb861f6", "format": 1 }, { @@ -1338,38 +1807,101 @@ "format": 1 }, { - "name": "tests/regression/roles/sonic_vrfs", + "name": "tests/regression/roles/sonic_dhcp_snooping", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/regression/roles/sonic_vrfs/templates", + "name": "tests/regression/roles/sonic_dhcp_snooping/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/regression/roles/sonic_vrfs/templates/cli_test_case_01.cfg", + "name": "tests/regression/roles/sonic_dhcp_snooping/tasks/cleanup_tests.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8e05b1c592caa3ffa0aef7d93d7f1e12d42c1eb98ce4f6b5b8736ac698ebd351", + "chksum_sha256": "b614a84ae322512ff4f365d3964688f6cc4fec47d0aa39c3699a610234039c2f", "format": 1 }, { - "name": "tests/regression/roles/sonic_vrfs/tasks", + "name": "tests/regression/roles/sonic_dhcp_snooping/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d8e57b2782f58b8f61663fdfc7ba10faa1b169c463a9852e3e36c90f5a89f6db", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_snooping/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b04b49d3e449d0b75e1970e581c970c31e55641b2e798622c29e1917862d3549", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_snooping/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1933de6c2fbf34610af75db33540157c8447dd5aed36cb2fb9a3e7e9f1c20775", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_snooping/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_snooping/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "93fe55260d77d598c10fec53353ef25f97fd473a95281670d06a78543803c0dc", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_snooping/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_dhcp_snooping/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "af0aad24caea6100f1eaf28dd660a9deb60f86870295a92c77ba0dc0ee9b92b1", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_vrfs", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_vrfs/templates", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/regression/roles/sonic_vrfs/tasks/cleanup_tests.yaml", + "name": "tests/regression/roles/sonic_vrfs/templates/cli_test_case_01.cfg", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a0a4218ce9437e1a54f2edc499296591ffdf35f861f5d12eee0f53b76292032f", + "chksum_sha256": "543ae24304c348bec48a338979d12396b22638cb4064ba5c78fa822c9b5c2b0d", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_vrfs/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { @@ -1383,14 +1915,14 @@ "name": "tests/regression/roles/sonic_vrfs/tasks/preparation_tests.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a640983377723baab16e6b9a3fd9b5f68fd947abd94282237c8591a396fefc94", + "chksum_sha256": "47f9d14be7a62e7c2ce8078162ed5e227ae4f66431a317a49e26de7ac2550914", "format": 1 }, { "name": "tests/regression/roles/sonic_vrfs/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f27630400987fa257da883e08c8fc908510f51e2dea4bbba00219d329fe3eae4", + "chksum_sha256": "4c66d3dbf3797263c35165a4ee0b1700191fde6756bc9bdb0691fa00b82db906", "format": 1 }, { @@ -1404,7 +1936,7 @@ "name": "tests/regression/roles/sonic_vrfs/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "31e02192de52bb9e3299115d5392687e7baf808d0224e168e426bf1fd98a6048", + "chksum_sha256": "7d052d943034184c33885c4c5564a83993825026a82fb592722fa75d2f01c7d0", "format": 1 }, { @@ -1481,7 +2013,7 @@ "name": "tests/regression/roles/sonic_system/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5d09922e1001ecaedc3e5326bd7f1c4e9133357ba2eba8df4c8fe8e9ffb9eb4b", + "chksum_sha256": "923b800c0f4be3ac9074a1ff688ba3b9b725076118fc46355a1024c2af6ad8b9", "format": 1 }, { @@ -1625,6 +2157,13 @@ "format": 1 }, { + "name": "tests/regression/roles/sonic_static_routes/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "da555d0e369c8c0daae5112eb1801f8cf6d53c40f276525b607f7294d1ac92c0", + "format": 1 + }, + { "name": "tests/regression/roles/sonic_static_routes/tasks/tasks_template.yaml", "ftype": "file", "chksum_type": "sha256", @@ -1642,39 +2181,102 @@ "name": "tests/regression/roles/sonic_static_routes/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "05c972841c89a97abfeffe3ea0ce4df26d90a7ea0e4b12a43e94391dc8f4a8a1", + "chksum_sha256": "3d4e79dbd408f0a9143552e55412c5b47f1cb8970c3afc70a9954fb99bf08ac5", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_static_routes/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/regression/roles/sonic_static_routes/tasks/tasks_template_del.yaml", + "name": "tests/regression/roles/sonic_static_routes/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "162e1ee53ad29b43871e3319c81a8a897c3fa5e431c3adfa11225c39819c1b68", + "chksum_sha256": "26bf5a3a1f242151d5192d191279032cd9de8a19e3501fc5b0496f52bfa0efd0", "format": 1 }, { - "name": "tests/regression/roles/sonic_static_routes/defaults", + "name": "tests/regression/roles/sonic_static_routes/meta", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/regression/roles/sonic_static_routes/defaults/main.yml", + "name": "tests/regression/roles/sonic_static_routes/meta/main.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2df5842ca274f91f4c478ebfa10e895039cffe8e44eff6eb846a0206feb04192", + "chksum_sha256": "b16570e34d6f129bbec985a4ed0852051e0bb2a4007e8a9a73120f9a1d3ae876", "format": 1 }, { - "name": "tests/regression/roles/sonic_static_routes/meta", + "name": "tests/regression/roles/sonic_mac", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/regression/roles/sonic_static_routes/meta/main.yaml", + "name": "tests/regression/roles/sonic_mac/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_mac/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8bfae10085ae270b566d618ec13b499ffd57691d6eb264789959d5b7c4949253", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_mac/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "72432a5fbaaa41b3ffd8494450da257df2b6736654b93fdc72c5c6c20cc97076", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_mac/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9b83563657926ea8b4f3ab71eb6a190f55b05e729ad0a9de24c51dcd5b38ceb2", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_mac/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ca35cf404e80a4326059cc872ae324c80d871f636e59401ee02a822d6dbe8b3d", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_mac/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_mac/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e84a948ab216fa7501a30bee677b9c770d56c81e413a95003927e14aa95daa47", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_mac/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_mac/meta/main.yaml", "ftype": "file", "chksum_type": "sha256", "chksum_sha256": "b16570e34d6f129bbec985a4ed0852051e0bb2a4007e8a9a73120f9a1d3ae876", @@ -1698,7 +2300,7 @@ "name": "tests/regression/roles/test_reports/templates/regression_html_report.j2", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ba914f5e928e8d590b975bc1392def64bf9724d0a405a17e20c2a40ff297ff34", + "chksum_sha256": "b44730b5284330a2308b097eaa9ad10ec3df0d0165b2a2efac6f9c3d777b79bd", "format": 1 }, { @@ -1796,7 +2398,7 @@ "name": "tests/regression/roles/sonic_bgp_neighbors_af/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8c74c59e8e666909f5dc072cafbfb243973612317c3a2aef51c9474ddb84c505", + "chksum_sha256": "47566db8562aa54bf86ace18e285524af1c203128332d459a45930a81c7bcda7", "format": 1 }, { @@ -1814,6 +2416,153 @@ "format": 1 }, { + "name": "tests/regression/roles/sonic_vlan_mapping", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_vlan_mapping/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_vlan_mapping/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fe8418d3201dc4e07b63ff53105f743f59567b1cb9c72eafbcfe03d6682d81b3", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_vlan_mapping/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a64267f08b28406bd2cc8f472d12e0fc9d43de121d41b15bdd628fe3a228853b", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_vlan_mapping/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a784efb397137c55c72693c1e62a4ae2ad95b350a9030e7c90fcf42c5c9c8549", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_vlan_mapping/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f3f1789877770c3a5f6141cdf26c45ccd43b6c81f1025e48075edb73321c007a", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_vlan_mapping/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_vlan_mapping/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c4fe4d04ceb98beac6327157077ca7f386b30a6cda19662437714c5adff51ed6", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_vlan_mapping/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_vlan_mapping/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "af0aad24caea6100f1eaf28dd660a9deb60f86870295a92c77ba0dc0ee9b92b1", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_pki", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_pki/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_pki/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "07bf7c7c747cbb27b05e02568b2d138b2ee0d1c32929edc785b4809ee5a9f362", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_pki/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5f176a6a888b035d4a7e7db03b38a9c8201547ee35b2ae67a691c360e4dc0a5b", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_pki/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1fbc8b30dea4474a35f9387409ed5aaa3a797d77990cc01a5d63e84b85ca1ccb", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_pki/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "84279b579ec3b6afd9b4e0e80dec9b7f16467cf4cca02827a22a70bd3773b49d", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_pki/tasks/tasks_template_del.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bf6880b29e7cb0360e99db6440f732b27de14ff9cdc89435ba6933ff0c088733", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_pki/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_pki/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ddd3d69d300a23a64a8dbd01be9d2e75950e6563daaf2856ffab266fa84f78be", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_pki/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_pki/meta/main.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b16570e34d6f129bbec985a4ed0852051e0bb2a4007e8a9a73120f9a1d3ae876", + "format": 1 + }, + { "name": "tests/regression/roles/sonic_l3_interfaces", "ftype": "dir", "chksum_type": null, @@ -1838,7 +2587,7 @@ "name": "tests/regression/roles/sonic_l3_interfaces/tasks/preparation_tests.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9cf66706c721d3eea2222ebb0aba2c6fe1b4cdba36cd8bbdbffb39882f551d51", + "chksum_sha256": "b7e1520a9a666a935ab282a4a54f6421717457a50169b3364ad0b6f90921d4ff", "format": 1 }, { @@ -1859,7 +2608,7 @@ "name": "tests/regression/roles/sonic_l3_interfaces/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e8ded0a1e8c9fa73380e115371f1cfc30a59fbe4520fddf1fec4c09f8317ad4d", + "chksum_sha256": "9f52ca0fcfe84732914aa8fd009c1359f2110c5a761bf5c6f4fb5bf2ee7e4f5a", "format": 1 }, { @@ -1929,7 +2678,7 @@ "name": "tests/regression/roles/sonic_aaa/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "696ae45b0e5d370c8b2330355a266ecb910da3d29073a6892ca3f756c18af4c8", + "chksum_sha256": "b27248b5ccb69300f21f7d146f203991ed7a21aac4020082d1828ad387d0cf54", "format": 1 }, { @@ -1947,6 +2696,146 @@ "format": 1 }, { + "name": "tests/regression/roles/sonic_port_group", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_port_group/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_port_group/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "609ba46dc67d758e013fa3d7b903efe6432896aa257ef6a4810a768e287d437e", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_port_group/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a5e0319990e4850b0c26104462b64de5538845f2070687efd65643bd53c2eaeb", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_port_group/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1e57db59c6f4143f32fb6ef55ad4365bb6c99bbfc112dfb8c3a88ca82186b740", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_port_group/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2bba3732c4a67066dfd85415588a4363774dfe38a767f7529834bf481ab5035f", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_port_group/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_port_group/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e9a39599414d552b3da8bafb342d27761a450f34d641a2b8067f372a3b266479", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_port_group/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_port_group/meta/main.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_route_maps", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_route_maps/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_route_maps/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5d7aeef77926e57734152e5267e2f662ae13feb403edfae40df642550b6f79dc", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_route_maps/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4a138e8a66245c02dbf7e3f25728f5c9575d9df602cf78e27f82e43556985351", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_route_maps/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9a0798645ec73fc0c9a19f75acf10ca277a6496e52436cd76c15920a8bea5475", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_route_maps/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c5bcd1f568fa27b9c1bd1ca8204499df590663028ba59ca3070a7884e63d4f2d", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_route_maps/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_route_maps/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0eac27d4b755873e686133b19a0ddb237fe17c6966865982b4e1ace457bbcd91", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_route_maps/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_route_maps/meta/main.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b16570e34d6f129bbec985a4ed0852051e0bb2a4007e8a9a73120f9a1d3ae876", + "format": 1 + }, + { "name": "tests/regression/roles/sonic_l2_interfaces", "ftype": "dir", "chksum_type": null, @@ -1992,7 +2881,7 @@ "name": "tests/regression/roles/sonic_l2_interfaces/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f9f8c4a3db4f35e73942f4a63a109181a274470176e955ab0b9a4403446f743f", + "chksum_sha256": "b63a9264a3fbe13aaa02ad4c5a7042d498457104ce603e9c203ebcf6a5f7c5e6", "format": 1 }, { @@ -2010,6 +2899,76 @@ "format": 1 }, { + "name": "tests/regression/roles/sonic_logging", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_logging/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_logging/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "86eee618148807d4346ebef4e688f2f6469f18482f609177e8dd27fc3903050b", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_logging/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "46b4d92958a8e042e9a8fe87eb8a2f5ce55a47f1853e670a915e28582f4677be", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_logging/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "574a69598232bc2ef6b98c5f7fd1c526f1dbab925885726791e6bc2188c9a54e", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_logging/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fe9d4e14ffd81f5fde70ad10d78c8476d9bb5b9cad6677fe07fa50de4b29a8c7", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_logging/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_logging/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b5f0a520ca407ae8525bdeaca7233ffd78a33909b9add21657ee64c26d555c39", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_logging/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_logging/meta/main.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd", + "format": 1 + }, + { "name": "tests/regression/roles/sonic_bgp_ext_communities", "ftype": "dir", "chksum_type": null, @@ -2055,7 +3014,7 @@ "name": "tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "af2a290eb466df24bdb922fea12f682d747f282f863e490f20c5e52b04257c87", + "chksum_sha256": "d97c8ce284a0ce4462f35fe81910920e9df1b864d1789aeaab1464dc3de293b2", "format": 1 }, { @@ -2073,24 +3032,80 @@ "format": 1 }, { - "name": "tests/regression/roles/sonic_port_breakout", + "name": "tests/regression/roles/sonic_acl_interfaces", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_acl_interfaces/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_acl_interfaces/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6023efafd474483d305c0a00be0cecf00e6b2b1f59597fff6648875ed11d3e23", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_acl_interfaces/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3a7a9b02d62c55c9de1d8c8c3bf6e73a5a665b87fa4c39c680c22c7432120281", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_acl_interfaces/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ed1a8509ced1032c4cbcafd67a756ae7149967ceb5c843d4a9259b82bd50b738", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_acl_interfaces/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2dc54dcb4c04c0ecfb6c7a8e2f76e31057b3390e767f57fcaed428fff64a2aff", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_acl_interfaces/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/regression/roles/sonic_port_breakout/templates", + "name": "tests/regression/roles/sonic_acl_interfaces/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "19b4facf27771fc212fc8a323d019dd9084cad21b50091f3cb75e4a20ee74bdf", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_acl_interfaces/meta", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/regression/roles/sonic_port_breakout/templates/cli_test_case_01.cfg", + "name": "tests/regression/roles/sonic_acl_interfaces/meta/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "7d4f4ca7215bfc805c826f3d98811131a6b0d2a4edd3e7e7ebb98ecac9fbf9ac", + "chksum_sha256": "af0aad24caea6100f1eaf28dd660a9deb60f86870295a92c77ba0dc0ee9b92b1", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_port_breakout", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { @@ -2104,14 +3119,14 @@ "name": "tests/regression/roles/sonic_port_breakout/tasks/cleanup_tests.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "7fa4dc0183d6c8a679bc13428976c0398b8a87bec0d1032fc6b9d29dc2e4a72a", + "chksum_sha256": "57668ca2a2ec12b8936b63a90584ed2e42cf1df540dee221c51c768869f62e33", "format": 1 }, { "name": "tests/regression/roles/sonic_port_breakout/tasks/tasks_template.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ee8893139926d4625752da4613cdf0615613a69ad298a5adbf58641af1249f1e", + "chksum_sha256": "aab1882c51c9960dce77b9cad7b7b248ece221b8579e69f2f686882d4252d12d", "format": 1 }, { @@ -2125,7 +3140,7 @@ "name": "tests/regression/roles/sonic_port_breakout/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "547f1a09ce0a20c3f9e1ad4b189618dfc6f260b23ddd9c19d29228d08409f228", + "chksum_sha256": "41e7d0e8c33bf1696cc52354a2bcc48ef506f7050b6a565f5fdbff17b7bf2939", "format": 1 }, { @@ -2139,7 +3154,7 @@ "name": "tests/regression/roles/sonic_port_breakout/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e9ba4af225692807f8176182acc8f195d083e9be5c9efa46b6dcba30c8e21f04", + "chksum_sha256": "d111a1491cb26bbad6bf5dc053cedb04a5da1b9b92c13a13dfc0f6123be2658e", "format": 1 }, { @@ -2157,6 +3172,83 @@ "format": 1 }, { + "name": "tests/regression/roles/sonic_stp", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_stp/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_stp/tasks/cleanup_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "42372bd707f2523bad1f34703d862ece8da9267ccc3e1ba874d610f6b3fb4e06", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_stp/tasks/tasks_template.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d96cb1d38757a269f3e6d76f7bb9738db25b446b299fd023673372a8d8572fd0", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_stp/tasks/preparation_tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be5ca34c31c98943ebfde9441e451e3d54426c8e1754a48b64b923c96cca6bea", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_stp/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ccef7685ab5396e8ede8b998f0729f20af1e9d9c96a4743ebd20d301981b41a0", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_stp/tasks/tasks_template_del.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "162e1ee53ad29b43871e3319c81a8a897c3fa5e431c3adfa11225c39819c1b68", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_stp/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_stp/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "29f3e4e7b69f48f417552efdef5d8c38df4d8518275287ec09d88f7d4a5eea56", + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_stp/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/regression/roles/sonic_stp/meta/main.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b16570e34d6f129bbec985a4ed0852051e0bb2a4007e8a9a73120f9a1d3ae876", + "format": 1 + }, + { "name": "tests/regression/roles/sonic_bgp_communities", "ftype": "dir", "chksum_type": null, @@ -2202,7 +3294,7 @@ "name": "tests/regression/roles/sonic_bgp_communities/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "dc406ecec44fdd088a078456ed4385c7df08875bbc1b12bc216a8ce5b88da5c5", + "chksum_sha256": "9f504a50c935ab1338be04bfa241007828d028dcf233efb52bfc38d52a639b72", "format": 1 }, { @@ -2237,7 +3329,7 @@ "name": "tests/regression/roles/sonic_bgp/templates/cli_test_case_01.cfg", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "aa7d7ab0948506ca0f3bf4deba39bc70898dc04b0374a4cce76c8683b4017c53", + "chksum_sha256": "53b28c7b273239f77b18c154a7d2e2ae7a0c0e14e45b0461df31a3bf0a2b83c8", "format": 1 }, { @@ -2272,7 +3364,7 @@ "name": "tests/regression/roles/sonic_bgp/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a0496cb1c3516588ee23d24020b55400241530bfa142471bcc79d25a07064f11", + "chksum_sha256": "3b173415754c18435f3641ca991cf903ae28a1be2ecfd53e1fe25105695258c5", "format": 1 }, { @@ -2286,7 +3378,7 @@ "name": "tests/regression/roles/sonic_bgp/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ba7db7bb0225c59771f2c8809ba3de33ec974de3821518f70faea65cc24cdfd5", + "chksum_sha256": "5455f01f6a02095972453d6d292bdafafac6b7cc9b1b56b578ce12c3be65795e", "format": 1 }, { @@ -2318,6 +3410,13 @@ "format": 1 }, { + "name": "tests/sanity/ignore-2.17.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0fcb36eb462773c00b14c9e9f4db400f752ceed07d6ceb2a34833393f0b01134", + "format": 1 + }, + { "name": "tests/sanity/ignore-2.13.txt", "ftype": "file", "chksum_type": "sha256", @@ -2353,6 +3452,13 @@ "format": 1 }, { + "name": "tests/sanity/ignore-2.15.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0fcb36eb462773c00b14c9e9f4db400f752ceed07d6ceb2a34833393f0b01134", + "format": 1 + }, + { "name": "tests/sanity/ignore-2.11.txt", "ftype": "file", "chksum_type": "sha256", @@ -2360,6 +3466,13 @@ "format": 1 }, { + "name": "tests/sanity/ignore-2.16.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0fcb36eb462773c00b14c9e9f4db400f752ceed07d6ceb2a34833393f0b01134", + "format": 1 + }, + { "name": "tests/unit", "ftype": "dir", "chksum_type": null, @@ -2367,6 +3480,34 @@ "format": 1 }, { + "name": "tests/unit/compat", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "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": "bcc6ed7c3901ca3bd4b6e501f9fc44e7cf0c7c5fafbe067f58cce4e0682f60f0", + "format": 1 + }, + { + "name": "tests/unit/compat/unittest.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6bc5679c48b6d6440fe6801c44c0447b63b27ef82b85f2f06fc39a342a4a80fb", + "format": 1 + }, + { "name": "tests/unit/utils", "ftype": "dir", "chksum_type": null, @@ -2500,10 +3641,661 @@ "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/network", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_users.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "84846412b1338f7c1acd2e7d2a112886b1170c948e1a8eba14c60499ce85f947", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_ip_neighbor.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7812657b2836f8451e22b0e74d4d8a3fd849409b708b4706328df06aac8ed12e", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_bgp_as_paths.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3391da894ca7437bdc673ccf934a96b97ddbc8a611ce2940baa3b7bc2f324a73", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_l3_interfaces.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "33b9c2ed0478106edc13fee0311dbfee94e301863e4e00b704081fd8f0cd8c86", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_ntp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "461f62b920d3d9903399327e0236a40d642f3f41de10bad24c065caa11ec0123", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_vxlans.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b780f21666769f5acdaee027504a61b6a7ad3bbd77092b88a94756de7a3fbdc6", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_l3_acls.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "77e6a2e12d251ed8675c30a637d68495599b04ae95d4fa09188863babced33ec", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_port_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "df079336249208347c9a68f6abb9ce5b11aadd8b04cbffd859a7a19733538e3e", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_pki.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9b16780017d7c8ceaac10253705e1ca6e277dded24a04e1585d7e1386d5b4d4b", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_config.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "11a9890867861ddfa4da07ca2f5fa2a68292805c20980463d17201f2b05f1ea0", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_port_breakout.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4c7d62fcf9b4c8ca86eea87afdabe14b8f475daabd2e121c50b8bc7256366af8", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_copp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7f0f89b4157dfbbac078809763286a6e3f281e6bd915047ae3dfcb1bd127281f", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_aaa.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cf0a17e40e07834b3502c4e9e2c30cc4e8f2b86b4a3dedb3e29f7fb00b731f26", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_lag_interfaces.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "57ba750e25dd6e8a05a62017741d82737a1b9abc29930c31fc460bb815928510", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_vlans.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3d2475c5a04830284dc3d6dabcd8fb50dbd1a214bdb383e949877b082e2a874", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_bgp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e9cf8726d21fb237aa68c4532b5a53ef1d769951e8bbc8ac50105a422c90a986", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_l2_acls.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0a59c951c23ab38b24b21adca3442344171974784c3b4fb4add0d7bacdd6e2a8", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_tacacs_server.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e8eeac0b91a39e401a518a11bf081d661b104c632a1c1e14c6e132a51432acf5", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_interfaces.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9952114bcc557fa8da116d22c23f69d2d4a0f9dc44a504f8eeeb15132cfe7cea", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_bfd.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "739c57e36a10c62b40c3ce2196229522610a1d2c72875321be62b896d20ed563", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_bgp_communities.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b2c97c5b00a38d94308290a62e142953e1cee142badda76d5da189f1e512d629", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_vrfs.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "681b666257989b02b334c50d8b26de195ab78466d8743d71f3b70a34eb7de08e", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_bgp_neighbors_af.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ebf961a4a357a00ec2af0bfa2b531900f77d7d97ff9b9d5d5123a3e0c30ad373", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_dhcp_relay.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4b52a319b46255437ebe87481ffb3f1e24f7b21e5c70e8bfecc079fa07ddfb2d", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_prefix_lists.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "48e8b4857249158f91327b33c70efe2fac2968e2d3f697abe6aec91d1092fa5f", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/sonic_module.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "216071749200be4782c98d617334e20684f33475aa8a28caf2cffb681c0a0a54", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_acl_interfaces.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "937d2af485c722c75dbff22f69ce1a225e3bed7868150c34a24d07fab152d2fc", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_mclag.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9e99fc4f4929a873f6d0ff316e1e1514e8d5210f3d9db9dc63d5ad3edda67d61", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_command.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f7618ea5a4d586cd5e28e7cb2b9b730772f5396510f5629bbbb940a92831974a", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_bgp_af.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fdeb4639e96d7558cbd7fed66ebf493b1bf13fd4df0f63ddd0526253318aebc4", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_system.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0c649de8273a577f48d3530293227900073fe8e4af4f4a20a0cda6cae925b520", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "731ffb9a14e7e8da385ea2bd25b198c8f6db21e27556f02ff0b6320eb07031b7", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_ip_neighbor.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3059428bfe7023a47725f581e44f8bcfd5db0cffed22ba734530b9576771ac65", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_config.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6675258e48c305b5b25d837decc58afbf8c39e59a814bd5b77b6ba9dd5f4161b", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_users.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bd9a364fec8cd983f0bec7530969585e92bf8c5fed3260985b3e2d1531b3e17a", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_bgp_as_paths.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b6d192121712a806565c91392649f9bea0167d3f229011fe85f9f84b35bf7935", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_bgp_neighbors.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6650884d87f736e09997330bc84209581348183479a30bd95945a78a0714968c", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_port_breakout.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "afda0c3a42f25f93d64ef580fc8ddd3d8b0c54e59547407f2f15fb35b7b497de", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_bgp_communities.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8730f1ccb2748ff19bc4913aa73be0805001e6294fcbe14bd1a5e35e493e1389", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_system.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5c738fda63cc7b920300bc85f0146f4e2ac4a688ef3cd0ccb339271687e4b408", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_facts.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b86784ebea9530d30a397f672233acf8f17de1a8cabfa1b1ded87c87b14fe34b", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_command.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "73b43ad9e588b433190b5261a5dfbf78a154a91df67c1c77eae3b7733c5f7b62", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_vrfs.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "95f59f0e3adf9657fcdc64495f2317738c597da8d6ec574f50dc722eadf985c4", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_l2_acls.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "92095adfe22cca87229adfcbe4deabf9a2d871e17f75e244dfd31c3b4828805d", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_vlans.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2573b212e84c55195ebfd82647379c500405b798a417b449360b972e52a9d1b2", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_ntp.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b70ce5dfbde06d04873df25d4908a6c7e36118d48c76538a0801e0e317e36407", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_vxlans.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "aa577758b7fce46da14c389e9a2ba1a6068c91a4fa5d53060f45db7ba23a645c", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_acl_interfaces.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1d25cdc82397e9446992b46525f2bc86e523620702a2be242ad25a1945e9f3ba", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_static_routes.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "56bb744374ae377a7919fa393791d002c1fbfe7d924aee96f66d4348d7f84e34", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_bgp.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "796d02dc7a6256963db342978cbdab85785548c6a92d7ed4880b06803a92f792", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_bgp_ext_communities.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "78df3938f6c6108ae4bbd61eb16e70fc47410895cb087949a1ec84dc2be31da9", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_lag_interfaces.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "db2efffba2efd2c664b7a11a3443cc59eb56eff0734ff53322700388247acd79", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_mclag.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7ea9559192550e7e757b0083eb140fada8343dcb281906a6fa6a7dcd839a3295", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_bfd.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f1548b86305e7ded6b3c4e2f0122d283e624abf86830b756b006c6fe32083967", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_radius_server.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a10862f910d398696fd2687694e9d28c3f9094c8ff8bfa97f7c7fa9bc49558c8", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_api.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "738de456dc6edd2f20f53e12c86286a2fccf2049a8f05c425ec0cf9ace60d1e9", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_copp.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "39a64c3a8ca40faabe77bf03911facf7ce79568d9a611408a7a2b291f804ec66", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_l2_interfaces.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9bb6b1f86b55bfe2999b964bcd29dd9cdf045945645c6786727b230307ceb656", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_stp.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9507fe7193f23d26bae4319cbc3c62db5f8d438152b2715f6f6b7a50d6e88526", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_logging.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c78e20bd32dcec9e218e3007d3f29be61e34c0f6fea38ebc397ea619004ce49a", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_l3_acls.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fc8142a851881703d2405496f55be7100fdce1fa7ac3c17a22a1ff02e9b419e6", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_mac.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d96d40d02da28a8565988d54c7390c66e6ea3a0fd8888f60f0758d6de2188b15", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_pki.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ec23f706faf66b3693d412cf2efc5fddbe208f4c24e10d74d66de9d18eaa00f4", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_bgp_af.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fe4eb640ad6306d6e273651552ac92c206ff3e926953143deae0dfd02d188216", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_tacacs_server.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "24f036209532f7068430b1a17c7931645cd68041f67198ab1d541a2552ef9c71", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_interfaces.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "05b5d6d6ef66f7fbe851ab890de6f8a47348be74a5eccd0b9af0372994da6996", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_bgp_neighbors_af.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c831b13f5a6a3fbc814cb08f2d31a8cb8b3d489c62e2822db02becc71a016183", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_dhcp_relay.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8e3857d4a1c0bc982eb4c014935a5d45037aeeef489557a731d0684997143aee", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e67e6bfda5ee943c070db0918bb743e0ae2bcce5e552defefb31acb2460c607e", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_port_group.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c4c10d6c2ad330f39878c385b57318bceff8773787d93368a28ecb7fedad686e", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_l3_interfaces.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d87cb1772f8cde12fa511dbfcdd891d65189b223b8fbb7c33aa8d90c34188fa1", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_dhcp_snooping.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "265e10a30d2e2a0241874bed5e28eabf83c4472f9463111174305fd57e88716b", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/fixtures/sonic_prefix_lists.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "14bdd5088a6980f94a3c945095a1f6a72bb1271e9c91f2201c24596dd2dcc450", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_stp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2c4fa56134678807051f50ea115676ef2daeb02e120a636e18e7e9317c9b64b6", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_l2_interfaces.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9ccf584d58fdef4f278c730af969b575207d49a0e5817aaad92353dcf5bc9278", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_bgp_ext_communities.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ee0111f6cf42d91d8b26d5c5a7d3b1ddf6f8ccc6a1dd35f9f0baabf3b5043335", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_api.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "50ff06b123aa80391b948ee7c1cbdeeb5fb61ab0260f799f1c40effc474614c9", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_dhcp_snooping.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e87e4de6f76beb57425202461ae8e2f59150977855f6b27165531a90749f0092", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_radius_server.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2b549fb1c24643a5b02c9b9db20d35cf0e3812baa83c964ff0909401cda2a91d", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_static_routes.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e7a16699b24bbfec3c02574a0fd478865a314e1fe064921a57108071f7cc000e", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_bgp_neighbors.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cfbcb32220e63320fd084a4f5a854c8889d935741a60c8876a350d849966d68a", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_mac.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "070cabd8116eeb8cc6f41ef00bec60fdb775422ffc97963e7eac684f767028db", + "format": 1 + }, + { + "name": "tests/unit/modules/network/sonic/test_sonic_logging.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6e16524f514cbf5915c6de662ac92d2ae7848f7b5e5ff9e51855a241ec3657de", + "format": 1 + }, + { + "name": "tests/unit/modules/network/__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": "d0a1f40b1a4bc36faa044960fe9008d75b4a70f5507c7ed05753c0b439df7c24", + "format": 1 + }, + { + "name": "tests/unit/modules/conftest.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "909818cefd5093894a41494d1e43bd625538f57821375a564c52fe4219960967", + "format": 1 + }, + { + "name": "test-requirements.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e648ca0b7462d007b274d16f011185c624c8f9fb67cbee0b4e765efac87f14e8", + "format": 1 + }, + { "name": "README.md", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e9e65f5f05a2180c6335355f0497ff6ec97bc6fe7b58a8e3bd9c3442899cbf8e", + "chksum_sha256": "63c52092d1668009f1241e246365442da3db34c636131005008c2fa51c930496", "format": 1 }, { @@ -2517,7 +4309,7 @@ "name": "changelogs/.plugin-cache.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b0c306452265a20c55ab0327c7a69da747859d5073f753fc2be7d3eaa675348f", + "chksum_sha256": "ab8b94b3c8e4b7230e515d3319300f6dfde08dbcbf2b985eb0ca9829585fa10a", "format": 1 }, { @@ -2528,6 +4320,685 @@ "format": 1 }, { + "name": "changelogs/archive_fragments/2.1.0", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/149-l2-interfaces-vlan-trunk-range-support-for-playbooks.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c6e4c2fdb9d5c7cdd73282f5ef857f9248157ea1afbf9ac598997ea4b4c79fc2", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/188-unit-tests-for-bgp-neighbors-af-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fbda753887c058bbffda47d36b052778d193af72b9d22b6c26bfa0c98799f444", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/245-enhance-bgp-neighbors-unit-tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d3cbe2950a2a8b40bc72cc1fddbb8c8c84dc9b9865979f246680f0a956436bd4", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/200-unit-tests-for-l2-interfaces-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7f4b7df2958608897f7c217785b8e19895145355ef521ec33ec70e53cb9e9ead", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/228-unit-tests-for-port-group-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "636a2dbacf8e8931c5c8c45c0ef3b3b93f271b5445b7a687bf1d91f9c9f4172c", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/v2.1.0_summary.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ad89753a4aa93b1b71edaa6a2f817e2790449f4192fff2c820836c4193e26547", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/235-replaced-overridden-for-tacacs-server-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e0b9229139e100ad027db61f523d305be728c1ebe915cb7828658f2acaf800a2", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/249-dhcp-relay-replaced-overridden-support.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b34cebeaaafefbf834d44d4aa0549480211c982eda28a76be83c4ebf4b4e7070", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/119-lag_interfaces-port-name-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cd47affa17ae646beba22527a09c22668db181e0ea474b5f7abab9b642d64baa", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/145-mclag-new-attributes.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6e72f9aa77ba6204a04377118c78684e151f23812608dea99df827fb7036a74a", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/224-ntp-clear-all-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a9721c169efddea3936e05db66f522003b6585ae1bbb0cb0bef6ac0967de0675", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/255-prefix_lists_replaced_overridden_support.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2a87c4aff2c8d8813ef7c4e0b84dd128ae72cfc3e8426d9d759b10b6273a0de4", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/226-unit-tests-for-logging-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "665275271a150f45e40d5505b25a5ed3ffec2472c4cc44fd0470aeb42632a096", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/197-unit-tests-for-interfaces-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e322c453439484acaad04090adac3d1b2f90627453a8ef1707d0573bd31f4aa1", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/258-change-logging-module-source-interface-naming.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "72bd5a409e4cd1fbbbc0f3395dd3cda03b3b3aeae4f22c294cf571682926f182", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/242-users-replaced-overridden-support.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fa085731aaa5a0cf449dd73c2afba93ccdb9e4bbcf2f18ede632b409202153f4", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/202-unit-tests-for-l3-interfaces-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e5448eb086500b75e6d71a640f7c201446aa98a0871563500ee084b28f609253", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/209-unit-tests-for-prefix-lists-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e9f1ee74a0be7ed27fdf3ecb79cd1d2201e14bcf904283f19ca1782e73335738", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/199-code-coverage-workflow-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3dcde322b4ae9a703af34466f3aa143d6b4a559eff70b1fe200890bab5799eb1", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/182-unit-tests-for-bgp-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6bb536c5e5bd115cb0094b4be001baba0730b14b0fdfd32d5a31edd88264d74e", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/128-add-several-attributes-to-interface-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7bd7c3765cfd44e2dd5557605ca063d2f8142b20fa2caef01557c7ea0f2072a5", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/247-vxlans-replaced-overridden-support.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "74c1ffa7f517c2eb9a12768cfa1ee39b1957c5dd61fdfdc1a7770d0592853a74", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/106-change-ntp-get-fact.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a8c3eed258826c87638ac712bcfd911cab6b662115564d6887d11e9a87ed3014", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/250-bgp-as-paths-fix-merged-deleted.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "51bda25ca1035432edb4b60d2b8e803150597702349d20cb10399b392673effa", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/152-copp.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c27ed054e1e351e938d1055126364095b3a43ae67ff814bedcf2377597039a38", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/216-unit-tests-for-vrfs-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a7185931f582405c7d74d57f36dd21928e2694e38f3af12a8c54e65c69750ca0", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/257-vrfs-cli-test-case-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8d7ca4f37bdec6ce43df458ed8689c5b42a1963d3660255ab2140eb209169fee", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/237-aaa-replaced-overridden-support.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dba1f6a9d1573a404ce5fb2490df19da0f842840d36ce58d95cf26f3eef603d2", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/124-l2-interfaces-oc-yang-vlan-range-format-config-support.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9c2362d254f162c927a2269b15a65daafb4156d8fb23c6f3ce475ecd35bd4b7b", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/186-unit-tests-for-bgp-ext-communities-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1832d0ca05bc879e913a3261b786a38d43c682536b6e423f16c165209ac901b4", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/223-unit-tests-for-system-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "57a5cc5e12871c831308635c58356df3e38982165bb4d3f017e8bfa15cf9a60b", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/196-replaced-overridden-for-lag-interface-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5063fd4d27dd3187f30a0e1a574fc8c298c56e2550afd2e7e915fe2a819f3947", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/176-update-netcommon-version.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9a821830e86a4bf0da137da744b302c8fb9223f04096090c15d6ea9374a0fdc9", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/159-replaced-overridden-for-system-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1a8e923f9cbf1c938e45d922fda741356e3275bc2a310d2cd233424eea2023cd", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/146-vrf-mgmt-bug-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c69085bf2db0f7c29b789bfcd52595c2181f1162128551a847b1c8ccc46eb434", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/177-bfd.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a4f3e29535d1feccbe044dc95327482cc3cfaf6a4449b1d43e870004066f04d2", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/253-change-replaced-function-for-ip-neighbor-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7e61759122b7322f5b7d5b3c0c51f8189cf69c2196e545f66fb3192b2c548227", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/210-unit-tests-for-radius-server-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1b8911946d8327f60b69093cda80e84049ebed28fa5c8a34ef29082d7f46ab8d", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/129-ntp-minpoll-maxpoll-config.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8ac8ec33b7da98fc6ffe4cb793c169b78bf9ed8cca4888e54c1781fffc0269df", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/233-bgp-neighbors-defaults-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e71284112a80fbfa412dca48386fbd939f13bce3cca173efb0d8ad2af63b18eb", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/157-mac.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ad0c0fd7f4668e5bd0979f2c23fcdc4ad4d107d55e344b99585843978bb82437", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/229-unit-tests-for-port-breakout-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "631fcdb3ee9ec6a0b576af5ff4cb42ece858c0fd5d010082808050f0b51b3b8f", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/231-l3-interfaces-delete-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "53f9b152c9d62551596480f741a1efc50ec2b9a6d24fb7d16f2b75beb56eb7fe", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/150-replaced-overridden-for-logging-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ed910e2599398dc3ccef9de7648dd2e7fa88d6979b15c10380fd2d0ecb87d3e8", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/217-replaced-overridden-for-vlans-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2c48a9f53c3307d9225eabc2c35641e66f663fb4a2c04846a91991fcece7bf05", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/180-regression-failures-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "db62984e703dd22b1eeb4dffc112a1f80396ed3245da82b25f6a18dfb19e3e8b", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/261-interfaces-timeout-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "98ff91df7bbe6e9f5be04b63e3977eb3a4cc57b82f87658b4837e1eb46fc8234", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/191-unit-tests-for-bgp-af-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a898014ab3470844efb2821bb32ad2c33352cc36ee78887bd29975f6544850b6", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/114-change-ntp-module-name-in-regression-script.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0a5fc5498935e33885b3903f3b3cfbc405bd582b08db5e70b04b7580982cfa64", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/220-unit-tests-for-config-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4d66e028d2bb3e6eed5008e7b987c52960ab41dd0b083b763d904aed9d85cdc6", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/214-unit-tests-vlans-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "51250ebbb062d1d53b546d5971d4ec07c4540799538c8855ffab9763a3570c86", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/227-replaced-overridden-for-port-group-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8aad90aeecf09994a6fa9b782646a3372f17ad551b2e6ffa25f33d031c1df8bf", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/107-change-ntp-key-values-in-regression-script.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4d046f0dae04676361731428d29ceb848553838f78d6282aa3d77d9c39367643", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/156-replaced-overridden-for-vrfs-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e345d0b0c85ec6925ed46e8497a998b5b0f17f35acdff2eb8978917e3c6cb118", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/172-module-utils-import-remove-empties-from-ansible-lib.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fc4f42c6c1aa3adecf1486b29c1acd9c8c25840e264834dc4c4fb6c52e05e4db", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/215-unit-tests-for-vxlans-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b220e64d0d4a4024d658176ac83c8042c675ecfaea4dc7a150e641be6a86bcae", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/195-aaa-login-authentication.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "acbf365f955ed27f745684452ee5e0600084ac3ed80a1b107ca38311fc1c5941", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/240-bgp-replaced-overridden-support.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b5f79d958dd00ed8dc62bec7d0230cdea640aa28619c53163b5a941de10f9255", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/203-unit-tests-for-lag-interfaces-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1ed1d97cedf9ab3dafeaa7212c05762d27def6994812013531c5be33b2ac686d", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/184-unit-tests-for-bgp-as-paths-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d158692f06482f8f2da4e9c97711f69dba5459ce9d6e8a66ea1c821b950400dd", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/262-vlan-mapping-bug-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1404acb34e5e7ae4dd6a8db1d30103b387ebf7f73e5a0d4ddef08f914fb79a73", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/130-vxlans-attribute-restriction-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5f19152d3186eea17439f4f5375cc0625a6659372814ac328a280c68a9cb09ea", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/118-add-ntp-prefer-attribute.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "619ab15ba2b2d4a8b88e888c2e30193704cd92e9baa88bb0201ed34e1674ff2b", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/246-bgp-af-replaced-overridden-support.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4e0c00649384d85f08625580adb0256e53366a66d25cc692a1b1d3b417309900", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/198-unit-tests-for-aaa-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "89680addb1e277c7b6dbab77959afd5ebcd270cc9eb28c28ba730513ef249aa1", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/218-unit-tests-for-api-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3df9e517eb46ad5aa504c77e3930c9a841ac6aae2b2fadf27de76622eb8a4c59", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/213-unit-tests-for-users-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f4910e4e2a46a65242d8c341da84aba1eac100cd8df9854bee4965f50e37147b", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/225-unit-tests-for-ip-neighbor-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a90d443bb531e3ed64fd3ad361603997111b8e5a9fec650287839b0e23fa9e3c", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/160-sanity-check-errors-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "da281479af1eb6633dc78ab6574ee79cffccfa8b508ebd75d299356a6fc11d80", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/151-replaced-overridden-for-ntp-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "24ffff39364be20efcef0aca74e15cfafb67c3e55286384480504829115a8246", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/230-vrfs-delete-interface-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1433f40b00b40f9c4af805e6f40bac1a3df790234c4c10103ef1393211ebeb80", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/212-unit-tests-for-static-routes-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "40f906a73d93fd6b9129ee2f2d96be9bb5ae11f43d9431154d963a6ac7e569dc", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/219-unit-tests-for-command-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "441c47066c728308b7abe67abd7a0d9057b54bbfa4dee006122e46745da0001a", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/236-static-routes-replaced-overridden-support.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a9c8cdf4e9973ea0c72401ffba3e98e2893b9c9afa403df105a8f5f530e9e279", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/208-unit-tests-for-tacacs-server-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "731ba989a8324e7b5942791d245a14fbe20c266850f60224167414e394a93332", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/185-unit-tests-for-bgp-communities-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e94508835f3c70ff46f91425b681a7e6f859769cee767551f077294828e33c8e", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/207-unit-tests-for-ntp-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3504064c2344478ace44a12fcd4bd8a1d1663dffa94a4cf21b2baec5809071c3", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/141-add-route-distinguisher-target-attributes-to-bgp-af-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "84c6e756a913a408c494ea576188c522e0e8854661198522406c35868d86ece6", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/183-unit-tests-for-bgp-af-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "05bf36ee34d1e7c42fa5a002d143a276ff906c1e2e23e35a13839af8599accf8", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/187-unit-tests-for-bgp-neighbors-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "370bc1025deed2c8ff1dfb133975d221bba4f40b8881a438bfefdddb472d8b9a", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/254-update-replace-methods.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "314d0fcc5f6ddfd8f5de37b9df220dbf86dcded0ecb7ff9fb7e9e2770bde8cd4", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/244-added-rt-delay-attribute-to-bgp-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0b23fc4a7f395f43d295fc60dc2d9203c6d6b6a25b07cc02c6353861456556ad", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/201-bgp-af-modify-vni-advertise-fix.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2342c0d30118bf95257c8b1889120d717ba02d1ac066fac244d117366b8caec5", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/222-unit-tests-for-facts-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "560a1f617c149644afb38c2e1cf75b32818907a1392f528073e03569c742e2da", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/148-dhcp-relay-unit-tests.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1a631b02aaf412fdd573609670d6c60c3c986c9a79d0dfb9b6e633e4cd51ac17", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/239-replaced-overridden-for-radius-server-resource-module.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "504b9a4723eb0deb6a324610e3abf012bc3c31ab512458dae200f3d346d71cee", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/113-change-ntp-module-name.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "409717d508bef1f049748409a5c72fdf98a2ef2714ef2576a39f008ac5564a1c", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.1.0/221-l2-interfaces-replaced-overridden-support.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "28203ff8c962f41793cd0db321fafb6aa4b2e8ed38f0f5b207fa1cd36ec9a56e", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.2.0", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.2.0/269-revert-aaa-breaking-changes.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c23c77698cd627620c741612f71b72f13f43dd592b438e1dc883b8b326129c36", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.2.0/270-galaxy-yml-netcommon-and-version-fixes.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dfce8e1ec5b130da0ed6354942be85a3532f5bc1d28ac2c284872e3323dfbf9f", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.2.0/v2.2.0_summary.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "20b675a4e33fea199634b3b8b1323bd02766a90d536fa2b201877dbdfd8f841c", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.4.0", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.4.0/v2.4.0_summary.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "164f388ec5778b9cae6a02fc8625e6ddb43813500768aef1b5e12a1222a754a5", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.4.0/322-docs-README-updates.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bc73da3eddf77cb1996c46f37f0f413c41015754f84eed2c7713ac3804b24e93", + "format": 1 + }, + { + "name": "changelogs/archive_fragments/2.4.0/321-requirements-update-meta-runtime-ansible-version.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6b828ac92681ac287b6c82845e5ad7249e6fc30a12474a856e58f905b8cf4307", + "format": 1 + }, + { "name": "changelogs/archive_fragments/2.0.0", "ftype": "dir", "chksum_type": null, @@ -2689,24 +5160,24 @@ "format": 1 }, { - "name": "changelogs/CHANGELOG.rst", + "name": "changelogs/config.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5c07bc10602bb49ad903484c5c58ecd1bedf2197a6f320da391592823b891cfd", + "chksum_sha256": "399e761b5051610f04bd2c4f9ebca480a3d6e8a2d82e8e60ef2211a825aaa682", "format": 1 }, { - "name": "changelogs/config.yaml", + "name": "changelogs/changelog.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "85862c6553f33777c8fd2f7fe3cfbe2d840deafc202b404052c6c829fb64495e", + "chksum_sha256": "aa104b57cecdbf13eafb305fc7abc66551fc3413da9c623effbda9ac2eaaa267", "format": 1 }, { - "name": "changelogs/changelog.yaml", + "name": "bindep.txt", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2ad2a65538878b7704f2cae149e38beebaf5bc92d3ff7d245b6dbd9ef4762877", + "chksum_sha256": "80645079eb025b3a905b4775ac545d080a3d7d35d537c31e04f7197c94315ab5", "format": 1 }, { @@ -2717,6 +5188,55 @@ "format": 1 }, { + "name": ".github/CODEOWNERS", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c6da2c1b1fdb7763d55fe592e9d55af7ac8b87b7ea896b57a4d28e0e66f44d4e", + "format": 1 + }, + { + "name": ".github/ISSUE_TEMPLATE", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".github/ISSUE_TEMPLATE/feature_request.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ffbee443a3f80c91eb59268069f8d01947cf0f1b8f45342026ad9224d2b998d2", + "format": 1 + }, + { + "name": ".github/ISSUE_TEMPLATE/bug_report.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "11cae2d85e28f56cb5ae8386e3167f75dbb2ea6f09cec0bb87fb6f5a112bf0a3", + "format": 1 + }, + { + "name": ".github/ISSUE_TEMPLATE/config.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "05905c8f244d51298bbd1778c286c8a6c9f7adf0d0e5a5f72f764d71ec82cc64", + "format": 1 + }, + { + "name": ".github/ISSUE_TEMPLATE/ask_a_question.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bc3938f48861224f03b9f2a71e63f3329e3161506451c14b911aa0ef1abc201a", + "format": 1 + }, + { + "name": ".github/PULL_REQUEST_TEMPLATE.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "67476fe5a2ebebe213883092cbc25295e0bc1a4f553d6a1805fe4f77d03c763b", + "format": 1 + }, + { "name": ".github/workflows", "ftype": "dir", "chksum_type": null, @@ -2727,7 +5247,14 @@ "name": ".github/workflows/ansible-test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0dc2fd66d9bb583a726caf8281f456be2130992b6f93d3155a7bc1a711b855fc", + "chksum_sha256": "14382f9d48e55607f50d5a1935a3dafad3aaca829a7542cda50ea2495e403eda", + "format": 1 + }, + { + "name": ".github/workflows/code-coverage.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3838cf11c8377683180dfaba3186e81d47715578f4bfa6f37de779dcc34b10c9", "format": 1 }, { @@ -2892,6 +5419,13 @@ "format": 1 }, { + "name": "playbooks/common_examples/mgmt_vrf_config.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "98796ef0a1de9da997b16eaad459d6f5408484928a356062e151fe315247c6e9", + "format": 1 + }, + { "name": "playbooks/common_examples/sonic_api.yaml", "ftype": "file", "chksum_type": "sha256", @@ -2906,6 +5440,13 @@ "format": 1 }, { + "name": "playbooks/common_examples/mgmt_vrf_off.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "84bf6e04674e215b9a7b24e45ddb53f46e1726a7b7c0d5317819251b95d55e0f", + "format": 1 + }, + { "name": "playbooks/common_examples/patch.txt", "ftype": "file", "chksum_type": "sha256", @@ -2913,6 +5454,13 @@ "format": 1 }, { + "name": "playbooks/common_examples/mgmt_vrf_on.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bb6d7f0c12f319a4070d208282d816ca7f58bdecfb8b1ca15a52cb65abaca6bf", + "format": 1 + }, + { "name": "plugins", "ftype": "dir", "chksum_type": null, @@ -2972,7 +5520,7 @@ "name": "plugins/module_utils/network/sonic/facts/vxlans/vxlans.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4c0ce8d53537f8d62d93cb6eab94f613269a9abb3fd6089a251344f29ace1581", + "chksum_sha256": "9c862191ebab05f72bc18cdfbc4d489873391fd9eaa3a46cfb8c28714b18f8f4", "format": 1 }, { @@ -2986,7 +5534,7 @@ "name": "plugins/module_utils/network/sonic/facts/l3_interfaces/l3_interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "175568e94084854f2f4338d76b8fe62871258d200dd422db6b77998a1b534c6b", + "chksum_sha256": "3c85acbaff4badf880e0cdbe5b03d9df7c07e79cb96e337d65eb2f6e813acf7f", "format": 1 }, { @@ -3000,7 +5548,7 @@ "name": "plugins/module_utils/network/sonic/facts/facts.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1ce8bd508d419b7a1c80302d56ca3b23b30f0dfaaca99fb5af06b27369077f6d", + "chksum_sha256": "6944f26cecc9afdba32f35ea4bdcc09bfd5a74776b1d5eb334e8c9dcd515c22d", "format": 1 }, { @@ -3011,6 +5559,48 @@ "format": 1 }, { + "name": "plugins/module_utils/network/sonic/facts/mac", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/mac/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/mac/mac.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c15d6e9c7984e7611729fbc059217440e98685e890fb8fe0401c599a01014864", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/dhcp_relay", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/dhcp_relay/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/dhcp_relay/dhcp_relay.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5e1375b8462f7377f5f0092672377c8e6f5599fb5f367730f60e39a9f3c5f927", + "format": 1 + }, + { "name": "plugins/module_utils/network/sonic/facts/users", "ftype": "dir", "chksum_type": null, @@ -3021,7 +5611,21 @@ "name": "plugins/module_utils/network/sonic/facts/users/users.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "aa4a5a009e2bd6b77a97c254e0f7f462fe556d08028902740e9707f8804bbd13", + "chksum_sha256": "2bbf09a24dff32142bc9f77aba5d71f7dd3f306708398af3b75d9a4cb1b62e9a", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/port_group", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/port_group/port_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4366c3540cdf6cd7aa5caf8ff2eed4b74d4c5c63be01bdb6829ebead0216c0ba", "format": 1 }, { @@ -3042,7 +5646,7 @@ "name": "plugins/module_utils/network/sonic/facts/aaa/aaa.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ef4b054752c855757ba2727a9838b5aa61cf88699765c9b842eecc9727048ff5", + "chksum_sha256": "d64144e0152dcc788ea4cb146f9c1bba788db714b6c89d6a40cb6a36edfdc1ab", "format": 1 }, { @@ -3063,7 +5667,28 @@ "name": "plugins/module_utils/network/sonic/facts/static_routes/static_routes.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "78d654467abcbf68095d79cb59c29ba6470ca4e5ac426a9595b7e3f37fc931ae", + "chksum_sha256": "7deeafd0ac091d6bfe972837242d884f6e3b867b0c67931011eb432dfa740c22", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/bfd", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/bfd/bfd.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c3cb8e8dbe2178894bf4fba9a857d01f645ee441f855d8fbda9b8315c2afafcd", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/bfd/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": 1 }, { @@ -3077,7 +5702,7 @@ "name": "plugins/module_utils/network/sonic/facts/port_breakout/port_breakout.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "494bdf331a5806f341f0125b6ba46ff888a935c4aff4d5b896f405764fc59a8d", + "chksum_sha256": "8cb702778fb2dac4785484630f9a02a5951fc6c75031669104a9dc50e5fddb3a", "format": 1 }, { @@ -3091,7 +5716,7 @@ "name": "plugins/module_utils/network/sonic/facts/vrfs/vrfs.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "7ed445c1fa06bfb70ed56605171012c5b5fd9c13b1f0cd3fb81af6214ddc3ae0", + "chksum_sha256": "17f84db9762528614e53caf903b7ca82ef0ac0a3e565a5539ba4aaf20030da38", "format": 1 }, { @@ -3112,7 +5737,28 @@ "name": "plugins/module_utils/network/sonic/facts/interfaces/interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d2eccbe6d74967f1198f8d1b5858a259a09850226ecc4d940d016be5e44b6e81", + "chksum_sha256": "37b7058bce770d9c628946a2133a340cea934ca5c2012cbd10ba17856cb68735", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/acl_interfaces", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/acl_interfaces/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/acl_interfaces/acl_interfaces.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b4ffb0de7a16618e7963992795aa35cbb4a5a0c1822eb7d2384aefaa433caf1d", "format": 1 }, { @@ -3133,7 +5779,7 @@ "name": "plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "41c01d93815ca5cbb62cd6ac2918041e5a67b792d11dfe291932f0f64b2ce4e1", + "chksum_sha256": "969f2079fc54fba0dc76eb226b29b1b2c1b18c5ff59520fd16cbb48463e474ac", "format": 1 }, { @@ -3154,7 +5800,28 @@ "name": "plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6322ce72c60c1098c7b04ba2c05dc2be5c70ff1760373f131005369995efa440", + "chksum_sha256": "d7e93b3c900c29ad02a24d359296ccbfc34fdc6b9e83a985e4f9b0eec049733b", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/route_maps", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/route_maps/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/route_maps/route_maps.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "55aa98fa6e781d27bc08360923649f9e26e3df85382dd93d21ad2e64752f5d98", "format": 1 }, { @@ -3175,7 +5842,7 @@ "name": "plugins/module_utils/network/sonic/facts/lag_interfaces/lag_interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "abc6c91a997dd3a42dcac331eb3d7b9b8ffc91fd2d80dc389752ef8e5d7e9d37", + "chksum_sha256": "feea671befcaccf560df99f816f6e4b71e507bf8b9d69417bbc4f0c61128b356", "format": 1 }, { @@ -3217,7 +5884,7 @@ "name": "plugins/module_utils/network/sonic/facts/bgp/bgp.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "48d0915a4d169eb7accb4d921fa2956c2019e7e757fd4747bea093d2c6637b4f", + "chksum_sha256": "cead6cef11f16a576d894cf9dd7f349bda5760ddb3edae7d9e9b3221315cf69b", "format": 1 }, { @@ -3238,7 +5905,28 @@ "name": "plugins/module_utils/network/sonic/facts/bgp_neighbors/bgp_neighbors.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d7daff29f60266f474a5ecb39569367146eee672c8bc7b5882cedd713d7c32ef", + "chksum_sha256": "2a9b1228cee27b451e6b4658e7412468550a3cae76687b4bcf1218ec32be6bcb", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/vlan_mapping", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/vlan_mapping/vlan_mapping.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "daf715775a622cfb63723d94c7693fb1c8113dbe6c2a4b0cd317737d1c0bcc40", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/vlan_mapping/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": 1 }, { @@ -3259,7 +5947,21 @@ "name": "plugins/module_utils/network/sonic/facts/ntp/ntp.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c8142bfefd6b7392123b7785a0c90ed43e6bf24ad4881fbd6d1df42c0c43fa27", + "chksum_sha256": "4ce5751969c185b75ee3060b20529d0c75c09ea03383f8db670fbc98153d9d93", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/logging", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/logging/logging.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "310fb9044fd61b3269adfabc4d95d510a362c69348bd39a571c048f1d71863a7", "format": 1 }, { @@ -3280,7 +5982,49 @@ "name": "plugins/module_utils/network/sonic/facts/system/system.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "7716880b9ab1847ad3a9a4c5f70fdfd7176c33853d4ee9b4711d94bcbf51cc29", + "chksum_sha256": "6e6988fabfec74823e24990bc41eaf2c2e6659967c5876d04c4a44e962e891d6", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/copp", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/copp/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/copp/copp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "47dc1005584c5da56b289d8d85e00db4198eebf640596986f40eeaad0092538d", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/l3_acls", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/l3_acls/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/l3_acls/l3_acls.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7ccbe0a9aef7b0f3fa0e09cca865cab8e49933a3e7ea9005ea6e7ee5bed042d9", "format": 1 }, { @@ -3301,7 +6045,28 @@ "name": "plugins/module_utils/network/sonic/facts/bgp_as_paths/bgp_as_paths.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "7f2c4b27f8142c32eaa6219d00b261145ba5990b73ee96f2113ce3b8714b44c2", + "chksum_sha256": "69777f5af2956b007c936f2a89cdea350d91686af68c5e9a15da6f51248de7ef", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/dhcp_snooping", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/dhcp_snooping/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/dhcp_snooping/dhcp_snooping.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "aecc4b601e9501b5a5f6a041ec61f668d6a6fb3debfcb9f2023282c2f3c150e2", "format": 1 }, { @@ -3322,7 +6087,28 @@ "name": "plugins/module_utils/network/sonic/facts/vlans/vlans.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "571ecaf2bef5d15c91f39a0b22922282757d9b32305d4221e9967815bc195a39", + "chksum_sha256": "9757329e83f21738a279cbdb34b04deecc29a2372186d82d88ecf70948613612", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/pki", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/pki/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/pki/pki.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "65e4c4aab7c87ada7baf0603092cbc0ce66a7bee5b7921c8a182dff9b4b1207f", "format": 1 }, { @@ -3336,7 +6122,7 @@ "name": "plugins/module_utils/network/sonic/facts/tacacs_server/tacacs_server.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f8c0dc71948a994b45b2ab55601891f26665cc538b23a01e55f8f947afc859bd", + "chksum_sha256": "9580d7d41d7edcb682e053789f2e2f47f9c9cbf6c9f25a93c67f03e2cb3130a6", "format": 1 }, { @@ -3350,7 +6136,7 @@ "name": "plugins/module_utils/network/sonic/facts/mclag/mclag.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "cd027811ad981ae9b946f7247b648baccab1cbc9b877784ddc9cdb578fe68cc3", + "chksum_sha256": "94c2015c4b30bf3d07617687419e189eccda35c5919454806f872eb15986a0e1", "format": 1 }, { @@ -3371,7 +6157,21 @@ "name": "plugins/module_utils/network/sonic/facts/bgp_af/bgp_af.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "58292688b6f354c7e19bfc59bdcc91f76fc68cdf7ce4e0968c36ba0fde3e7334", + "chksum_sha256": "e67c382ea7b79cdb4204d2b5b6febea030b8d00951a571d1ba7e539d7a84af4d", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/ip_neighbor", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/ip_neighbor/ip_neighbor.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "75c71dc60e69d40cace6257acdcf91c713e9d69f1a9d8721609dc8a739ed9ff7", "format": 1 }, { @@ -3385,7 +6185,7 @@ "name": "plugins/module_utils/network/sonic/facts/l2_interfaces/l2_interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "58fb25ed857a13d6af43a2609b90f09ac135694ce44997fd428242815e145b5f", + "chksum_sha256": "1484ae89f8a19134c1c397d6281d9d0e9765a7013b3ac9eb64bc74cfb5acdfd6", "format": 1 }, { @@ -3399,7 +6199,49 @@ "name": "plugins/module_utils/network/sonic/facts/radius_server/radius_server.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a3f05a591387d9c24cb7dc066621ac75229b9fb5f871e165546daea9fa2d6a87", + "chksum_sha256": "d87aba748530b8484b855bb3022d6d0bdc94e02c83ed546ef339631f1c63989d", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/lldp_global", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/lldp_global/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/lldp_global/lldp_global.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c489b523367dc46bc3dccfc9a44effbca57b4781714c57f823d1efcef8139411", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/l2_acls", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/l2_acls/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/l2_acls/l2_acls.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d4def1d2f7d9301f2eee96f109cdbbfee2cdf9d12c7c18bc6f1856f2a8f576ff", "format": 1 }, { @@ -3420,7 +6262,28 @@ "name": "plugins/module_utils/network/sonic/facts/bgp_neighbors_af/bgp_neighbors_af.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "35667a83682027b3b6051ab7729a43847de0092318b8645a4910b9b2ac003d39", + "chksum_sha256": "4d2b35a3c7694851868e776b829113d94c9e807ce393e3ab4ec41bba82d47bbb", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/stp", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/stp/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/facts/stp/stp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "34c1e2f946f6486080bd39e48598a1173215f7766eea1d1dcd5dcd5f18ef5131", "format": 1 }, { @@ -3448,7 +6311,7 @@ "name": "plugins/module_utils/network/sonic/argspec/facts/facts.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a4e95f45024b8de1d0898d87a33fa01311168b82f0de6ec7efa012b927f3fe08", + "chksum_sha256": "8340af43f5a7bef0aa242a2bc355c9352110450146acc6db9db89484f22556ae", "format": 1 }, { @@ -3476,7 +6339,7 @@ "name": "plugins/module_utils/network/sonic/argspec/vxlans/vxlans.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "de5a9c713994b11f97572c67af335fef9ef883c5ae28bbe8b3d29509f1479708", + "chksum_sha256": "9955ea45cd45213636339519cc74f807f10dbeb0f6a9191750a7ef4a684fe992", "format": 1 }, { @@ -3490,7 +6353,7 @@ "name": "plugins/module_utils/network/sonic/argspec/l3_interfaces/l3_interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ea892c0cb638d806819a81b730c8bf272d1d22f5a785feab6bb7899d5d8290f5", + "chksum_sha256": "c1282954f966ff09c49902e13f4bb40020baa893605b8b80e88ef1a9cbee2b0c", "format": 1 }, { @@ -3508,6 +6371,48 @@ "format": 1 }, { + "name": "plugins/module_utils/network/sonic/argspec/mac", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/mac/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/mac/mac.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d5dc85318a649550e94af47943ae3256b6243f1291feb47f41c809c26dd47e9f", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/dhcp_relay", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/dhcp_relay/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/dhcp_relay/dhcp_relay.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "90249eaea49f761822bb5aea2fa18fbfb6da80111c220d1715597dc6ca72e814", + "format": 1 + }, + { "name": "plugins/module_utils/network/sonic/argspec/users", "ftype": "dir", "chksum_type": null, @@ -3518,7 +6423,28 @@ "name": "plugins/module_utils/network/sonic/argspec/users/users.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e7016795dc961f47c1363b62c733419ed2f45bb51083b8c13a9cdcdc78393b80", + "chksum_sha256": "a85cc07e6862626bbcce6606eadce7d06cda92451c04de84099aa7cbf3464f2d", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/port_group", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/port_group/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/port_group/port_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f80a2047cd7e0f4d1f25768caafc926fe70c75dc0ab703743705bed4aa19ec2d", "format": 1 }, { @@ -3539,7 +6465,7 @@ "name": "plugins/module_utils/network/sonic/argspec/aaa/aaa.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "099cb25564ace85aff3f9b665f0e8291cf30cc566393aa73fe7fd7495adb75b5", + "chksum_sha256": "61219ac1fa1c852ed1662fcea00fd733097880ae437479ec83fa38b702fcb21e", "format": 1 }, { @@ -3560,7 +6486,28 @@ "name": "plugins/module_utils/network/sonic/argspec/static_routes/static_routes.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "664fa1bfe5e35df445395c8d97c3664d61b3616e46a1e566dfae8dffe5d3cd51", + "chksum_sha256": "527115990932ccc7e3d8e52491340b29d092ba91833b82a173dd812d58ee292a", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/bfd", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/bfd/bfd.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a03413305d8eb78e576fbff71f7aad402c9138ec077d29510d0b006b527b9af2", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/bfd/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": 1 }, { @@ -3574,7 +6521,7 @@ "name": "plugins/module_utils/network/sonic/argspec/port_breakout/port_breakout.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ed14043c2d3655852bddd40ee00d07bb3731e109d738a8df15cdfe8c859dfcc3", + "chksum_sha256": "386ae329105705ac5076412c975ef753154cdfc4176344f583d2b2623f0b72a3", "format": 1 }, { @@ -3588,7 +6535,7 @@ "name": "plugins/module_utils/network/sonic/argspec/vrfs/vrfs.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ac08fa8db8afd66c4a6856700ca38615bcf8b8fba6d712981493f5452ea61c62", + "chksum_sha256": "e7397f371379fa70a1f68e0a9de6875a894264b3bfc48bc04a0eb41d908c2214", "format": 1 }, { @@ -3609,7 +6556,28 @@ "name": "plugins/module_utils/network/sonic/argspec/interfaces/interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9858adae2a8083270cc1d513b77fce9fe93c9789c5968c8e843b843ed940bfab", + "chksum_sha256": "04e92b4c1ceb5c318e76a933dd5c08a7c05a009d4a40038d076e43169c70d56e", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/acl_interfaces", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/acl_interfaces/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/acl_interfaces/acl_interfaces.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7b5050121018f557f0d1b8f6bc81a99005be8dbec77c27fc8258bc82d57023a4", "format": 1 }, { @@ -3630,7 +6598,7 @@ "name": "plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "64e16da1a2cc3af929d265a73536e9ce637861ecd8723c0043bc7536ca9acbba", + "chksum_sha256": "c1c69b72962c5030db0e623ffe66d51db1b0456cb87b8c1dd1815b011c58f650", "format": 1 }, { @@ -3651,7 +6619,21 @@ "name": "plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "880c709b98414d38453eadce51b2771ed5d6c8bdd3fb6cbde45b60191e4568f9", + "chksum_sha256": "e6ac6392af89a494fffb2c9af04d5f1441fbf3e0f2d15bb9caeadea480ada9ad", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/route_maps", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/route_maps/route_maps.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4d8538cc2bd9211274678c40cfc2f86a39e41fbd5710d1a89af8124bacdf1e04", "format": 1 }, { @@ -3672,7 +6654,7 @@ "name": "plugins/module_utils/network/sonic/argspec/lag_interfaces/lag_interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "980ce16a72689da30138007e45e755e9f32b020769e884b8b856505acf8d9863", + "chksum_sha256": "fa023f3538e7a1238bbd9fb04648264040b6581a70f4e6b842e6a63724a799c9", "format": 1 }, { @@ -3693,7 +6675,7 @@ "name": "plugins/module_utils/network/sonic/argspec/prefix_lists/prefix_lists.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3047173ed7946102928cd2b23bfd7117ad4574d86f2a6330bb5a74ffaccd6368", + "chksum_sha256": "1fd06fe051d5e2a5adf9385c8fe1333cbd9cb953734017b435b0b5a98f48493c", "format": 1 }, { @@ -3714,7 +6696,7 @@ "name": "plugins/module_utils/network/sonic/argspec/bgp/bgp.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "28692ff70358779f277a4bb9f5c2dc2b9b53ab4288b2102e01ae5394c4a12cc9", + "chksum_sha256": "e04590a9c664009fb627e95b787f245b873db928bb14ae9556579c8f60a53422", "format": 1 }, { @@ -3739,6 +6721,27 @@ "format": 1 }, { + "name": "plugins/module_utils/network/sonic/argspec/vlan_mapping", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/vlan_mapping/vlan_mapping.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bd0e8cb34175ac5eeede2e26a1f4fe186da14e0881c0a7c40865a45c63c67756", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/vlan_mapping/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { "name": "plugins/module_utils/network/sonic/argspec/ntp", "ftype": "dir", "chksum_type": null, @@ -3749,7 +6752,21 @@ "name": "plugins/module_utils/network/sonic/argspec/ntp/ntp.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "027ba50a79f0347df9702030f8849258711e516df33da4b8949b51cad88d3c86", + "chksum_sha256": "bbed7abf20c5f65bb31829cce51784ef04e7049a59ceb7f1236fc00065e033b0", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/logging", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/logging/logging.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6058f4857729da1dacd9312b9fa2b494961d1cd6b5866b6760097194c4eaebb5", "format": 1 }, { @@ -3770,7 +6787,49 @@ "name": "plugins/module_utils/network/sonic/argspec/system/system.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "53707e1f9e6d035be3514ea21d61a0fec4a0dd75ebeb4e44a803f63a3020be7d", + "chksum_sha256": "e4a2559f9e125a53cbef71dac79bfa891122493192933d1c84087f3fdbe86450", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/copp", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/copp/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/copp/copp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d95c938992425142ee78d568dda0a91eb349364dd807e5b2facc379af81401f6", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/l3_acls", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/l3_acls/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/l3_acls/l3_acls.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1525d60212ad72a57ce568d4ef13a274a471fbaf9649decd7f85f7b096e6b8dd", "format": 1 }, { @@ -3791,7 +6850,28 @@ "name": "plugins/module_utils/network/sonic/argspec/bgp_as_paths/bgp_as_paths.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d9ccf45561ffa483c765a8c0ce00a2818e271c43134f9c46b3dfb3aee80f12c7", + "chksum_sha256": "ef546a5a09f39e7f062bcee63e92dc5e410d8a9ee5ef26daa89d2ff95c3aa9af", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/dhcp_snooping", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/dhcp_snooping/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/dhcp_snooping/dhcp_snooping.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9c9d94c9f0341098db28a0bf7a2c9de85ff0ab5b291dc07c3be0e74700cfcd87", "format": 1 }, { @@ -3812,7 +6892,28 @@ "name": "plugins/module_utils/network/sonic/argspec/vlans/vlans.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c0f599c09eaf915361ca18d5898de652abea87835ababe1ac726f8367a6fabd5", + "chksum_sha256": "dd437ac0c25fcb80fe78be467e598879a88fa150e5ac0c582c5db3116bce0220", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/pki", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/pki/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/pki/pki.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2803b45f839e95fae01b43efa67977bd0516fc47153e7026375de2451cb254dd", "format": 1 }, { @@ -3826,7 +6927,7 @@ "name": "plugins/module_utils/network/sonic/argspec/tacacs_server/tacacs_server.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4dcd58e947b120be713fa3f571f6c5485cab6bdfe7efc75a4e90cec626a46a71", + "chksum_sha256": "a23ba33a273db878d89e3d2d10f406843358ec206abc9a15e5aed34db2b5f5c7", "format": 1 }, { @@ -3840,7 +6941,7 @@ "name": "plugins/module_utils/network/sonic/argspec/mclag/mclag.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "df57beee6331242c7c1cced8fea388ec78291d8ce2b32c20c5e0e50738bfb9f2", + "chksum_sha256": "ad32353a4503f116dd8ff78a666d65f5070d453f1828f583e78112e9d9aa3fab", "format": 1 }, { @@ -3861,7 +6962,21 @@ "name": "plugins/module_utils/network/sonic/argspec/bgp_af/bgp_af.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "cb90a1d43f0acc3ab981e9579533505e4c23fc7d5c045b3266e13ad41b4606fa", + "chksum_sha256": "908f8cb10b2c81c70b45dd29dfb339410324fae2814b6a9a051bcbcbaa05ba42", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/ip_neighbor", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/ip_neighbor/ip_neighbor.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fc1ba714079549b79bac9d0551fc24f8376384f404e83d9d4ee05ab8e1ed174e", "format": 1 }, { @@ -3875,7 +6990,7 @@ "name": "plugins/module_utils/network/sonic/argspec/l2_interfaces/l2_interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3762f248b6f01066931467b74ffbe37b8d8c535fc97fe571f6d01acb1669f74c", + "chksum_sha256": "84ed10ad236c601747b93503e435619cbd429de6d2a6ab85401c5003c91146a6", "format": 1 }, { @@ -3889,7 +7004,49 @@ "name": "plugins/module_utils/network/sonic/argspec/radius_server/radius_server.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "00a2640dd2eacd73a6ee61c7f00f913964e9a3072f0550682f6d4732ff119c58", + "chksum_sha256": "4786969ad3843ff6b0844b3ea0410cb91dcd8eb8da3c25355afc857d21b75652", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/lldp_global", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/lldp_global/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/lldp_global/lldp_global.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "899b1767cd75963775ab4d216182ed85ac8cb538308e20bfcf988edc4cc4e120", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/l2_acls", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/l2_acls/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/l2_acls/l2_acls.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "91cdd1aae4371f0ea68895b40028e46b3c65f1fa97d61b1ef7c0532a33789350", "format": 1 }, { @@ -3914,6 +7071,27 @@ "format": 1 }, { + "name": "plugins/module_utils/network/sonic/argspec/stp", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/stp/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/argspec/stp/stp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0d058343ea2c3cce3fe58078701f66cebbe91944cc9402d37ca7bc31b98542da", + "format": 1 + }, + { "name": "plugins/module_utils/network/sonic/utils", "ftype": "dir", "chksum_type": null, @@ -3924,7 +7102,7 @@ "name": "plugins/module_utils/network/sonic/utils/bgp_utils.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "efbf8c72fe520e860020fceef08d8e3a9c3aaa10fb3601999d96d604aceb09a7", + "chksum_sha256": "9ee1ba4d06a035bbbd92dc54ba63342bd0173ef2af71a7a285255477c7869001", "format": 1 }, { @@ -3938,21 +7116,28 @@ "name": "plugins/module_utils/network/sonic/utils/interfaces_util.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "356468c4f1fdb40153c2b220ddb2c5441feecd4a177067aef748edc824feaa1a", + "chksum_sha256": "50ede8649f0a1bc79a6fcb45eab44c4d1f5bd5c69940a16b770de53057829005", "format": 1 }, { "name": "plugins/module_utils/network/sonic/utils/utils.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6b6f8897c68aaff47fd0bc497e5d99bbf71d5a78837c000f2b0d4ae719c44300", + "chksum_sha256": "c45e4e5b1e4c04a993faacb702adb5ee9e64aecc6ce55714b0233d9b9620e5ff", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/utils/formatted_diff_utils.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "158be0d32a29c7cd89f0643774252798f9890a90871e05183c1b8d0f235ccab1", "format": 1 }, { "name": "plugins/module_utils/network/sonic/sonic.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2852d2baa75d3b3a9260f156c500738eb2d37ef2861a6d63e20290c93bc7c8c1", + "chksum_sha256": "4a709417d10692943de02955526e60f2171c92d13005ed7612848881ff933e67", "format": 1 }, { @@ -3973,7 +7158,7 @@ "name": "plugins/module_utils/network/sonic/config/vxlans/vxlans.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "751480b64c2188fb97cc3eabde60a7d328fc780bb82dac9dbb215ce75da58b3e", + "chksum_sha256": "5acba99c206f0873f313c05eb3a27c346c11a766457b4aef26eefaff9f1ab78e", "format": 1 }, { @@ -3987,7 +7172,7 @@ "name": "plugins/module_utils/network/sonic/config/l3_interfaces/l3_interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "79d2b8680dc3f023e536ab624ad00c3b36c2d592a22f9cde98ffa66bc95fa2c8", + "chksum_sha256": "28a9de1ab8d6aba8d77836348ebb01c13ab61a95d72986736586118b49703275", "format": 1 }, { @@ -3998,6 +7183,48 @@ "format": 1 }, { + "name": "plugins/module_utils/network/sonic/config/mac", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/mac/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/mac/mac.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "22484d36e29561f186b66c6b957ff8cfc6a7200026ee1cb04252d1ad41baf8ca", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/dhcp_relay", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/dhcp_relay/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/dhcp_relay/dhcp_relay.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c89f8fc65d0bae28e0d3d384ad0a73cfbbdca5865d8fde6cd241b5617bb818ac", + "format": 1 + }, + { "name": "plugins/module_utils/network/sonic/config/users", "ftype": "dir", "chksum_type": null, @@ -4008,7 +7235,21 @@ "name": "plugins/module_utils/network/sonic/config/users/users.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "45344f3b1a07ad06af3e0cda474f78bebde079b4a929d27a9e99a888aa3231fc", + "chksum_sha256": "73568a1acf800cb2ac37b78efac670468252e90dfb27a473ada8988e9dad8afa", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/port_group", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/port_group/port_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1800f3d4804abbb6af1e4ee6fa85eacce80c67f6f512f13f8a3eba8c466381b0", "format": 1 }, { @@ -4029,7 +7270,7 @@ "name": "plugins/module_utils/network/sonic/config/aaa/aaa.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eed3f6afdc941372f55212449b300bdfc5fa0150eb5068dd98848133ae8eb7ff", + "chksum_sha256": "0c62cfcf15301239d3606d505b5cdebb06480becb236a2cfa42d18e67857141d", "format": 1 }, { @@ -4050,7 +7291,28 @@ "name": "plugins/module_utils/network/sonic/config/static_routes/static_routes.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "146d536b99180f3f99e0c0e9c0d0b97487b2e1d6b736c85e448bb17b6dbe96f5", + "chksum_sha256": "57e15a7aa859388fdaec5d19f511cfddc791dc7aeae397c3fef392930d6a2062", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/bfd", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/bfd/bfd.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "752c743958b770fb88377181d4ef2eead55047f8bd6c4909067d968a693a5aa8", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/bfd/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": 1 }, { @@ -4064,7 +7326,7 @@ "name": "plugins/module_utils/network/sonic/config/port_breakout/port_breakout.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1b925177c3c5e66b32066d1c16d45c73878da5e19f7f14d309aeabebe90ef8c8", + "chksum_sha256": "6edbaeae363836fc6bc19fff0623260e7f1b288461bcea41c462717247d412d2", "format": 1 }, { @@ -4078,7 +7340,7 @@ "name": "plugins/module_utils/network/sonic/config/vrfs/vrfs.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "121221ad6e55fffad0b00ca0a5e7cd58b35a4e7a575481dfd074c6b41411ad31", + "chksum_sha256": "19dacdae548ee992bf9cd87a2e2c77cba13461c338b547bb79e1fe2fa4cce433", "format": 1 }, { @@ -4092,7 +7354,28 @@ "name": "plugins/module_utils/network/sonic/config/interfaces/interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e4289ea78fef1e900174cf355a7093f5ea144573abdf7bcab63879430d8a1d3d", + "chksum_sha256": "7b4be292490ab0b82514f040e6cec06914304ba87d0341a67b7f006faa719e8f", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/acl_interfaces", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/acl_interfaces/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/acl_interfaces/acl_interfaces.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "882b2ebf820225a6f40112d845d5bedc2b83997ad284f9eae66552f2d846f149", "format": 1 }, { @@ -4113,7 +7396,7 @@ "name": "plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "873dc9442732042397ac3346598dbfc8040eff5b388a6a6c5462d156ea6c999e", + "chksum_sha256": "25bb8b114f354df51dadddec84f09ba4c1b7bfa3bd02d22bb98d7e397098ffb2", "format": 1 }, { @@ -4134,7 +7417,21 @@ "name": "plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a66f6fa13607ce0aae48f7ec944cc7410fd2443efa696fc59d254803402aea99", + "chksum_sha256": "47674b96bcd45f570cd925d51dc814dd08b95ccc7b43c3e7498bd2d9450398d7", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/route_maps", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/route_maps/route_maps.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3a2b4dfd291ab4349cc77ab7e54f508aff0ded37ea1a95d11ff57c92ab345400", "format": 1 }, { @@ -4148,7 +7445,7 @@ "name": "plugins/module_utils/network/sonic/config/lag_interfaces/lag_interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "42ea3ce2e2c15ec958f3072986def9bb6bbeee7e523a54e0cde7923ab7e855cb", + "chksum_sha256": "c7b84fd62451f70102d4d0d19bb6f555246fc9533899e2778ceb363c24f79834", "format": 1 }, { @@ -4169,7 +7466,7 @@ "name": "plugins/module_utils/network/sonic/config/prefix_lists/prefix_lists.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "fad647ed365fd0991abcb3f70efb94c443dc8ab0fe19751690cfb5b529467763", + "chksum_sha256": "a9c9ca5d1921e85c5cb443950d75c5d5fa9089e4624a23205fd35b5d205bf689", "format": 1 }, { @@ -4183,7 +7480,7 @@ "name": "plugins/module_utils/network/sonic/config/bgp/bgp.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "294427bd5c538c82eca5212bcfb57967230aa64892c5e2073ad12bf889381a97", + "chksum_sha256": "66a2fd56a5dd1d1793ba1b8c3c8da0e945680565911c957227a77a3532aa6e87", "format": 1 }, { @@ -4197,7 +7494,28 @@ "name": "plugins/module_utils/network/sonic/config/bgp_neighbors/bgp_neighbors.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f94c033887e9c1065c0a8155bb7e6f36888e2e66c7e475d86400dc87660a6489", + "chksum_sha256": "3fe330259fd9ea780794cdae8b6c5fa7505416d3909c22b0efc27f5e4ed3498e", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/vlan_mapping", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/vlan_mapping/vlan_mapping.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8ba00006e25c72c156c1d6c0276f592550e5361d4924175abb5ba6be6980bf6b", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/vlan_mapping/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": 1 }, { @@ -4211,7 +7529,21 @@ "name": "plugins/module_utils/network/sonic/config/ntp/ntp.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a19fdad43d518a26619fc9fb39a2d6ef3362cd1cc89252d74f69dc760ed815b3", + "chksum_sha256": "957a4e72b376a79d51305ad90fae0166e40fff23bdca5ec45ab26b04c8b7bc24", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/logging", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/logging/logging.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0580847184f421c94d6e2fdcbe5465959961dfbe78f71df74d7bce993d962043", "format": 1 }, { @@ -4232,7 +7564,49 @@ "name": "plugins/module_utils/network/sonic/config/system/system.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4a59efae25b1106a79087ecdfcb268fe2c6566ea61af795cce9d806fbe9f89e8", + "chksum_sha256": "e363bca18f96b07836ecd9d8790978da2c766235f11f713732348871b968dffc", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/copp", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/copp/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/copp/copp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0a13cc33ec860d16e53cf73aadec658c8d07a430134d038112d2f195c2f7cd83", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/l3_acls", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/l3_acls/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/l3_acls/l3_acls.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a2441d50c4fe032a7ba192f808da64aae49bdf2aa17ce88289dd15e9c4f5f6f2", "format": 1 }, { @@ -4246,7 +7620,28 @@ "name": "plugins/module_utils/network/sonic/config/bgp_as_paths/bgp_as_paths.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "7fff98321324ac64feb8edc5f8042f16bd4fa145c36781d53e86978d6edd9637", + "chksum_sha256": "c0b8e152a86919f3d014b9df29b11ef358c1a5fd7bd6b19e33f3de91a82278de", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/dhcp_snooping", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/dhcp_snooping/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/dhcp_snooping/dhcp_snooping.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f0aad3f0c466a28f653bbd75cbbaee5d18fc61b75da7eb2039fb1fe956d005f2", "format": 1 }, { @@ -4260,7 +7655,28 @@ "name": "plugins/module_utils/network/sonic/config/vlans/vlans.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0f26c8eeb4cd0b4de7fd058ef2a7d1e7f728cd5d4acb0f78f19d5ea729569255", + "chksum_sha256": "b8744022a7dd6c9d05577421b758d0ecc40710e9ba11611139b7a7b4903e8836", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/pki", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/pki/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/pki/pki.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f86f3ddf782c183cbacece171fe026aaf2b2c46ba32b428424f3b81cc584fd63", "format": 1 }, { @@ -4274,7 +7690,7 @@ "name": "plugins/module_utils/network/sonic/config/tacacs_server/tacacs_server.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6019047cfa54495d68c7ddca3b1b39bdb90d0e8854dd7835d8a0ad31fe55226a", + "chksum_sha256": "7770859ec20edd871b4c60ddde0cf2f2e2a1085e3ac67d119bddcd53f0a88468", "format": 1 }, { @@ -4288,7 +7704,7 @@ "name": "plugins/module_utils/network/sonic/config/mclag/mclag.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8cb43549024ce183291b1dbfd8512ee25d9401a9413e631457cdc1852dbdf98e", + "chksum_sha256": "4c39a47929eebf90ad31a8376fa5b15575a7c41a9a9d6360ffaa420637b26780", "format": 1 }, { @@ -4302,7 +7718,21 @@ "name": "plugins/module_utils/network/sonic/config/bgp_af/bgp_af.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "bd007d97137f9f87e82a2e3b3e545cdc811908ff7bf3ae142d304b0732ddc8db", + "chksum_sha256": "70c75fe20a4b0bb23e6e91b9faac5651e36aec58810916e3e174c9d98e367798", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/ip_neighbor", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/ip_neighbor/ip_neighbor.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "38e8afceb0c3efbe724a8961cc4e4ebdc28e5cac0c63dad450aa3243f1bae6d8", "format": 1 }, { @@ -4316,7 +7746,7 @@ "name": "plugins/module_utils/network/sonic/config/l2_interfaces/l2_interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "cbb277af89e7ebe04824f8c6c31cbfba3882f67a084861507153a2ef3c50c01c", + "chksum_sha256": "fe8e9eea28fb241490c3cdc4847e2f72c800d170a1f298e8611fd8d948127467", "format": 1 }, { @@ -4330,7 +7760,49 @@ "name": "plugins/module_utils/network/sonic/config/radius_server/radius_server.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "76ad9bce200ded760e69515a4694b1dbbd32347a2257f5b2841cd64644d48724", + "chksum_sha256": "302d4e7347100ab38525b96cd18381999c38f88d0c4c36edf60dc22f04cc84fe", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/lldp_global", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/lldp_global/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/lldp_global/lldp_global.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "de1f768f1b6e97d90a9a4e24d1d1778aa108266875e817ab232abad7d2d90bb5", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/l2_acls", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/l2_acls/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/l2_acls/l2_acls.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9031167a9874efa11bee2d7fe0c108e9f4834793a29ab60740f7978946d52e2d", "format": 1 }, { @@ -4344,7 +7816,28 @@ "name": "plugins/module_utils/network/sonic/config/bgp_neighbors_af/bgp_neighbors_af.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b34c814844873084b563d07af9334c6ee2d5cb11ae805a05c4a039c08c1d03dc", + "chksum_sha256": "bbd063fb2329be4503a1beb8dde1dbd0ea07c3fbb6a4c7a18abe56597985f268", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/stp", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/stp/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/network/sonic/config/stp/stp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e7ff773040559ebfcd0f5b73220238a7e186398d53f97c112aa6b962251a84ea", "format": 1 }, { @@ -4414,7 +7907,7 @@ "name": "plugins/cliconf/sonic.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4fbeb05a4a01583778cad49752c4fa8f3562926b4f7cbb399700aa0f59b1168b", + "chksum_sha256": "447bdc617826b39f3d11e2852897c79ae4db64648bfe509e45a9fd87b53af23d", "format": 1 }, { @@ -4439,10 +7932,17 @@ "format": 1 }, { + "name": "plugins/modules/sonic_l3_acls.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1801d73137632f113b989fc101c18b29e5a60af4f99c52316c0aebca52cd3d11", + "format": 1 + }, + { "name": "plugins/modules/sonic_vrfs.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f65a8f6e26c21af62aca63db39f12c28226e173706e2475a336f5774c9d0fd6e", + "chksum_sha256": "d09b615234f23dd67d53ee486a99943c0d81b357c253456704c5b647742ca5c6", "format": 1 }, { @@ -4456,175 +7956,280 @@ "name": "plugins/modules/sonic_tacacs_server.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a610ddc06cd5a8088ed2eae495f4e99f2adbbf3242aac9503b82317ee7dc1212", + "chksum_sha256": "da42242527f9dab3237e657359546769bf3eb7cb83264c660a261bdb7026a113", "format": 1 }, { "name": "plugins/modules/sonic_radius_server.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a64fe7ce6837f85d5ebdc5772ab8649ff4e07ee79c2ff56bf29dec4286cc260a", + "chksum_sha256": "a11a15204f50cb1710ebaf49d4025e8bcd3c8d5a4a3962942b840609ee1a304e", + "format": 1 + }, + { + "name": "plugins/modules/sonic_mac.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bfc9845da37d8a01bd27175ad93de2ec01b8a1d05dba6997ba29d3d13d95a43b", + "format": 1 + }, + { + "name": "plugins/modules/sonic_port_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "eb803b34c21eeacde6c3fe314b83fddba8610926da654de45240fb25a109be4e", + "format": 1 + }, + { + "name": "plugins/modules/sonic_ip_neighbor.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "accf797e421239f33b701a2d2ae7818f46c26a7e4428198ea187cc8cca15f385", + "format": 1 + }, + { + "name": "plugins/modules/sonic_lldp_global.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "19b5edffcc6d176407e74f3d8ea281a1acd267e468c52439e9f14585b2f56358", "format": 1 }, { "name": "plugins/modules/sonic_vlans.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0820305b7619cfc6080ec69893ea8093bef0224f7078833a9da4ee139e901257", + "chksum_sha256": "837f8d60d3141bb8a7489cec95401b04111e246bf1502d3900c1951c6ea25154", "format": 1 }, { "name": "plugins/modules/sonic_aaa.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c2a2be0a9b65b42e19f099adf045b875961c5876d7ef17457828bf1c7ffd2833", + "chksum_sha256": "5e0367da89779b1fd77ede17f153e486059653537afc129399c4633b78bc5301", + "format": 1 + }, + { + "name": "plugins/modules/sonic_copp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dd729289ef0441c7a6709f1daf3f8d0a9a76a28fdec8ceaa3b582b2bb7c370ae", "format": 1 }, { "name": "plugins/modules/sonic_l2_interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d1ee00011322c29a0936114c42775a91932c6b46605109a0c7f53911018f3e85", + "chksum_sha256": "329e5654e34c2febab2bf3624345eff6a310e1d9fae1d5e849dc0d5c2695bf68", "format": 1 }, { "name": "plugins/modules/sonic_ntp.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "92cfdb9b5e31bddf0ef61f01b255e1f4754c2fe6a1ce47571202ef3155b69d51", + "chksum_sha256": "9eee6712594bb51a088afd81b0cad244909b6bb78110e1f462ac2357f55392fd", + "format": 1 + }, + { + "name": "plugins/modules/sonic_dhcp_relay.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0823a8ddd0955e548a6d0bec452ef041d2611bc20f7a22d3e2be5327dd6f4894", + "format": 1 + }, + { + "name": "plugins/modules/sonic_dhcp_snooping.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "77bb3d3e35a46aaf35980a2ca27d30832de6ba7543f5f71212a461518654eb02", "format": 1 }, { "name": "plugins/modules/sonic_mclag.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e55c254e12c77ea06035f9cdd87a4248e89f7cd9d0df068266e7226c78d5fd20", + "chksum_sha256": "2923c5a306e5251584038af5c0a58e8bcf8ef5230e4ec02e944b9c04902b729c", "format": 1 }, { "name": "plugins/modules/sonic_bgp.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5e531d9a1f281831911dba58b4e5568bea27ae13b5a46119fd0a50c234d80a4f", + "chksum_sha256": "0c7da2ba338a8be6b4512f9a9fbcc9443da6f55b8322433682ed37b07c905028", "format": 1 }, { "name": "plugins/modules/sonic_l3_interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b9c8607ca719658fa1a5f4bf20e1bf0b71f06618664b56aa3f3f771c0416d535", + "chksum_sha256": "87a3d765bb9407e43b633b1deeef5c4c3634cc249ee2235e14b16f01a100aefa", + "format": 1 + }, + { + "name": "plugins/modules/sonic_acl_interfaces.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6ff78ada1d10b818c3ae8e9c83823fa0fa42e05faae2ec8c3929244b78397390", "format": 1 }, { "name": "plugins/modules/sonic_system.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "daf0edd6622a20743c5c9f51d4c4a7732389719390f512885cd3af012e65ad92", + "chksum_sha256": "0266c47a530ddcc434cc10d31b1692eb9b874e4dd77d60366d2d632fc996359c", + "format": 1 + }, + { + "name": "plugins/modules/sonic_vlan_mapping.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e818b7fa043182c6f0052f95db41547ffa02be6fa8aaabb998a09126cdb87ed0", "format": 1 }, { "name": "plugins/modules/sonic_users.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1104943f68ff99954e8481b915fefb206af930a9c6d6edc79f43aee233ea1db5", + "chksum_sha256": "ebe26def6331fe488be1c7431f398c804cd75c473426a19c6e73a01ef4b16091", + "format": 1 + }, + { + "name": "plugins/modules/sonic_l2_acls.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8cee67cd1d9445e86ebc2462a00d2011511e9267958949b392403da720c3fc18", "format": 1 }, { "name": "plugins/modules/sonic_bgp_neighbors.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "417b1a3e3b325ffbef1b84db13dedef88c9e935d441d3b2f1cd8ed79eaa933bd", + "chksum_sha256": "63681f4185bd7aa6954bf4b8aa8c87d1d2824276c27f1ee5a7ff2e296178a8df", "format": 1 }, { "name": "plugins/modules/sonic_bgp_af.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "869e043591404007ed592618bcb282ed59c64c88e02e77b8fca083323bce30dd", + "chksum_sha256": "c3aacc4b881fa318b8037a8e2c5fd48b683f36dc8635071193b428d4113264f1", "format": 1 }, { "name": "plugins/modules/sonic_port_breakout.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1896147e09a083d1fbce1900b21c29572b6dd992884947c2ffb4fb9379148e2f", + "chksum_sha256": "bd7c03e4ab8a0c16f8da318725e8ae69e6ba8eb93a782329e90e9e5dabf35bf7", "format": 1 }, { "name": "plugins/modules/sonic_bgp_ext_communities.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a0949a6e579a7eaef3d4b355ecaf328c8357c8b4d1ca7f8cf5624ed95582b81e", + "chksum_sha256": "e8e0ae69099a79826f1732110f2aa01751d91530630bbdc0559487ff7bc569b1", + "format": 1 + }, + { + "name": "plugins/modules/sonic_stp.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3f9f5bf5d9003a22f6b89cdb5a57edff7931a764c232ebac9a4401071296e026", "format": 1 }, { "name": "plugins/modules/sonic_lag_interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "77d851e7f2915eb77407dccabfb7e9a84a02c740e3bde4067ae22ddd11932088", + "chksum_sha256": "f20a7e72cc89eb39b43bf5d04929da070a62a1b57fbdca27f3b10f9c69dfd27c", "format": 1 }, { "name": "plugins/modules/sonic_bgp_as_paths.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e2c98cdd58166716bd27b7c658dd75849959877c0f987a1ad5f189d33762ee8e", + "chksum_sha256": "fc279ab490d4c025eeaef174e4ad2e582e88e4f978d6b87063eda38faad2e8e4", "format": 1 }, { "name": "plugins/modules/sonic_config.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2f596c49a79cc793a11a9f045602ae9dc226dd49b0e6ed5ebebb901814db2c3a", + "chksum_sha256": "419e86557940d2e1ddeeb46cfa27463ae108f25b6dc9f2e39ea62fd54831460d", "format": 1 }, { "name": "plugins/modules/sonic_facts.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "66e57eab9be3a92fabd25d58d4b50ad4989b5ed82930b9fa26005a778d004aaa", + "chksum_sha256": "3384a94f121e500936d58c39d7b9c64dd0a48933a722f0215f6d954dbd6f5dca", "format": 1 }, { "name": "plugins/modules/sonic_bgp_communities.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3053900903a2b5998e14d9a055cd0c907e9d4a1fcbcc76aff3efb616977ce038", + "chksum_sha256": "bad602f180c3f3b632e7f7179e2f8a74c4f531e4db782b47aec1262c26b5832b", "format": 1 }, { "name": "plugins/modules/sonic_bgp_neighbors_af.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "06e2639cc7ce2b03bd29e3909bf26525cefbd47035d9cb28927a96ccb91a16ad", + "chksum_sha256": "5cca331d24851606c9fa2cc6ed514c8ce1d614fa2fcd891ae0afed30c83a95fa", "format": 1 }, { "name": "plugins/modules/sonic_prefix_lists.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b7226bb99d6699bf1f974bafb6659d23949b94afb80cac714c374a2ffb126ad7", + "chksum_sha256": "b1fc8df7bb0e4b6b9462b947dd340ec00304c885f97c6812226016b8d291d74d", "format": 1 }, { "name": "plugins/modules/sonic_static_routes.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d0a90d0134fdcbb81da9195eadbd91821ba400199c32c6f8750c549a6653b58a", + "chksum_sha256": "588edb1a3b552ed38657b7bceda79dd80ef876b237ff743b505d69a63a264ee4", "format": 1 }, { "name": "plugins/modules/sonic_interfaces.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3d602d47c8e1bc50ba18d8929117ba98da1dade7d01b4099f7ddec558eeafba0", + "chksum_sha256": "9939b603d8eb85216a84f5cea1107aa826f966a0eeff705e30d4f8ee7a65045c", "format": 1 }, { "name": "plugins/modules/sonic_vxlans.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3c10ca81fd5969960f897c93b2159f6ac6c9ecd05084272433da32ab156f176a", + "chksum_sha256": "6f37906c6e578fefce8d697197b330b83df8777dd65f67372fa6c1e51396aeee", + "format": 1 + }, + { + "name": "plugins/modules/sonic_bfd.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "929e559af34df300a09096f470599512cdbd21c99500d8dc1fe3a6cb8a7ebb93", + "format": 1 + }, + { + "name": "plugins/modules/sonic_logging.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0e57448b8cfe6b311c4bab0a2f7d27f1de7b7737ebca01f299bedf51cae528c7", + "format": 1 + }, + { + "name": "plugins/modules/sonic_pki.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a62144d8151e36ef9fceb79a798f45c5b89d35bfe4e2d472b8764035146c144d", + "format": 1 + }, + { + "name": "plugins/modules/sonic_route_maps.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8dbb8bbb55defa96b6421dc379994627ef8c814d962a9ee74288fca502847c60", "format": 1 }, { @@ -4663,6 +8268,13 @@ "format": 1 }, { + "name": "CHANGELOG.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f56ba07cd40eeadaf3d0a40b370cbf8c3863ea967e8abf3e1df7b086126dbe0e", + "format": 1 + }, + { "name": "LICENSE", "ftype": "file", "chksum_type": "sha256", @@ -4677,6 +8289,62 @@ "format": 1 }, { + "name": "docs", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "docs/BRANCHING.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "03626549a59abf648ee59163b3b8acbf66c36513cb1e76d6e277bc044c926e30", + "format": 1 + }, + { + "name": "docs/SUPPORT.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "489515b299c57466195c73f4f779e074d8ccd881d1c40ffe8ebdfc1460df558a", + "format": 1 + }, + { + "name": "docs/ADDITIONAL_INFORMATION.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2ad23c71f8cb335215a86959658c76614e40a609698610948cd03e466ea08b72", + "format": 1 + }, + { + "name": "docs/ISSUE_TRIAGE.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dc9e301d33c99bd57c412a9b93e8e8c7e7a6d9a1a24cafd0c7391b7bbddb2501", + "format": 1 + }, + { + "name": "docs/COMMITTER_GUIDE.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9f6ce7fe985d513fed1f142c1ab7643f2e7a47f6379885b97cb28fcd2f4124c7", + "format": 1 + }, + { + "name": "docs/MAINTAINER_GUIDE.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a1ffd49773af4fc0f200fb01495ce566b911d7cc3b3d6d11287d41b6521b9545", + "format": 1 + }, + { + "name": "docs/CONTRIBUTING.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "729aed78631d8be8920e96c3a6f49b2efaeb7a64ce4abc846c6ba1bfe0c0e32f", + "format": 1 + }, + { "name": "meta", "ftype": "dir", "chksum_type": null, @@ -4687,7 +8355,7 @@ "name": "meta/runtime.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d6e301db0d0e6b8d8df2dac7fe249f0d21f5deadb47edb23f31fc25a87ea704f", + "chksum_sha256": "be750c50a25d2ffa34b8ba4734bd36edc8e4d3ad6c2dc46bf0d6fd9b5a9da4ec", "format": 1 }, { diff --git a/ansible_collections/dellemc/enterprise_sonic/MANIFEST.json b/ansible_collections/dellemc/enterprise_sonic/MANIFEST.json index 06fd002e1..3d6e88fc2 100644 --- a/ansible_collections/dellemc/enterprise_sonic/MANIFEST.json +++ b/ansible_collections/dellemc/enterprise_sonic/MANIFEST.json @@ -2,7 +2,7 @@ "collection_info": { "namespace": "dellemc", "name": "enterprise_sonic", - "version": "2.0.0", + "version": "2.4.0", "authors": [ "Senthil Kumar Ganesan <Senthil_Kumar_Ganesa@Dell.com>", "Abirami <Abirami_N@Dell.com>", @@ -11,7 +11,11 @@ "Nirai Madai <Niraimadaiselvam_Mar@Dell.com>", "Shade Talabi <Shade_Talabi@Dell.com>", "Kerry Meyer <Kerry_Meyer@Dell.com>", - "Mingjun Zhang <Mingjun_Zhang@Dell.com>" + "Mingjun Zhang <Mingjun_Zhang@Dell.com>", + "Arun Saravanan Balachandran (arun_saravanan_balac@dell.com)", + "Santhosh Kumar T (santhosh_kumar_t@dell.com)", + "Divya Balasubramanian <Divya_Balasubramania@DELL.com>", + "Cypher Miller <Cypher.Miller@Dell.com>" ], "readme": "README.md", "tags": [ @@ -25,7 +29,7 @@ "license": [], "license_file": "LICENSE", "dependencies": { - "ansible.netcommon": ">=2.0.0" + "ansible.netcommon": ">5.0.0" }, "repository": "https://github.com/ansible-collections/dellemc.enterprise_sonic", "documentation": null, @@ -36,7 +40,7 @@ "name": "FILES.json", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f419780826940a762216126025170e01d39987da0d8fb273560a2488307493f5", + "chksum_sha256": "0d2bd086845a8c77b0eb30ed113910a41421b8f0410012a6b68954aa500cc208", "format": 1 }, "format": 1 diff --git a/ansible_collections/dellemc/enterprise_sonic/README.md b/ansible_collections/dellemc/enterprise_sonic/README.md index 4c17c9e9c..d4a698d73 100644 --- a/ansible_collections/dellemc/enterprise_sonic/README.md +++ b/ansible_collections/dellemc/enterprise_sonic/README.md @@ -31,17 +31,50 @@ Name | Description | Connection type Collection network resource modules ----------------------------------- -Listed are the SONiC Ansible network resource modules which need ***httpapi*** as the connection type. Supported operations are ***merged*** and ***deleted***. - -| **Interfaces** | **BGP** | **VRF** | **Users** | -| -------------- | ------- | ------- | ------- | -| [**sonic_interfaces**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_interfaces_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-interfaces-module)|[**sonic_bgp**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-module)| [**sonic_vrfs**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_vrfs_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-vrfs-module)|[**sonic_users**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_users_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-users-module)| -| [**sonic_l2_interfaces**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_l2_interfaces_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-l2-interfaces-module)| [**sonic_bgp_af**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_af_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-af-module)| **MCLAG** | **AAA** | -| [**sonic_l3_interfaces**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_l3_interfaces_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-l3-interfaces-module) |[**sonic_bgp_as_paths**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_as_paths_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-as-paths-module)| [**sonic_mclag**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_mclag_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-mclag-module)| [**sonic_aaa**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_aaa_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-aaa-module)| -|**Port channel**|[**sonic_bgp_communities**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_communities_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-communities-module)| **VxLANs** |[**sonic_tacacs_server**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_tacacs_server_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-tacacs-server-module)| -|[**sonic_lag_interfaces**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_lag_interfaces_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-lag-interfaces-module)|[**sonic_bgp_ext_communities**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_ext_communities_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-ext-communities-module)| [**sonic_vxlans**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_vxlans_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-vxlans-module)|[**sonic_radius_server**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_radius_server_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-radius-server-module)| -|**VLANs**|[**sonic_bgp_neighbors**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_neighbors_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-neighbors-module)| **Port breakout** | **System** | -|[**sonic_vlans**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_vlans_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-vlans-module)|[**sonic_bgp_neighbors_af**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_neighbors_af_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-neighbors-af-module)|[**sonic_port_breakout**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_port_breakout_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-port-breakout-module) |[**sonic_system**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_system_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-system-module) | +Listed are the SONiC Ansible network resource modules which need ***httpapi*** as the connection type. Supported operations are ***merged***, ***deleted***, ***replaced*** and ***overridden***. + +Name | Description +--- | --- +[**sonic_aaa**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_aaa_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-aaa-module)| Manage AAA and its parameters +[**sonic_acl_interfaces**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_acl_interfaces_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-acl-interfaces-module)| Manage access control list (ACL) to interface binding +[**sonic_bfd**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bfd_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bfd-module)| Manage BFD configuration +[**sonic_bgp**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-module)| Manage global BGP and its parameters +[**sonic_bgp_af**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_af_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-af-module)| Manage global BGP address-family and its parameters +[**sonic_bgp_as_paths**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_as_paths_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-as-paths-module)| Manage BGP autonomous system path (or as-path-list) and its parameters +[**sonic_bgp_communities**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_communities_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-communities-module)| Manage BGP community and its parameters +[**sonic_bgp_ext_communities**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_ext_communities_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-ext-communities-module)| Manage BGP extended community-list and its parameters +[**sonic_bgp_neighbors**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_neighbors_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-neighbors-module)| Manage a BGP neighbor and its parameters +[**sonic_bgp_neighbors_af**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_neighbors_af_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-neighbors-af-module)| Manage the BGP neighbor address-family and its parameters +[**sonic_copp**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_copp_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-copp-module)| Manage CoPP configuration +[**sonic_dhcp_relay**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_dhcp_relay_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-dhcp-relay-module)| Manage DHCP and DHCPv6 relay configurations +[**sonic_dhcp_snooping**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_dhcp_snooping_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-dhcp-snooping-module)| Manage DHCP Snooping +[**sonic_interfaces**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_interfaces_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-interfaces-module)| Configure Interface attributes +[**sonic_ip_neighbor**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_sonic_ip_neighbor_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-sonic-ip-neighbor-module)| Manage IP neighbor global configuration +[**sonic_l2_acls**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_l2_acls_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-sonic-l2-acls-module)| Manage Layer 2 access control lists (ACL) configurations +[**sonic_l2_interfaces**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_l2_interfaces_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-l2-interfaces-module)| Configure interface-to-VLAN association +[**sonic_l3_acls**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_l3_acls_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-sonic-l3-acls-module)| Manage Layer 3 access control lists (ACL) configurations +[**sonic_l3_interfaces**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_l3_interfaces_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-l3-interfaces-module)| Configure the IPv4 and IPv6 parameters on Interfaces +[**sonic_lag_interfaces**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_lag_interfaces_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-lag-interfaces-module)| Manage link aggregation group (LAG) interface parameters +[**sonic_lldp_global**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_sonic_lldp_global_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-sonic-lldp-global-module)| Manage Global LLDP configurations +[**sonic_logging**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_logging_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-logging-module)| Manage logging configuration +[**sonic_mac**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_mac_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-mac-module)| Manage MAC configuration +[**sonic_mclag**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_mclag_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-mclag-module)| Manage multi chassis link aggregation groups domain (MCLAG) and its parameters +[**sonic_ntp**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_ntp_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-ntp-module)| Manage NTP configuration +[**sonic_pki**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_pki_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-pki-module)| Manages PKI attributes +[**sonic_port_breakout**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_port_breakout_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-port-breakout-module)| Configure port breakout settings on physical interfaces +[**sonic_port_group**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_port_group_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-port-group-module)| Manage port group configuration +[**sonic_prefix_lists**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_prefix_lists_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-prefix-lists-module)| Manage prefix list configuration +[**sonic_radius_server**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_radius_server_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-radius-server-module)| Manage RADIUS server and its parameters +[**sonic_route_maps**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_route_maps_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-route-maps-module)| Manage route map configuration +[**sonic_static_routes**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_static_routes_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-static-routes-module)| Manage static routes configuration +[**sonic_stp**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_stp_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-stp-module)| Manage STP configuration +[**sonic_system**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_system_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-system-module)| Configure system parameters +[**sonic_tacacs_server**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_tacacs_server_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-tacacs-server-module)| Manage TACACS server and its parameters +[**sonic_users**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_users_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-users-module)| Manage users and its parameters +[**sonic_vlan_mapping**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_vlan_mapping_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-vlan-mapping-module)| Configure vlan mappings +[**sonic_vlans**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_vlans_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-vlans-module)| Manage VLAN and its parameters +[**sonic_vrfs**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_vrfs_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-vrfs-module)| Manage VRFs and associate VRFs to interfaces +[**sonic_vxlans**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_vxlans_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-vxlans-module)| Manage VxLAN EVPN and its parameters Sample use case playbooks ------------------------- @@ -53,9 +86,9 @@ Name | Description Version compatibility ---------------------- -* Recommended Ansible version 2.10 or higher +* Recommended Ansible version 2.14 or higher (This is required for enterprise_sonic collection version >= 2.4.0). * Enterprise SONiC Distribution by Dell Technologies version 3.1 or higher -* Recommended Python 3.5 or higher, or Python 2.7 +* Recommended Python 3.9 or higher (This is required for enterprise_sonic collection version >= 2.4.0.). * Dell Enterprise SONiC images for releases 3.1 - 3.5: Use Ansible Enterprise SONiC collection version 1.1.0 or later 1.m.n versions (from the 1.x branch of this repo) * Dell Enterprise SONiC images for release 4.0 and later 4.x.y releases: Use Ansible Enterprise SONiC collection version 2.0.0 or later 2.m.n releases (from the "2.x" branch of this repo). * In general: Dell Enterprise SONiC release versions "R.x.y" are supported by Ansible Enterprise SONiC collection versions "R-2.m.n" on branch "R-2.x". diff --git a/ansible_collections/dellemc/enterprise_sonic/bindep.txt b/ansible_collections/dellemc/enterprise_sonic/bindep.txt new file mode 100644 index 000000000..ba9c980fb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/bindep.txt @@ -0,0 +1,6 @@ +# This is a cross-platform list tracking distribution packages needed by tests; +# see https://docs.openstack.org/infra/bindep/ for additional information. + +gcc-c++ [doc test platform:rpm] +python3-devel [test platform:rpm] +python3 [test platform:rpm] diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/.plugin-cache.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/.plugin-cache.yaml index 9d3d51d00..8b0d92009 100644 --- a/ansible_collections/dellemc/enterprise_sonic/changelogs/.plugin-cache.yaml +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/.plugin-cache.yaml @@ -10,6 +10,7 @@ plugins: name: sonic version_added: null connection: {} + filter: {} httpapi: sonic: description: HttpApi Plugin for devices supporting Restconf SONIC API @@ -23,11 +24,21 @@ plugins: name: sonic_aaa namespace: '' version_added: 1.1.0 + sonic_acl_interfaces: + description: Manage access control list (ACL) to interface binding on SONiC + name: sonic_acl_interfaces + namespace: '' + version_added: 2.1.0 sonic_api: description: Manages REST operations on devices running Enterprise SONiC name: sonic_api namespace: '' version_added: 1.0.0 + sonic_bfd: + description: Manage BFD configuration on SONiC + name: sonic_bfd + namespace: '' + version_added: 2.1.0 sonic_bgp: description: Manage global BGP and its parameters name: sonic_bgp @@ -73,6 +84,21 @@ plugins: name: sonic_config namespace: '' version_added: 1.0.0 + sonic_copp: + description: Manage CoPP configuration on SONiC + name: sonic_copp + namespace: '' + version_added: 2.1.0 + sonic_dhcp_relay: + description: Manage DHCP and DHCPv6 relay configurations on SONiC + name: sonic_dhcp_relay + namespace: '' + version_added: 2.1.0 + sonic_dhcp_snooping: + description: Manage DHCP Snooping on SONiC + name: sonic_dhcp_snooping + namespace: '' + version_added: 2.3.0 sonic_facts: description: Collects facts on devices running Enterprise SONiC name: sonic_facts @@ -84,12 +110,27 @@ plugins: name: sonic_interfaces namespace: '' version_added: 1.0.0 + sonic_ip_neighbor: + description: Manage IP neighbor global configuration on SONiC. + name: sonic_ip_neighbor + namespace: '' + version_added: 2.1.0 + sonic_l2_acls: + description: Manage Layer 2 access control lists (ACL) configurations on SONiC + name: sonic_l2_acls + namespace: '' + version_added: 2.1.0 sonic_l2_interfaces: description: Configure interface-to-VLAN association that is based on access or trunk mode name: sonic_l2_interfaces namespace: '' version_added: 1.0.0 + sonic_l3_acls: + description: Manage Layer 3 access control lists (ACL) configurations on SONiC + name: sonic_l3_acls + namespace: '' + version_added: 2.1.0 sonic_l3_interfaces: description: Configure the IPv4 and IPv6 parameters on Interfaces such as, Eth, LAG, VLAN, and loopback @@ -101,6 +142,21 @@ plugins: name: sonic_lag_interfaces namespace: '' version_added: 1.0.0 + sonic_lldp_global: + description: Manage Global LLDP configurations on SONiC + name: sonic_lldp_global + namespace: '' + version_added: 2.1.0 + sonic_logging: + description: Manage logging configuration on SONiC. + name: sonic_logging + namespace: '' + version_added: 2.1.0 + sonic_mac: + description: Manage MAC configuration on SONiC + name: sonic_mac + namespace: '' + version_added: 2.1.0 sonic_mclag: description: Manage multi chassis link aggregation groups domain (MCLAG) and its parameters @@ -112,11 +168,21 @@ plugins: name: sonic_ntp namespace: '' version_added: 2.0.0 + sonic_pki: + description: Manages PKI attributes of Enterprise Sonic + name: sonic_pki + namespace: '' + version_added: 2.3.0 sonic_port_breakout: description: Configure port breakout settings on physical interfaces name: sonic_port_breakout namespace: '' version_added: 1.0.0 + sonic_port_group: + description: Manages port group configuration on SONiC. + name: sonic_port_group + namespace: '' + version_added: 2.1.0 sonic_prefix_lists: description: prefix list configuration handling for SONiC name: sonic_prefix_lists @@ -127,11 +193,21 @@ plugins: name: sonic_radius_server namespace: '' version_added: 1.0.0 + sonic_route_maps: + description: route map configuration handling for SONiC + name: sonic_route_maps + namespace: '' + version_added: 2.1.0 sonic_static_routes: description: Manage static routes configuration on SONiC name: sonic_static_routes namespace: '' version_added: 2.0.0 + sonic_stp: + description: Manage STP configuration on SONiC + name: sonic_stp + namespace: '' + version_added: 2.3.0 sonic_system: description: Configure system parameters name: sonic_system @@ -147,6 +223,11 @@ plugins: name: sonic_users namespace: '' version_added: 1.1.0 + sonic_vlan_mapping: + description: Configure vlan mappings on SONiC. + name: sonic_vlan_mapping + namespace: '' + version_added: 2.1.0 sonic_vlans: description: Manage VLAN and its parameters name: sonic_vlans @@ -166,5 +247,6 @@ plugins: netconf: {} shell: {} strategy: {} + test: {} vars: {} -version: 2.0.0 +version: 2.4.0 diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/CHANGELOG.rst b/ansible_collections/dellemc/enterprise_sonic/changelogs/CHANGELOG.rst deleted file mode 100644 index c9a571cd8..000000000 --- a/ansible_collections/dellemc/enterprise_sonic/changelogs/CHANGELOG.rst +++ /dev/null @@ -1,119 +0,0 @@ -====================================== -Dellemc.Enterprise_Sonic Release Notes -====================================== - -.. contents:: Topics - - -v2.0.0 -====== - -Release Summary ---------------- - -This release provides Dell SONiC Enterprise Ansible Collection support for SONiC 4.x images. It is the first release for the 2.x branch of the collection. Subsequent enhancements for support of SONiC 4.x images will also be provided as needed on the 2.x branch. This release also contains bugfixes and enhancements to supplement the Ansible functionality provided previously for SONiC 3.x images. The changelog describes changes made to the modules and plugins included in this collection since release 1.1.0. - - -Major Changes -------------- - -- Added 'static_routes' module to collection (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/82). -- Added a resource module for NTP support (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/99). -- Added a resource module for support of prefix lists (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/100). -- Updated backend REST API request formats in all applicable modules for compatibility with SONiC 4.x openconfig YANG compliant REST APIs. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/53) - -Minor Changes -------------- - -- Added an execution-environment.yml file to the "meta" directory to enable use of Ansible execution environment infrastructure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/88). -- bgp_af - Added support for BGP options to configure usage and advertisement of vxlan primary IP address related attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/62). -- bgp_as_paths - updated module examples with 'permit' attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102) -- bgp_neighbors - Add BGP peer group support for multiple attributes. The added attributes correspond to the same set of attributes added for BGP neighbors with PR 72 (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/81). -- bgp_neighbors - Add support for multiple attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/72). -- bgp_neighbors - add an auth_pwd dictionary and nbr_description attribute to the argspec (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/67). -- bgp_neighbors - added prefix-list related peer-group attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101). -- bgp_neighbors_af - added prefix-list related neighbor attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101). -- playbook - updated examples to reflect module changes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102) -- sonic_vxlans - Add configuration capability for the primary IP address of a vxlan vtep to facilitate vxlan path redundundancy (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/58). -- vlans - Added support for the vlan "description" attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/98). -- workflow - Added stable-2.13 to the sanity test matrix (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90). - -Breaking Changes / Porting Guide --------------------------------- - -- bgp_af - Add the route_advertise_list dictionary to the argspec to replace the deleted, obsolete advertise_prefix attribute used for SONiC 3.x images on the 1.x branch of this collection. This change corresponds to a SONiC 4.0 OC YANG REST compliance change for the BGP AF REST API. It enables specification of a route map in conjunction with each route advertisement prefix (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/63). -- bgp_af - remove the obsolete 'advertise_prefix' attribute from argspec and config code. This and subsequent co-req replacement with the new route advertise list argument structure require corresponding changes in playbooks previoulsly used for configuring route advertise prefixes for SONiC 3.x images. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60) -- bgp_neighbors - Replace the previously defined standalone "bfd" attribute with a bfd dictionary containing multiple attributes. This change corresponds to the revised SONiC 4.x implementation of OC YANG compatible REST APIs. Playbooks previously using the bfd attributes for SONiC 3.x images must be modified for useon SONiC 4.0 images to use the new definition for the bfd attribute argspec structure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/72). -- bgp_neighbors - Replace, for BGP peer groups, the previously defined standalone "bfd" attribute with a bfd dictionary containing multiple attributes. This change corresponds to the revised SONiC 4.x implementation of OC YANG compatible REST APIs. Playbooks previously using the bfd attributes for SONiC 3.x images must be modified for useon SONiC 4.0 images to use the new definition for the bfd attribute argspec structure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/81). - -Bugfixes --------- - -- Fixed regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/103). -- Fixed regression test sequencing and other regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/85). -- aaa - Fixed a bug in facts gathering by providing required conditional branching (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90) -- aaa - Modify regression test sequencing to enable correct testing of the functionality for this module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/78). -- bgp_neighbors - remove string conversion of timer attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60) -- port_breakout - Fixed a bug in formulation of port breakout REST APIs (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/88). -- sonic - Fix a bug in handling of interface names in standard interface naming mode (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/103). -- sonic_command - Fix bugs in handling of CLI commands involving a prompt and answer sequence (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/76/files). -- users - Fixed a bug in facts gathering (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90). -- vxlan - update Vxlan test cases to comply with SONiC behavior (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/105). - -New Modules ------------ - -- dellemc.enterprise_sonic.sonic_ntp - Manage NTP configuration on SONiC. -- dellemc.enterprise_sonic.sonic_prefix_lists - prefix list configuration handling for SONiC -- dellemc.enterprise_sonic.sonic_static_routes - Manage static routes configuration on SONiC - -v1.1.0 -====== - -New Modules ------------ - -- dellemc.enterprise_sonic.sonic_aaa - AAA resource module. -- dellemc.enterprise_sonic.sonic_radius_server - RADIUS resource module. -- dellemc.enterprise_sonic.sonic_system - SYSTEM resource module. -- dellemc.enterprise_sonic.sonic_tacacs_server - TACACS Server resource module. - -v1.0.0 -====== - -New Plugins ------------ - -Cliconf -~~~~~~~ - -- dellemc.enterprise_sonic.sonic - Use Ansible CLICONF to run commands on Enterprise SONiC. - -Httpapi -~~~~~~~ - -- dellemc.enterprise_sonic.sonic - Use Ansible HTTPAPI to run commands on Enterprise SONiC. - -New Modules ------------ - -- dellemc.enterprise_sonic.sonic_api - Perform REST operations through the Management Framework REST API. -- dellemc.enterprise_sonic.sonic_bgp - BGP resource module. -- dellemc.enterprise_sonic.sonic_bgp_af - BGP AF resource module. -- dellemc.enterprise_sonic.sonic_bgp_as_paths - BGP AS path resource module. -- dellemc.enterprise_sonic.sonic_bgp_communities - BGP communities resource module. -- dellemc.enterprise_sonic.sonic_bgp_ext_communities - BGP Ext communities resource module. -- dellemc.enterprise_sonic.sonic_bgp_neighbors - BGP neighbors resource module. -- dellemc.enterprise_sonic.sonic_bgp_neighbors_af - BGP neighbors AF resource module. -- dellemc.enterprise_sonic.sonic_command - Run commands through Management Framework CLI. -- dellemc.enterprise_sonic.sonic_config - Manage configuration through the Management Framework CLI. -- dellemc.enterprise_sonic.sonic_interfaces - Interface resource module. -- dellemc.enterprise_sonic.sonic_l2_interfaces - Layer 2 interface resource module. -- dellemc.enterprise_sonic.sonic_l3_interfaces - Layer 3 interface resource module. -- dellemc.enterprise_sonic.sonic_lag_interfaces - Link aggregation (LAG) resource module. -- dellemc.enterprise_sonic.sonic_mclag - MCLAG resource module. -- dellemc.enterprise_sonic.sonic_port_breakout - port breakout resource module. -- dellemc.enterprise_sonic.sonic_users - USERS resource module. -- dellemc.enterprise_sonic.sonic_vlans - VLAN resource module. -- dellemc.enterprise_sonic.sonic_vrfs - VRF resource module. -- dellemc.enterprise_sonic.sonic_vxlans - VxLAN EVPN resource module. diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/106-change-ntp-get-fact.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/106-change-ntp-get-fact.yaml new file mode 100644 index 000000000..6232a4619 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/106-change-ntp-get-fact.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_ntp - change NTP get facts to get default parameters (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/106). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/107-change-ntp-key-values-in-regression-script.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/107-change-ntp-key-values-in-regression-script.yaml new file mode 100644 index 000000000..83371c643 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/107-change-ntp-key-values-in-regression-script.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_ntp - change NTP key values in NTP regression test script (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/107). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/113-change-ntp-module-name.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/113-change-ntp-module-name.yaml new file mode 100644 index 000000000..2760de791 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/113-change-ntp-module-name.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_ntp - change NTP module name (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/113). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/114-change-ntp-module-name-in-regression-script.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/114-change-ntp-module-name-in-regression-script.yaml new file mode 100644 index 000000000..4db143be3 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/114-change-ntp-module-name-in-regression-script.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_ntp - change NTP module names in NTP regression test script (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/114). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/118-add-ntp-prefer-attribute.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/118-add-ntp-prefer-attribute.yaml new file mode 100644 index 000000000..3f263712f --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/118-add-ntp-prefer-attribute.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_ntp - Added prefer attribute to NTP resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/118) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/119-lag_interfaces-port-name-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/119-lag_interfaces-port-name-fix.yaml new file mode 100644 index 000000000..6db3bf2fa --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/119-lag_interfaces-port-name-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - sonic_lag_interfaces - Fixed port name issue (GitHub issue#153) (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/119) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/124-l2-interfaces-oc-yang-vlan-range-format-config-support.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/124-l2-interfaces-oc-yang-vlan-range-format-config-support.yaml new file mode 100644 index 000000000..6713e8948 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/124-l2-interfaces-oc-yang-vlan-range-format-config-support.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_l2_interfaces - Add support for parsing configuration containing the OC Yang vlan range syntax (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/124). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/128-add-several-attributes-to-interface-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/128-add-several-attributes-to-interface-resource-module.yaml new file mode 100644 index 000000000..ee45fa81a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/128-add-several-attributes-to-interface-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_interfaces - Added speed, auto-negotiate, advertised-speed and FEC to interface resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/128) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/129-ntp-minpoll-maxpoll-config.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/129-ntp-minpoll-maxpoll-config.yaml new file mode 100644 index 000000000..3d66d5bfe --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/129-ntp-minpoll-maxpoll-config.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_ntp - change NTP resource module to make minpoll and maxpoll be configured together (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/129). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/130-vxlans-attribute-restriction-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/130-vxlans-attribute-restriction-fix.yaml new file mode 100644 index 000000000..d0e1bb1d3 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/130-vxlans-attribute-restriction-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - sonic_vxlans - Removed required_together restriction for evpn_nvo and source_ip attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/130) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/141-add-route-distinguisher-target-attributes-to-bgp-af-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/141-add-route-distinguisher-target-attributes-to-bgp-af-module.yaml new file mode 100644 index 000000000..09ed96844 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/141-add-route-distinguisher-target-attributes-to-bgp-af-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bgp_af - Added several attributes to support configuration of route distinguisher and route target (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/141 diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/145-mclag-new-attributes.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/145-mclag-new-attributes.yaml new file mode 100644 index 000000000..eb4649894 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/145-mclag-new-attributes.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_mclag - Added delay_restore, gateway_mac, and peer_gateway attributes to module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/145) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/146-vrf-mgmt-bug-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/146-vrf-mgmt-bug-fix.yaml new file mode 100644 index 000000000..c7e95a1cd --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/146-vrf-mgmt-bug-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - sonic_vrfs - Added tasks as a workaround to mgmt VRF bug (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/146) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/148-dhcp-relay-unit-tests.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/148-dhcp-relay-unit-tests.yaml new file mode 100644 index 000000000..cf62e4b32 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/148-dhcp-relay-unit-tests.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_dhcp_relay - Added a common unit tests module and unit tests for dhcp relay module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/148) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/149-l2-interfaces-vlan-trunk-range-support-for-playbooks.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/149-l2-interfaces-vlan-trunk-range-support-for-playbooks.yaml new file mode 100644 index 000000000..515cf9975 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/149-l2-interfaces-vlan-trunk-range-support-for-playbooks.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_l2_interfaces - Add support for specifying vlan trunk ranges in Ansible playbooks (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/149). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/150-replaced-overridden-for-logging-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/150-replaced-overridden-for-logging-resource-module.yaml new file mode 100644 index 000000000..7f151d860 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/150-replaced-overridden-for-logging-resource-module.yaml @@ -0,0 +1,2 @@ +major_changes: + - sonic_logging - Added replaced and overridden states support for logging resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/150) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/151-replaced-overridden-for-ntp-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/151-replaced-overridden-for-ntp-resource-module.yaml new file mode 100644 index 000000000..96a38748a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/151-replaced-overridden-for-ntp-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_ntp - Added replaced and overridden states support for NTP resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/151) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/152-copp.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/152-copp.yaml new file mode 100644 index 000000000..d05370fb7 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/152-copp.yaml @@ -0,0 +1,2 @@ +major_changes: + - Added copp module to collection (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/152). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/156-replaced-overridden-for-vrfs-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/156-replaced-overridden-for-vrfs-resource-module.yaml new file mode 100644 index 000000000..e35488b32 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/156-replaced-overridden-for-vrfs-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_vrfs - Added replaced and overridden states support for VRF resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/156) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/157-mac.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/157-mac.yaml new file mode 100644 index 000000000..56b2919ae --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/157-mac.yaml @@ -0,0 +1,2 @@ +major_changes: + - Added mac module to collection (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/157). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/159-replaced-overridden-for-system-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/159-replaced-overridden-for-system-resource-module.yaml new file mode 100644 index 000000000..e0a239933 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/159-replaced-overridden-for-system-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_system - Added replaced and overridden states support for system resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/159) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/160-sanity-check-errors-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/160-sanity-check-errors-fix.yaml new file mode 100644 index 000000000..dc874ed24 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/160-sanity-check-errors-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - Fixed sanity check errors in the collection caused by Ansible library changes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/160). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/172-module-utils-import-remove-empties-from-ansible-lib.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/172-module-utils-import-remove-empties-from-ansible-lib.yaml new file mode 100644 index 000000000..136cfaf93 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/172-module-utils-import-remove-empties-from-ansible-lib.yaml @@ -0,0 +1,2 @@ +minor_changes: + - module_utils - Change the location for importing remove_empties from the obsolete Netcommon location to the offically required Ansible library location to fix sanity errors (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/172). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/176-update-netcommon-version.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/176-update-netcommon-version.yaml new file mode 100644 index 000000000..083ad868e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/176-update-netcommon-version.yaml @@ -0,0 +1,2 @@ +bugfixes: + - install-Updated the required ansible.netcommon version (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/176) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/177-bfd.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/177-bfd.yaml new file mode 100644 index 000000000..e18973959 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/177-bfd.yaml @@ -0,0 +1,2 @@ +major_changes: + - Added bfd module to collection (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/177). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/180-regression-failures-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/180-regression-failures-fix.yaml new file mode 100644 index 000000000..6511f8878 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/180-regression-failures-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - Fixed regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/180). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/182-unit-tests-for-bgp-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/182-unit-tests-for-bgp-resource-module.yaml new file mode 100644 index 000000000..f6627f6ed --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/182-unit-tests-for-bgp-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bgp - Add unit tests for BGP resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/182) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/183-unit-tests-for-bgp-af-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/183-unit-tests-for-bgp-af-resource-module.yaml new file mode 100644 index 000000000..032bb3308 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/183-unit-tests-for-bgp-af-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bgp_af - Add unit tests for BGP AF resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/183) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/184-unit-tests-for-bgp-as-paths-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/184-unit-tests-for-bgp-as-paths-resource-module.yaml new file mode 100644 index 000000000..5e008055f --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/184-unit-tests-for-bgp-as-paths-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bgp_as_paths - Add unit tests for BGP AS paths resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/184) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/185-unit-tests-for-bgp-communities-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/185-unit-tests-for-bgp-communities-resource-module.yaml new file mode 100644 index 000000000..1bac125de --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/185-unit-tests-for-bgp-communities-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bgp_communities - Add unit tests for BGP communities resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/185) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/186-unit-tests-for-bgp-ext-communities-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/186-unit-tests-for-bgp-ext-communities-resource-module.yaml new file mode 100644 index 000000000..531e29e18 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/186-unit-tests-for-bgp-ext-communities-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bgp_ext_communities - Add unit tests for BGP ext communities resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/186) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/187-unit-tests-for-bgp-neighbors-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/187-unit-tests-for-bgp-neighbors-resource-module.yaml new file mode 100644 index 000000000..4c72e6f0e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/187-unit-tests-for-bgp-neighbors-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bgp_neighbors - Add unit tests for BGP neighbors resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/187) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/188-unit-tests-for-bgp-neighbors-af-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/188-unit-tests-for-bgp-neighbors-af-resource-module.yaml new file mode 100644 index 000000000..aa63260d9 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/188-unit-tests-for-bgp-neighbors-af-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bgp_neighbors_af - Add unit tests for BGP neighbors AF resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/188) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/191-unit-tests-for-bgp-af-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/191-unit-tests-for-bgp-af-resource-module.yaml new file mode 100644 index 000000000..d0dfb74d0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/191-unit-tests-for-bgp-af-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bgp_af - Add unit tests for BGP AF resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/191) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/195-aaa-login-authentication.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/195-aaa-login-authentication.yaml new file mode 100644 index 000000000..45a8ad91d --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/195-aaa-login-authentication.yaml @@ -0,0 +1,2 @@ +breaking_changes: + - aaa - Added default_auth attribute to the argspec to replace the deleted group and local attributes. This change allows for ordered login authentication. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/195). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/196-replaced-overridden-for-lag-interface-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/196-replaced-overridden-for-lag-interface-resource-module.yaml new file mode 100644 index 000000000..7fcd9bbc0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/196-replaced-overridden-for-lag-interface-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_lag_interface - Added replaced and overridden states support for LAG interface resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/196) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/197-unit-tests-for-interfaces-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/197-unit-tests-for-interfaces-resource-module.yaml new file mode 100644 index 000000000..d89d54eca --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/197-unit-tests-for-interfaces-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_interfaces - Add unit tests for interfaces resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/197) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/198-unit-tests-for-aaa-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/198-unit-tests-for-aaa-resource-module.yaml new file mode 100644 index 000000000..6661f4eea --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/198-unit-tests-for-aaa-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_aaa - Add unit tests for AAA resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/198) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/199-code-coverage-workflow-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/199-code-coverage-workflow-fix.yaml new file mode 100644 index 000000000..0e32dd4a8 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/199-code-coverage-workflow-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - workflows-Fixed dependency installation issue in the code coverage workflow (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/199) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/200-unit-tests-for-l2-interfaces-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/200-unit-tests-for-l2-interfaces-resource-module.yaml new file mode 100644 index 000000000..7ffb3592e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/200-unit-tests-for-l2-interfaces-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_l2_interfaces - Add unit tests for l2_interfaces resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/200) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/201-bgp-af-modify-vni-advertise-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/201-bgp-af-modify-vni-advertise-fix.yaml new file mode 100644 index 000000000..f59fa5991 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/201-bgp-af-modify-vni-advertise-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - sonic_bgp_af - Fixed issue with vnis and advertise modification for a single BGP AF (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/201) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/202-unit-tests-for-l3-interfaces-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/202-unit-tests-for-l3-interfaces-resource-module.yaml new file mode 100644 index 000000000..7ffd76899 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/202-unit-tests-for-l3-interfaces-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_l3_interfaces - Add unit tests for l3_interfaces resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/202) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/203-unit-tests-for-lag-interfaces-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/203-unit-tests-for-lag-interfaces-resource-module.yaml new file mode 100644 index 000000000..8a8b99a36 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/203-unit-tests-for-lag-interfaces-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_lag_interfaces - Add unit tests for lag_interfaces resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/203) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/207-unit-tests-for-ntp-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/207-unit-tests-for-ntp-resource-module.yaml new file mode 100644 index 000000000..c84312e11 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/207-unit-tests-for-ntp-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_ntp - Add unit tests for NTP resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/207) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/208-unit-tests-for-tacacs-server-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/208-unit-tests-for-tacacs-server-resource-module.yaml new file mode 100644 index 000000000..a1241cf55 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/208-unit-tests-for-tacacs-server-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_tacacs_server - Add unit tests for TACACS server resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/208) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/209-unit-tests-for-prefix-lists-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/209-unit-tests-for-prefix-lists-resource-module.yaml new file mode 100644 index 000000000..bd4dd0cc7 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/209-unit-tests-for-prefix-lists-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_prefix_lists - Add unit tests for prefix lists resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/209) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/210-unit-tests-for-radius-server-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/210-unit-tests-for-radius-server-resource-module.yaml new file mode 100644 index 000000000..db3f82dc1 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/210-unit-tests-for-radius-server-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_radius_server - Add unit tests for RADIUS server resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/210) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/212-unit-tests-for-static-routes-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/212-unit-tests-for-static-routes-resource-module.yaml new file mode 100644 index 000000000..9fa01a76f --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/212-unit-tests-for-static-routes-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_static_routes - Add unit tests for static routes resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/212) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/213-unit-tests-for-users-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/213-unit-tests-for-users-resource-module.yaml new file mode 100644 index 000000000..45e2b5221 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/213-unit-tests-for-users-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_users - Add unit tests for users resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/213) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/214-unit-tests-vlans-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/214-unit-tests-vlans-resource-module.yaml new file mode 100644 index 000000000..71e4b0014 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/214-unit-tests-vlans-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_vlans - Add unit tests for Vlans resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/214) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/215-unit-tests-for-vxlans-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/215-unit-tests-for-vxlans-resource-module.yaml new file mode 100644 index 000000000..70927073e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/215-unit-tests-for-vxlans-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_vxlans - Add unit tests for VxLans resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/215) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/216-unit-tests-for-vrfs-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/216-unit-tests-for-vrfs-resource-module.yaml new file mode 100644 index 000000000..bdd6f6314 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/216-unit-tests-for-vrfs-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_vrfs - Add unit tests for VRFS resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/216) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/217-replaced-overridden-for-vlans-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/217-replaced-overridden-for-vlans-resource-module.yaml new file mode 100644 index 000000000..6627b44b1 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/217-replaced-overridden-for-vlans-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_vlans - Added replaced and overridden states support for VLAN resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/217) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/218-unit-tests-for-api-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/218-unit-tests-for-api-resource-module.yaml new file mode 100644 index 000000000..31ca08e44 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/218-unit-tests-for-api-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_api - Add unit tests for api resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/218) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/219-unit-tests-for-command-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/219-unit-tests-for-command-resource-module.yaml new file mode 100644 index 000000000..2eaafc1e8 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/219-unit-tests-for-command-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_command - Add unit tests for command resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/219) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/220-unit-tests-for-config-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/220-unit-tests-for-config-resource-module.yaml new file mode 100644 index 000000000..46210e167 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/220-unit-tests-for-config-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_config - Add unit tests for config resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/220) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/221-l2-interfaces-replaced-overridden-support.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/221-l2-interfaces-replaced-overridden-support.yaml new file mode 100644 index 000000000..25b8da767 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/221-l2-interfaces-replaced-overridden-support.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - sonic_l2_interfaces - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/221). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/222-unit-tests-for-facts-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/222-unit-tests-for-facts-resource-module.yaml new file mode 100644 index 000000000..0f03fc697 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/222-unit-tests-for-facts-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_facts - Add unit tests for facts resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/222) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/223-unit-tests-for-system-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/223-unit-tests-for-system-resource-module.yaml new file mode 100644 index 000000000..03759ac63 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/223-unit-tests-for-system-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_system - Add unit tests for system resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/223) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/224-ntp-clear-all-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/224-ntp-clear-all-fix.yaml new file mode 100644 index 000000000..fcc0e16fe --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/224-ntp-clear-all-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - sonic_ntp - fixed the issue (GitHub issue#205) with NTP clear all without config given (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/224) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/225-unit-tests-for-ip-neighbor-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/225-unit-tests-for-ip-neighbor-resource-module.yaml new file mode 100644 index 000000000..32f04ca0c --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/225-unit-tests-for-ip-neighbor-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_ip_neighbor - Add unit tests for IP neighbor resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/225) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/226-unit-tests-for-logging-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/226-unit-tests-for-logging-resource-module.yaml new file mode 100644 index 000000000..575bbc6f1 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/226-unit-tests-for-logging-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_logging - Add unit tests for logging resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/226) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/227-replaced-overridden-for-port-group-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/227-replaced-overridden-for-port-group-resource-module.yaml new file mode 100644 index 000000000..dab5dce82 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/227-replaced-overridden-for-port-group-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_port_group - Added replaced and overridden states support for port group resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/227) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/228-unit-tests-for-port-group-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/228-unit-tests-for-port-group-resource-module.yaml new file mode 100644 index 000000000..140d4ef59 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/228-unit-tests-for-port-group-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_port_group - Add unit tests for port group resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/228) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/229-unit-tests-for-port-breakout-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/229-unit-tests-for-port-breakout-resource-module.yaml new file mode 100644 index 000000000..bcb668c7d --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/229-unit-tests-for-port-breakout-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_port_breakout - Add unit tests for port breakout resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/229) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/230-vrfs-delete-interface-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/230-vrfs-delete-interface-fix.yaml new file mode 100644 index 000000000..0a45fdb39 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/230-vrfs-delete-interface-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - sonic_vrfs - fixed the issue (GitHub issue#194) with VRF when deleting interface(https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/230) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/231-l3-interfaces-delete-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/231-l3-interfaces-delete-fix.yaml new file mode 100644 index 000000000..4a824d92b --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/231-l3-interfaces-delete-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - sonic_l3_interfaces - Fixed IP address deletion issue (GitHub issue#170) (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/231) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/233-bgp-neighbors-defaults-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/233-bgp-neighbors-defaults-fix.yaml new file mode 100644 index 000000000..a446deeef --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/233-bgp-neighbors-defaults-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - sonic_neighbors - Fixed handling of default attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/233) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/235-replaced-overridden-for-tacacs-server-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/235-replaced-overridden-for-tacacs-server-resource-module.yaml new file mode 100644 index 000000000..299c9ddc5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/235-replaced-overridden-for-tacacs-server-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_tacacs_server - Add replaced and overridden states support for TACACS server resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/235) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/236-static-routes-replaced-overridden-support.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/236-static-routes-replaced-overridden-support.yaml new file mode 100644 index 000000000..520745a72 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/236-static-routes-replaced-overridden-support.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_static_routes - Added support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/236) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/237-aaa-replaced-overridden-support.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/237-aaa-replaced-overridden-support.yaml new file mode 100644 index 000000000..99adf5771 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/237-aaa-replaced-overridden-support.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_aaa - Added replaced and overridden states support for AAA resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/237) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/239-replaced-overridden-for-radius-server-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/239-replaced-overridden-for-radius-server-resource-module.yaml new file mode 100644 index 000000000..5a33f41f6 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/239-replaced-overridden-for-radius-server-resource-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_radius_server - Added replaced and overridden states support for RADIUS server resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/239) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/240-bgp-replaced-overridden-support.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/240-bgp-replaced-overridden-support.yaml new file mode 100644 index 000000000..391feb478 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/240-bgp-replaced-overridden-support.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - sonic_bgp - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/240). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/242-users-replaced-overridden-support.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/242-users-replaced-overridden-support.yaml new file mode 100644 index 000000000..41d7490de --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/242-users-replaced-overridden-support.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_users - Add replaced and overridden states support for users resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/242) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/244-added-rt-delay-attribute-to-bgp-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/244-added-rt-delay-attribute-to-bgp-module.yaml new file mode 100644 index 000000000..002bb57ef --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/244-added-rt-delay-attribute-to-bgp-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bgp - Added rt_delay attribute to module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/244) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/245-enhance-bgp-neighbors-unit-tests.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/245-enhance-bgp-neighbors-unit-tests.yaml new file mode 100644 index 000000000..0898bf4b5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/245-enhance-bgp-neighbors-unit-tests.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bgp_neighbors - Enhance unit tests for BGP Neighbors resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/245) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/246-bgp-af-replaced-overridden-support.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/246-bgp-af-replaced-overridden-support.yaml new file mode 100644 index 000000000..c749422fb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/246-bgp-af-replaced-overridden-support.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - sonic_bgp_af - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/246). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/247-vxlans-replaced-overridden-support.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/247-vxlans-replaced-overridden-support.yaml new file mode 100644 index 000000000..3e395a184 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/247-vxlans-replaced-overridden-support.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_vxlans - Added support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/247) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/249-dhcp-relay-replaced-overridden-support.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/249-dhcp-relay-replaced-overridden-support.yaml new file mode 100644 index 000000000..72912650a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/249-dhcp-relay-replaced-overridden-support.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_dhcp_relay - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/249). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/250-bgp-as-paths-fix-merged-deleted.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/250-bgp-as-paths-fix-merged-deleted.yaml new file mode 100644 index 000000000..df69660e5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/250-bgp-as-paths-fix-merged-deleted.yaml @@ -0,0 +1,3 @@ +--- +bugfixes: + - sonic_bgp_as_paths - Fix issues with merged and deleted states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/250) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/253-change-replaced-function-for-ip-neighbor-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/253-change-replaced-function-for-ip-neighbor-module.yaml new file mode 100644 index 000000000..996c5ba80 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/253-change-replaced-function-for-ip-neighbor-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_ip_neighbor - Change the replaced function in ip_neighbor resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/253) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/254-update-replace-methods.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/254-update-replace-methods.yaml new file mode 100644 index 000000000..0e20c7ecf --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/254-update-replace-methods.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bfd, sonic_copp - Updated replaced methods (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/254) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/255-prefix_lists_replaced_overridden_support.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/255-prefix_lists_replaced_overridden_support.yaml new file mode 100644 index 000000000..a6d8731ed --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/255-prefix_lists_replaced_overridden_support.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_prefix_lists - Added support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/255) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/257-vrfs-cli-test-case-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/257-vrfs-cli-test-case-fix.yaml new file mode 100644 index 000000000..629a1973b --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/257-vrfs-cli-test-case-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - sonic_vrfs - Fixed spacing issue in CLI test case (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/257) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/258-change-logging-module-source-interface-naming.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/258-change-logging-module-source-interface-naming.yaml new file mode 100644 index 000000000..bddc22b99 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/258-change-logging-module-source-interface-naming.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_logging - change logging get facts for source_interface naming (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/258). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/261-interfaces-timeout-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/261-interfaces-timeout-fix.yaml new file mode 100644 index 000000000..73711a3d8 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/261-interfaces-timeout-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - sonic_interfaces - Fixed command timeout issue (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/261) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/262-vlan-mapping-bug-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/262-vlan-mapping-bug-fix.yaml new file mode 100644 index 000000000..2a27ef3a7 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/262-vlan-mapping-bug-fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - sonic_vlan_mapping - Removed platform checks (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/262) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/v2.1.0_summary.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/v2.1.0_summary.yaml new file mode 100644 index 000000000..01ff50fbd --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.1.0/v2.1.0_summary.yaml @@ -0,0 +1,8 @@ +release_summary: | + | Release Date: 2023-0515 + | This release provides enhanced Dell Enterprise SONiC Ansible Collection support for SONiC 4.x images. + | In addition to new resource modules to support previously existing functionality, it provides + | support for the "QinQ" (Vlan Mapping function introduced with SONiC release 4.1. It also provides + | bug fixes and enhancements for support of features that were initially introduced in previous + | Enterprise SONiC Ansible releases. The changelog describes changes made to the modules and plugins + | included in this collection since release 2.0.0. diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.2.0/269-revert-aaa-breaking-changes.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.2.0/269-revert-aaa-breaking-changes.yaml new file mode 100644 index 000000000..9cd346678 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.2.0/269-revert-aaa-breaking-changes.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_aaa - Reverted breaking changes for AAA nodule (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/269) diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.2.0/270-galaxy-yml-netcommon-and-version-fixes.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.2.0/270-galaxy-yml-netcommon-and-version-fixes.yaml new file mode 100644 index 000000000..733f96f5a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.2.0/270-galaxy-yml-netcommon-and-version-fixes.yaml @@ -0,0 +1,2 @@ +minor_changes: + - galaxy_yml - Enable installation of Ansible Netcomon versions after 5.0.0 and update the enterprise_sonic release version (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/270). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.2.0/v2.2.0_summary.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.2.0/v2.2.0_summary.yaml new file mode 100644 index 000000000..32016bf0e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.2.0/v2.2.0_summary.yaml @@ -0,0 +1,15 @@ +release_summary: | + | Release Date: 2023-0531 + | This release provides Ansible compliance changes required on top of the changes included in + | the 2.1.0 release of the enterprise_sonic Ansible network resource module collection. + | It addresses two issues raised by the Ansible core team with the content of the 2.1.0 release. + | 1) Back out the "breaking_change" made in the sonic_aaa resource module to fix a functional + | shortcoming in the enterprise_sonic Ansible collection. Although the change is still needed, + | it will be deferred to a "major" release. + | 2) Re-enable installation of new Ansible Netcommon repo instances when installing the + | enterprise_sonic Ansible collection. The 2.1.0 enterprise_sonic Ansible release included a + | workaround for a bug introduced in the 5.0.0 version of the Ansible Netcommon repo. This + | workaround was implemented in the "galaxy.yml" file for the enterprise_sonic + | 2.1.0 release. New versions of Ansible Netcommon were published after the problematic 5.0.0 + | version and the revised "galaxy.yml" file for this release enables installation of these + | newer versions. diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.4.0/321-requirements-update-meta-runtime-ansible-version.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.4.0/321-requirements-update-meta-runtime-ansible-version.yaml new file mode 100644 index 000000000..f12ddb105 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.4.0/321-requirements-update-meta-runtime-ansible-version.yaml @@ -0,0 +1,2 @@ +bugfixes: + - requirements - Update requires_ansible version in meta/runtime.yml to the oldest supported version (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/321). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.4.0/322-docs-README-updates.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.4.0/322-docs-README-updates.yaml new file mode 100644 index 000000000..6404045ed --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.4.0/322-docs-README-updates.yaml @@ -0,0 +1,2 @@ +trivial: + - requirements - Update the README file to document required Ansible and Python updates for this release (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/322). diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.4.0/v2.4.0_summary.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.4.0/v2.4.0_summary.yaml new file mode 100644 index 000000000..54bb92870 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.4.0/v2.4.0_summary.yaml @@ -0,0 +1,15 @@ +release_summary: | + | Release Date: 2024-0108 + | This release provides an Ansible compliance change required on top of the changes included in + | the 2.3.0 release of the enterprise_sonic Ansible network resource module collection. + | It addresses an issue raised by the Ansible core team with the content of the 2.3.0 release, + | and provides accompanying documentation changes in the README file. Additional details are + | described below. + | 1) Update the "requires_ansible" version in the meta/runtime.yml file for this collection + | to the oldest supported version of ansible-core. (This was recently changed by Redhat/Ansible + | to version "2.14.0".) + | 2) Update the README file "Recommended version" values for Ansible and Python in accordance + | with the previous change item to modify the oldest supported version of ansible-core which, + | in turn, requires a Python version >= "3.9". + | 3) Update the list of resource modules in the README file to include all currently available + | resource modules for this collection. diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/changelog.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/changelog.yaml index 0ce34f5e4..6c6dd1b91 100644 --- a/ansible_collections/dellemc/enterprise_sonic/changelogs/changelog.yaml +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/changelog.yaml @@ -2,88 +2,93 @@ ancestor: null releases: 1.0.0: modules: - - description: Perform REST operations through the Management Framework REST API. + - description: Manages REST operations on devices running Enterprise SONiC name: sonic_api namespace: '' - - description: BGP resource module. + - description: Manage global BGP and its parameters name: sonic_bgp namespace: '' - - description: BGP AF resource module. + - description: Manage global BGP address-family and its parameters name: sonic_bgp_af namespace: '' - - description: BGP AS path resource module. + - description: Manage BGP autonomous system path (or as-path-list) and its parameters name: sonic_bgp_as_paths namespace: '' - - description: BGP communities resource module. + - description: Manage BGP community and its parameters name: sonic_bgp_communities namespace: '' - - description: BGP Ext communities resource module. + - description: Manage BGP extended community-list and its parameters name: sonic_bgp_ext_communities namespace: '' - - description: BGP neighbors resource module. + - description: Manage a BGP neighbor and its parameters name: sonic_bgp_neighbors namespace: '' - - description: BGP neighbors AF resource module. + - description: Manage the BGP neighbor address-family and its parameters name: sonic_bgp_neighbors_af namespace: '' - - description: Run commands through Management Framework CLI. + - description: Runs commands on devices running Enterprise SONiC name: sonic_command namespace: '' - - description: Manage configuration through the Management Framework CLI. + - description: Manages configuration sections on devices running Enterprise SONiC name: sonic_config namespace: '' - - description: Interface resource module. + - description: Configure Interface attributes on interfaces such as, Eth, LAG, + VLAN, and loopback. (create a loopback interface if it does not exist.) name: sonic_interfaces namespace: '' - - description: Layer 2 interface resource module. + - description: Configure interface-to-VLAN association that is based on access + or trunk mode name: sonic_l2_interfaces namespace: '' - - description: Layer 3 interface resource module. + - description: Configure the IPv4 and IPv6 parameters on Interfaces such as, Eth, + LAG, VLAN, and loopback name: sonic_l3_interfaces namespace: '' - - description: Link aggregation (LAG) resource module. + - description: Manage link aggregation group (LAG) interface parameters name: sonic_lag_interfaces namespace: '' - - description: MCLAG resource module. + - description: Manage multi chassis link aggregation groups domain (MCLAG) and + its parameters name: sonic_mclag namespace: '' - - description: port breakout resource module. + - description: Configure port breakout settings on physical interfaces name: sonic_port_breakout namespace: '' - - description: USERS resource module. + - description: Manage users and its parameters name: sonic_users namespace: '' - - description: VLAN resource module. + - description: Manage VLAN and its parameters name: sonic_vlans namespace: '' - - description: VRF resource module. + - description: Manage VRFs and associate VRFs to interfaces such as, Eth, LAG, + VLAN, and loopback name: sonic_vrfs namespace: '' - - description: VxLAN EVPN resource module. + - description: Manage VxLAN EVPN and its parameters name: sonic_vxlans namespace: '' plugins: cliconf: - - description: Use Ansible CLICONF to run commands on Enterprise SONiC. + - description: Use sonic cliconf to run command on Dell OS10 platform name: sonic namespace: null httpapi: - - description: Use Ansible HTTPAPI to run commands on Enterprise SONiC. + - description: HttpApi Plugin for devices supporting Restconf SONIC API name: sonic namespace: null release_date: '2020-12-07' 1.1.0: modules: - - description: AAA resource module. + - description: Manage AAA and its parameters name: sonic_aaa namespace: '' - - description: RADIUS resource module. + - description: Manage RADIUS server and its parameters name: sonic_radius_server namespace: '' - - description: SYSTEM resource module. + - description: Configure system parameters name: sonic_system namespace: '' - - description: TACACS Server resource module. + - description: Manage TACACS server and its parameters name: sonic_tacacs_server namespace: '' release_date: '2021-05-28' @@ -95,63 +100,57 @@ releases: on the 1.x branch of this collection. This change corresponds to a SONiC 4.0 OC YANG REST compliance change for the BGP AF REST API. It enables specification of a route map in conjunction with each route advertisement prefix (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/63). - - bgp_af - remove the obsolete 'advertise_prefix' attribute from argspec and + - bgp_af - Remove the obsolete 'advertise_prefix' attribute from argspec and config code. This and subsequent co-req replacement with the new route advertise list argument structure require corresponding changes in playbooks previoulsly - used for configuring route advertise prefixes for SONiC 3.x images. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60) + used for configuring route advertise prefixes for SONiC 3.x images. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60). - bgp_neighbors - Replace the previously defined standalone "bfd" attribute with a bfd dictionary containing multiple attributes. This change corresponds to the revised SONiC 4.x implementation of OC YANG compatible REST APIs. Playbooks previously using the bfd attributes for SONiC 3.x images must be modified - for useon SONiC 4.0 images to use the new definition for the bfd attribute + for use on SONiC 4.0 images to use the new definition for the bfd attribute argspec structure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/72). - bgp_neighbors - Replace, for BGP peer groups, the previously defined standalone "bfd" attribute with a bfd dictionary containing multiple attributes. This change corresponds to the revised SONiC 4.x implementation of OC YANG compatible REST APIs. Playbooks previously using the bfd attributes for SONiC 3.x images - must be modified for useon SONiC 4.0 images to use the new definition for + must be modified for use on SONiC 4.0 images to use the new definition for the bfd attribute argspec structure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/81). bugfixes: - Fixed regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/103). - Fixed regression test sequencing and other regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/85). - - aaa - Fixed a bug in facts gathering by providing required conditional branching - (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90) - - aaa - Modify regression test sequencing to enable correct testing of the functionality - for this module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/78). - - bgp_neighbors - remove string conversion of timer attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60) + - bgp_neighbors - Remove string conversion of timer attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60). - port_breakout - Fixed a bug in formulation of port breakout REST APIs (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/88). - sonic - Fix a bug in handling of interface names in standard interface naming mode (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/103). + - sonic_aaa - Fix a bug in facts gathering by providing required conditional + branching (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90). + - sonic_aaa - Modify regression test sequencing to enable correct testing of + the functionality for this module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/78). - sonic_command - Fix bugs in handling of CLI commands involving a prompt and answer sequence (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/76/files). - users - Fixed a bug in facts gathering (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90). - vxlan - update Vxlan test cases to comply with SONiC behavior (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/105). - major_changes: - - Added 'static_routes' module to collection (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/82). - - Added a resource module for NTP support (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/99). - - Added a resource module for support of prefix lists (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/100). - - Updated backend REST API request formats in all applicable modules for compatibility - with SONiC 4.x openconfig YANG compliant REST APIs. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/53) minor_changes: - - Added an execution-environment.yml file to the "meta" directory to enable - use of Ansible execution environment infrastructure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/88). - - bgp_af - Added support for BGP options to configure usage and advertisement + - Add an execution-environment.yml file to the "meta" directory to enable use + of Ansible execution environment infrastructure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/88). + - bgp_af - Add support for BGP options to configure usage and advertisement of vxlan primary IP address related attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/62). - - bgp_as_paths - updated module examples with 'permit' attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102) + - bgp_as_paths - Update module examples with 'permit' attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102). - bgp_neighbors - Add BGP peer group support for multiple attributes. The added attributes correspond to the same set of attributes added for BGP neighbors with PR 72 (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/81). - - bgp_neighbors - Add support for multiple attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/72). - - bgp_neighbors - add an auth_pwd dictionary and nbr_description attribute to + - bgp_neighbors - Add an auth_pwd dictionary and nbr_description attribute to the argspec (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/67). - - bgp_neighbors - added prefix-list related peer-group attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101). - - bgp_neighbors_af - added prefix-list related neighbor attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101). - - playbook - updated examples to reflect module changes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102) + - bgp_neighbors - Add prefix-list related peer-group attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101). + - bgp_neighbors - Add support for multiple attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/72). + - bgp_neighbors_af - Add prefix-list related neighbor attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101). + - playbook - Update examples to reflect module changes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102). - sonic_vxlans - Add configuration capability for the primary IP address of a vxlan vtep to facilitate vxlan path redundundancy (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/58). - - vlans - Added support for the vlan "description" attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/98). - - workflow - Added stable-2.13 to the sanity test matrix (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90). + - vlans - Add support for the vlan "description" attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/98). + - workflow - Add stable-2.13 to the sanity test matrix (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90). release_summary: 'This release provides Dell SONiC Enterprise Ansible Collection support for SONiC 4.x images. It is the first release for the 2.x branch of the collection. Subsequent enhancements for support of SONiC 4.x images will @@ -195,3 +194,530 @@ releases: name: sonic_static_routes namespace: '' release_date: '2022-09-02' + 2.1.0: + changes: + breaking_changes: + - sonic_aaa - Add default_auth attribute to the argspec to replace the deleted + group and local attributes. This change allows for ordered login authentication. + (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/195). + bugfixes: + - Fix regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/180). + - Fix sanity check errors in the collection caused by Ansible library changes + (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/160). + - install - Update the required ansible.netcommon version (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/176). + - sonic_bgp_af - Fix issue with vnis and advertise modification for a single + BGP AF (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/201). + - sonic_bgp_as_paths - Fix issues with merged and deleted states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/250). + - sonic_interfaces - Fix command timeout issue (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/261). + - sonic_l3_interfaces - Fix IP address deletion issue (GitHub issue#170) (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/231). + - sonic_lag_interfaces - Fix port name issue (GitHub issue#153) (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/119). + - sonic_neighbors - Fix handling of default attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/233). + - sonic_ntp - Fix the issue (GitHub issue#205) with NTP clear all without config + given (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/224). + - sonic_vlan_mapping - Remove platform checks (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/262). + - sonic_vrfs - Add tasks as a workaround to mgmt VRF bug (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/146). + - sonic_vrfs - Fix spacing issue in CLI test case (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/257). + - sonic_vrfs - Fix the issue (GitHub issue#194) with VRF when deleting interface(https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/230). + - sonic_vxlans - Remove required_together restriction for evpn_nvo and source_ip + attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/130). + - workflows - Fix dependency installation issue in the code coverage workflow + (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/199). + minor_changes: + - module_utils - Change the location for importing remove_empties from the obsolete + Netcommon location to the offically required Ansible library location to fix + sanity errors (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/172). + - sonic_aaa - Add replaced and overridden states support for AAA resource module + (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/237). + - sonic_aaa - Add unit tests for AAA resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/198). + - sonic_api - Add unit tests for api resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/218). + - sonic_bfd, sonic_copp - Update replaced methods (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/254). + - sonic_bgp - Add rt_delay attribute to module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/244). + - sonic_bgp - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/240). + - sonic_bgp - Add unit tests for BGP resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/182). + - sonic_bgp_af - Add several attributes to support configuration of route distinguisher + and route target (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/141). + - sonic_bgp_af - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/246). + - sonic_bgp_af - Add unit tests for BGP AF resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/183). + - sonic_bgp_af - Modify BGP AF resource module unit tests to adjust for changes + in the resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/191). + - sonic_bgp_as_paths - Add unit tests for BGP AS paths resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/184). + - sonic_bgp_communities - Add unit tests for BGP communities resource module + (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/185). + - sonic_bgp_ext_communities - Add unit tests for BGP ext communities resource + module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/186). + - sonic_bgp_neighbors - Add unit tests for BGP neighbors resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/187). + - sonic_bgp_neighbors - Enhance unit tests for BGP Neighbors resource module + (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/245). + - sonic_bgp_neighbors_af - Add unit tests for BGP neighbors AF resource module + (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/188). + - sonic_command - Add unit tests for command resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/219). + - sonic_config - Add unit tests for config resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/220). + - sonic_dhcp_relay - Add a common unit tests module and unit tests for dhcp + relay module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/148). + - sonic_dhcp_relay - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/249). + - sonic_facts - Add unit tests for facts resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/222). + - sonic_interfaces - Add speed, auto-negotiate, advertised-speed and FEC to + interface resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/128). + - sonic_interfaces - Add unit tests for interfaces resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/197). + - sonic_ip_neighbor - Add unit tests for IP neighbor resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/225). + - sonic_ip_neighbor - Change the replaced function in ip_neighbor resource module + (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/253). + - sonic_l2_interfaces - Add support for parsing configuration containing the + OC Yang vlan range syntax (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/124). + - sonic_l2_interfaces - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/221). + - sonic_l2_interfaces - Add support for specifying vlan trunk ranges in Ansible + playbooks (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/149). + - sonic_l2_interfaces - Add unit tests for l2_interfaces resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/200). + - sonic_l3_interfaces - Add unit tests for l3_interfaces resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/202). + - sonic_lag_interface - Add replaced and overridden states support for LAG interface + resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/196). + - sonic_lag_interfaces - Add unit tests for lag_interfaces resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/203). + - sonic_logging - Add replaced and overridden states support for logging resource + module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/150). + - sonic_logging - Add unit tests for logging resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/226). + - sonic_logging - Change logging get facts for source_interface naming (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/258). + - sonic_mclag - Add delay_restore, gateway_mac, and peer_gateway attributes + to module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/145). + - sonic_ntp - Add prefer attribute to NTP resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/118). + - sonic_ntp - Add replaced and overridden states support for NTP resource module + (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/151). + - sonic_ntp - Add unit tests for NTP resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/207). + - sonic_ntp - Change NTP get facts to get default parameters (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/106). + - sonic_ntp - Change NTP key values in NTP regression test script (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/107). + - sonic_ntp - Change NTP module name (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/113). + - sonic_ntp - Change NTP module names in NTP regression test script (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/114). + - sonic_ntp - Change NTP resource module to make minpoll and maxpoll be configured + together (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/129). + - sonic_port_breakout - Add unit tests for port breakout resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/229). + - sonic_port_group - Add replaced and overridden states support for port group + resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/227). + - sonic_port_group - Add unit tests for port group resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/228). + - sonic_prefix_lists - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/255). + - sonic_prefix_lists - Add unit tests for prefix lists resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/209). + - sonic_radius_server - Add replaced and overridden states support for RADIUS + server resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/239). + - sonic_radius_server - Add unit tests for RADIUS server resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/210). + - sonic_static_routes - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/236). + - sonic_static_routes - Add unit tests for static routes resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/212). + - sonic_system - Add replaced and overridden states support for system resource + module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/159). + - sonic_system - Add unit tests for system resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/223). + - sonic_tacacs_server - Add replaced and overridden states support for TACACS + server resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/235). + - sonic_tacacs_server - Add unit tests for TACACS server resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/208). + - sonic_users - Add replaced and overridden states support for users resource + module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/242). + - sonic_users - Add unit tests for users resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/213). + - sonic_vlans - Add replaced and overridden states support for VLAN resource + module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/217). + - sonic_vlans - Add unit tests for Vlans resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/214). + - sonic_vrfs - Add replaced and overridden states support for VRF resource module + (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/156). + - sonic_vrfs - Add unit tests for VRFS resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/216). + - sonic_vxlans - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/247). + - sonic_vxlans - Add unit tests for VxLans resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/215). + release_summary: '| Release Date: 2023-0515 + + | This release provides enhanced Dell Enterprise SONiC Ansible Collection + support for SONiC 4.x images. + + | In addition to new resource modules to support previously existing functionality, + it provides + + | support for the "QinQ" (Vlan Mapping) function introduced with SONiC release + 4.1. It also provides + + | bug fixes and enhancements for support of features that were initially introduced + in previous + + | Enterprise SONiC Ansible releases. The changelog describes changes made + to the modules and plugins + + | included in this collection since release 2.0.0. + + ' + fragments: + - 106-change-ntp-get-fact.yaml + - 107-change-ntp-key-values-in-regression-script.yaml + - 113-change-ntp-module-name.yaml + - 114-change-ntp-module-name-in-regression-script.yaml + - 118-add-ntp-prefer-attribute.yaml + - 119-lag_interfaces-port-name-fix.yaml + - 124-sonic_l2_interfaces-oc-yang-vlan-range-format-config-support.yaml + - 128-add-several-attributes-to-interface-resource-module.yaml + - 129-ntp-minpoll-maxpoll-config.yaml + - 130-vxlans-attribute-restriction-fix.yaml + - 141-add-route-distinguisher-target-attributes-to-bgp-af-module.yaml + - 145-mclag-new-attributes.yaml + - 146-vrf-mgmt-bug-fix.yaml + - 148-dhcp-relay-unit-tests.yaml + - 149-l2-interfaces-vlan-trunk-range-support-for-playbooks.yaml + - 150-replaced-overridden-for-logging-resource-module.yaml + - 151-replaced-overridden-for-ntp-resource-module.yaml + - 152-copp.yaml + - 156-replaced-overridden-for-vrfs-resource-module.yaml + - 157-mac.yaml + - 159-replaced-overridden-for-system-resource-module.yaml + - 160-sanity-check-errors-fix.yaml + - 172-module-utils-import-remove-empties-from-ansible-lib.yaml + - 176-update-netcommon-version.yaml + - 177-bfd.yaml + - 180-regression-failures-fix.yaml + - 182-unit-tests-for-bgp-resource-module.yaml + - 183-unit-tests-for-bgp-af-resource-module.yaml + - 184-unit-tests-for-bgp-as-paths-resource-module.yaml + - 185-unit-tests-for-bgp-communities-resource-module.yaml + - 186-unit-tests-for-bgp-ext-communities-resource-module.yaml + - 187-unit-tests-for-bgp-neighbors-resource-module.yaml + - 188-unit-tests-for-bgp-neighbors-af-resource-module.yaml + - 191-unit-tests-for-bgp-af-resource-module.yaml + - 195-aaa-login-authentication.yaml + - 196-replaced-overridden-for-lag-interface-resource-module.yaml + - 197-unit-tests-for-interfaces-resource-module.yaml + - 198-unit-tests-for-aaa-resource-module.yaml + - 199-code-coverage-workflow-fix.yaml + - 200-unit-tests-for-l2-interfaces-resource-module.yaml + - 201-bgp-af-modify-vni-advertise-fix.yaml + - 202-unit-tests-for-l3-interfaces-resource-module.yaml + - 203-unit-tests-for-lag-interfaces-resource-module.yaml + - 207-unit-tests-for-ntp-resource-module.yaml + - 208-unit-tests-for-tacacs-server-resource-module.yaml + - 209-unit-tests-for-prefix-lists-resource-module.yaml + - 210-unit-tests-for-radius-server-resource-module.yaml + - 212-unit-tests-for-static-routes-resource-module.yaml + - 213-unit-tests-for-users-resource-module.yaml + - 214-unit-tests-vlans-resource-module.yaml + - 215-unit-tests-for-vxlans-resource-module.yaml + - 216-unit-tests-for-vrfs-resource-module.yaml + - 217-replaced-overridden-for-vlans-resource-module.yaml + - 218-unit-tests-for-api-resource-module.yaml + - 219-unit-tests-for-command-resource-module.yaml + - 220-unit-tests-for-config-resource-module.yaml + - 221-l2-interfaces-replaced-overridden-support.yaml + - 222-unit-tests-for-facts-resource-module.yaml + - 223-unit-tests-for-system-resource-module.yaml + - 224-ntp-clear-all-fix.yaml + - 225-unit-tests-for-ip-neighbor-resource-module.yaml + - 226-unit-tests-for-logging-resource-module.yaml + - 227-replaced-overridden-for-port-group-resource-module.yaml + - 228-unit-tests-for-port-group-resource-module.yaml + - 229-unit-tests-for-port-breakout-resource-module.yaml + - 230-vrfs-delete-interface-fix.yaml + - 231-l3-interfaces-delete-fix.yaml + - 233-bgp-neighbors-defaults-fix.yaml + - 235-replaced-overridden-for-tacacs-server-resource-module.yaml + - 236-static-routes-replaced-overridden-support.yaml + - 237-aaa-replaced-overridden-support.yaml + - 239-replaced-overridden-for-radius-server-resource-module.yaml + - 240-bgp-replaced-overridden-support.yaml + - 242-users-replaced-overridden-support.yaml + - 244-added-rt-delay-attribute-to-bgp-module.yaml + - 245-enhance-bgp-neighbors-unit-tests.yaml + - 246-bgp-af-replaced-overridden-support.yaml + - 247-vxlans-replaced-overridden-support.yaml + - 249-dhcp-relay-replaced-overridden-support.yaml + - 250-bgp-as-paths-fix-merged-deleted.yaml + - 253-change-replaced-function-for-ip-neighbor-module.yaml + - 254-update-replace-methods.yaml + - 255-prefix_lists_replaced_overridden_support.yaml + - 257-vrfs-cli-test-case-fix.yaml + - 258-change-logging-module-source-interface-naming.yaml + - 261-interfaces-timeout-fix.yaml + - 262-vlan-mapping-bug-fix.yaml + - v2.1.0_summary.yaml + modules: + - description: Manage access control list (ACL) to interface binding on SONiC + name: sonic_acl_interfaces + namespace: '' + - description: Manage BFD configuration on SONiC + name: sonic_bfd + namespace: '' + - description: Manage CoPP configuration on SONiC + name: sonic_copp + namespace: '' + - description: Manage DHCP and DHCPv6 relay configurations on SONiC + name: sonic_dhcp_relay + namespace: '' + - description: Manage IP neighbor global configuration on SONiC + name: sonic_ip_neighbor + namespace: '' + - description: Manage Layer 2 access control lists (ACL) configurations on SONiC + name: sonic_l2_acls + namespace: '' + - description: Manage Layer 3 access control lists (ACL) configurations on SONiC + name: sonic_l3_acls + namespace: '' + - description: Manage Global LLDP configurations on SONiC + name: sonic_lldp_global + namespace: '' + - description: Manage logging configuration on SONiC + name: sonic_logging + namespace: '' + - description: Manage MAC configuration on SONiC + name: sonic_mac + namespace: '' + - description: Manages port group configuration on SONiC + name: sonic_port_group + namespace: '' + - description: route map configuration handling for SONiC + name: sonic_route_maps + namespace: '' + - description: Configure vlan mappings on SONiC + name: sonic_vlan_mapping + namespace: '' + release_date: '2023-05-15' + 2.2.0: + changes: + minor_changes: + - galaxy_yml - Enable installation of Ansible Netcomon versions after 5.0.0 + and update the enterprise_sonic release version (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/270). + - sonic_aaa - Revert breaking changes for AAA nodule (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/269). + release_summary: '| Release Date: 2023-06-01 + + | This release provides Ansible compliance changes required on top of the + changes included in + + | the 2.1.0 release of the enterprise_sonic Ansible network resource module + collection. + + | It addresses two issues raised by the Ansible core team with the content + of the 2.1.0 release. + + | 1) Back out the "breaking_change" made in the sonic_aaa resource module + to fix a functional + + | shortcoming in the enterprise_sonic Ansible collection. Although the change + is still needed, + + | it will be deferred to a "major" release. + + | 2) Re-enable installation of new Ansible Netcommon repo instances when installing + the + + | enterprise_sonic Ansible collection. The 2.1.0 enterprise_sonic Ansible + release included a + + | workaround for a bug introduced in the 5.0.0 version of the Ansible Netcommon + repo. This + + | workaround was implemented in the "galaxy.yml" file for the enterprise_sonic + + | 2.1.0 release. New versions of Ansible Netcommon were published after the + problematic 5.0.0 + + | version and the revised "galaxy.yml" file for this release enables installation + of these + + | newer versions. + + ' + fragments: + - 269-revert-aaa-breaking-changes.yaml + - 270-galaxy-yml-netcommon-and-version-fixes.yaml + - v2.2.0_summary.yaml + release_date: '2023-05-31' + 2.3.0: + changes: + bugfixes: + - sonic_bgp_communities - Fix incorrect "facts" handling for parsing of a BGP + community list configured with an empty "members" list (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/319). + - sonic_bgp_neighbors - Fix prefix-limit issue (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/289). + - sonic_interfaces - Add warnings when speed and auto_negotiate is configured + at same time (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/314). + - sonic_interfaces - Fix support for standard naming interfaces (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/314). + - sonic_interfaces - Prevent configuring speed in port group interfaces (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/314). + - sonic_stp - Correct the commands list for STP delete state (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/302). + minor_changes: + - sonic_aaa - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/304). + - sonic_aaa - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_acl_interfaces - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/306). + - sonic_acl_interfaces - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_bgp_as_paths - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/290). + - sonic_bgp_communities - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/251). + - sonic_bgp_ext_communities - Add support for replaced and overridden states + (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/252). + - sonic_interfaces - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/301). + - sonic_interfaces - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/314). + - sonic_interfaces - Change deleted design for interfaces module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/310). + - sonic_interfaces - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_ip_neighbor - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/285). + - sonic_ip_neighbor - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_l2_acls - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/306). + - sonic_l2_acls - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_l2_interfaces - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/303). + - sonic_l2_interfaces - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_l3_acls - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/306). + - sonic_l3_acls - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_l3_interfaces - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/241). + - sonic_lag_interfaces - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/303). + - sonic_lag_interfaces - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_logging - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/285). + - sonic_logging - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_mclag - Add VLAN range support for 'unique_ip' and 'peer_gateway' options + (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/288). + - sonic_mclag - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/288). + - sonic_ntp - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/281). + - sonic_ntp - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_port_breakout - Add Ansible support for all port breakout modes now + allowed in Enterprise SONiC (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/276). + - sonic_port_breakout - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/291). + - sonic_port_group - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/284). + - sonic_port_group - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_radius_server - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/279). + - sonic_radius_server - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_static_routes - Add playbook check and diff modes support for static + routes resource module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/313). + - sonic_static_routes - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_system - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/284). + - sonic_system - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_tacacs_server - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/281). + - sonic_tacacs_server - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_users - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/304). + - sonic_users - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_vlans - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/301). + - sonic_vlans - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - sonic_vrfs - Add mgmt VRF replaced state handling to sonic_vrfs module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/298). + - sonic_vrfs - Add mgmt VRF support to sonic_vrfs module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/293). + - sonic_vrfs - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/285). + - sonic_vrfs - Enhance config diff generation function (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/318). + - tests - Add UTs for BFD, COPP, and MAC modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/287). + - tests - Enable contiguous execution of all regression integration tests on + an S5296f (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/277). + - tests - Fix the bgp CLI test base_cfg_path derivation of the bgp role_path + by avoiding relative pathing from the possibly external playbook_dir (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/283). + release_summary: '| Release Date: 2024-0103 + + | This release provides the functionality enhancements listed below, along + with fixes for + + | problems found in regression testing or reported by users. The main functionality + enhancements + + | provided are the following items. + + | 1) Complete the support for "replaced" and "overridden" state handling for + all resource modules except for the bgp_neighbors and bgp_neighbors_af modules. + + | With this release, the required support has been added for any resource + modules that were not + + | provided with this support for the 2.1.0 release with the two exceptions + noted above. + + | 2) Provide initial support for the "--check" and "--diff" mode options for + playbook execution. This + + | release provides the common utility support for these options for use by + all resource modules. + + | It also provides the specific resource module changes required for implementation + of the + + | functionality in many of the existing resource modules. (The "--check" and + "--diff" mode support + + | for the remaining resource modules is planned for inclusion in the next + release.) + + | 3) New resource modules for "Public Key Infrastructure", STP, and DHCP Snooping. + + | 4) Support for "ranges" of vlans (e.g ''2-100'') in tasks for the mclag + resource module. + + | Please refer to the "CHANGELOG.rst" file at the top directory level of this + repo for additional + + | details on the contents of this release. + + ' + fragments: + - 241-replaced-overridden-for-l3-interfaces-module.yaml + - 251-replaced-overridden-for-bgp-communities-module.yaml + - 252-replaced-overridden-for-bgp-ext-communities-module.yaml + - 274-add-changelog-workflow.yaml + - 276-add-new-port-breakout-modes.yaml + - 277-enable-running-all-regression-tests-on-s5296.yaml + - 279-playbook-check-diff-modes-and-radius-server-implement.yaml + - 281-playbook-check-diff-modes-for-ntp-module.yaml + - 282-update-changelog-workflow.yaml + - 283-bgp-cli-auto-regression-fix.yaml + - 284-playbook-check-diff-modes-for-system-and-port-group-modules.yaml + - 285-playbook-check-diff-modes-for-vrfs-logging-ip-neighbor.yaml + - 287-add-uts-for-bfd-copp-mac.yaml + - 288-mclag-replaced-overridden-vlan-range-support.yaml + - 289-bgp-neighbors-prefix-limit-fix.yaml + - 290-bgp-as-paths-replaced-overridden-support.yaml + - 291-port-breakout-replaced-overridden-support.yaml + - 293-add-mgmt-vrf-handling-in-vrfs-module.yaml + - 294-verbose-regression-report-template.yaml + - 298-add-mgmt-vrf-replaced-state-handling-in-vrfs-module.yaml + - 301-playbook-check-diff-modes-for-vlans-interfaces.yaml + - 302-stp-commands-delete-state-fix.yaml + - 303-playbook-check-diff-modes-for-l2-lag-interfaces.yaml + - 304-playbook-check-diff-modes-for-users-aaa.yaml + - 306-playbook-check-diff-modes-for-acls.yaml + - 310-change-deleted-design-for-interfaces-module.yaml + - 313-laybook-check-diff-modes-for-static-route.yaml + - 314-replaced-overridden-for-interfaces-module.yaml + - 318-playbook-diff-mode-enhancement.yaml + - 319-bgp-communities-no-member-facts-fix.yaml + - v2.3.0_summary.yaml + modules: + - description: Manage DHCP Snooping on SONiC + name: sonic_dhcp_snooping + namespace: '' + - description: Manages PKI attributes of Enterprise Sonic + name: sonic_pki + namespace: '' + - description: Manage STP configuration on SONiC + name: sonic_stp + namespace: '' + release_date: '2024-01-03' + 2.4.0: + changes: + bugfixes: + - requirements - Update requires_ansible version in meta/runtime.yml to the + oldest supported version (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/321). + release_summary: '| Release Date: 2024-0108 + + | This release provides an Ansible compliance change required on top of the + changes included in + + | the 2.3.0 release of the enterprise_sonic Ansible network resource module + collection. + + | It addresses an issue raised by the Ansible core team with the content of + the 2.3.0 release, + + | and provides accompanying documentation changes in the README file. Additional + details are + + | described below. + + | 1) Update the "requires_ansible" version in the meta/runtime.yml file for + this collection + + | to the oldest supported version of ansible-core. (This was recently changed + by Redhat/Ansible + + | to version "2.14.0".) + + | 2) Update the README file "Recommended version" values for Ansible and Python + in accordance + + | with the previous change item to modify the oldest supported version of + ansible-core which, + + | in turn, requires a Python version >= "3.9". + + | 3) Update the list of resource modules in the README file to include all + currently available + + | resource modules for this collection. + + ' + fragments: + - 321-requirements-update-meta-runtime-ansible-version.yaml + - 322-docs-README-updates.yaml + - v2.4.0_summary.yaml + release_date: '2024-01-08' diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/config.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/config.yaml index b857f936f..158fea109 100644 --- a/ansible_collections/dellemc/enterprise_sonic/changelogs/config.yaml +++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/config.yaml @@ -1,4 +1,4 @@ -changelog_filename_template: CHANGELOG.rst +changelog_filename_template: ../CHANGELOG.rst changelog_filename_version_depth: 0 changes_file: changelog.yaml changes_format: combined diff --git a/ansible_collections/dellemc/enterprise_sonic/docs/ADDITIONAL_INFORMATION.md b/ansible_collections/dellemc/enterprise_sonic/docs/ADDITIONAL_INFORMATION.md new file mode 100644 index 000000000..6b2a34808 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/docs/ADDITIONAL_INFORMATION.md @@ -0,0 +1,15 @@ +Additional Information +## Release cadence +<release cadence> + +<strong>TBD </strong> + +## Versioning +* This product releases follow [semantic versioning](https://semver.org/). + +## Deprecation +* <Product> deprecation cycle is aligned with [Ansible](https://docs.ansible.com/ansible/latest/dev_guide/module_lifecycle.html). + +<Any other additional information, +E.g. +Enabling/Ignoring SSL certificate validation>
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/docs/BRANCHING.md b/ansible_collections/dellemc/enterprise_sonic/docs/BRANCHING.md new file mode 100644 index 000000000..2fd9f9570 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/docs/BRANCHING.md @@ -0,0 +1 @@ +TBD
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/docs/COMMITTER_GUIDE.md b/ansible_collections/dellemc/enterprise_sonic/docs/COMMITTER_GUIDE.md new file mode 100644 index 000000000..5ee9da4b1 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/docs/COMMITTER_GUIDE.md @@ -0,0 +1,43 @@ +<!-- +Copyright (c) 2022 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the GPL, Version 3.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.gnu.org/licenses/gpl-3.0.txt +--> + +# Committer Guidelines + +These are the guidelines for people with commit privileges on the GitHub repository. Committers act as members of the Core Team and not necessarily employees of Dell. + +These guidelines apply to everyone and as Committers you have been given access to commit changes because you exhibit good judgment and have demonstrated your commitment to the vision of the project. We trust that you will use these privileges wisely and not abuse it. + +If these privileges are abused in any way and the quality of the project is compromised, our trust will be diminished and you may be asked to not commit or lose these privileges all together. + +## General Rules + +### Don't + +* Introduce Ansible sanity failures. +* Introduce enterprise_sonic regression test failures. +* Commit directly. +* Compromise backward compatibility. +* Disrespect your Community Team members. Help them grow. +* Think it is someone elses job to test your code. Write tests for all the code you produce. +* Forget to keep thing simple. +* Create technical debt. Fix-in-place and make it the highest priority above everything else. + + +### Do + +* Always follow the defined coding guideline +* Keep the design of your software clean and maintainable. +* Squash your commits, avoid merges. +* Write tests for all your deliverables. +* Automate everything. +* Maintain a high code coverage, equal to or greater than 90%. +* Keep an open communication with other Committers. +* Ask questions. +* Document your contributions and remember to keep it simple. diff --git a/ansible_collections/dellemc/enterprise_sonic/docs/CONTRIBUTING.md b/ansible_collections/dellemc/enterprise_sonic/docs/CONTRIBUTING.md new file mode 100644 index 000000000..3109bbcbc --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/docs/CONTRIBUTING.md @@ -0,0 +1,207 @@ +<!-- +Copyright (c) 2022 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the GPL, Version 3.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.gnu.org/licenses/gpl-3.0.txt +--> + +# How to Contribute + +Become one of the contributors to this project! We strive to build a welcoming and open community for anyone who wants to use the project or contribute to it. There are just a few small guidelines you need to follow. To help us create a safe and positive community experience for all, we require all participants to adhere to the [Code of Conduct](CODE_OF_CONDUCT.md). + +## Table of Contents + +* [Become a contributor](#Become-a-contributor) +* [Contributor agreement](#Contributor-Agreement) +* [Submitting issues](#Submitting-issues) +* [Triage issues](#Triage-issues) +* [Your first contribution](#Your-first-contribution) +* [Branching](#Branching) +* [Signing your commits](#Signing-your-commits) +* [Pull requests](#Pull-requests) +* [Code reviews](#Code-reviews) +* [Code Style](#Code-Style) + +## Become a contributor + +You can contribute to this project in several ways. Here are some examples: + +* Contribute to the project documentation and codebase. +* Report and triage bugs. +* Feature requests +* Write technical documentation and blog posts for users and contributors. +* Help others by answering questions about this project. + +## Contributor Agreement +All contributions shall be made under the Developer Certification of Origin ("DCO") (see http://elinux.org/Developer_Certificate_Of_Origin) which is reproduced below. Specifically, the Git commit message for the contribution should contain the following tag information signifying use of the DCO: + + +"Signed-off-by: [Your Name] [youremail@company.com]" + + +________________________________________ +### Developer's Certificate of Origin 1.1 +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or + +(b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or + +(c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. + +(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. + +## Submitting issues + +All issues related to the associated Ansible modules, roles, playbooks, regardless of the service/repository the issue belongs to (see table above), should be submitted [here](https://github.com/ansible-collections/dellemc.enterprise_sonic/issues). Issues will be triaged and labels will be used to indicate the type of issue. This section outlines the types of issues that can be submitted. + +### Report bugs + +We aim to track and document everything related to the repository via the Issues page. The code and documentation are released with no warranties or SLAs and are intended to be supported through a community driven process. + +Before submitting a new issue, make sure someone hasn't already reported the problem. Look through the [existing issues](https://github.com/ansible-collections/dellemc.enterprise_sonic/issues) for similar issues. + +Report a bug by submitting a [bug report](https://github.com/ansible-collections/dellemc.enterprise_sonic/issues/new?labels=type%2Fbug%2C+needs-triage&template=bug_report.md&title=%5BBUG%5D%3A). Make sure that you provide as much information as possible on how to reproduce the bug. + +When opening a Bug please include the following information to help with debugging: + +1. Version of relevant software: this software, Python version, Dell Server/Storage Platform, etc. +2. Details of the issue explaining the problem: what, when, where +3. The expected outcome that was not met (if any) +4. Supporting troubleshooting information. __Note: Do not provide private company information that could compromise your company's security.__ + +An Issue __must__ be created before submitting any pull request. Any pull request that is created should be linked to an Issue. + +### Feature request + +If you have an idea of how to improve this project, submit a [feature request](https://github.com/ansible-collections/dellemc.enterprise_sonic/issues/new?labels=type%2Ffeature-request%2C+needs-triage&template=feature_request.md&title=%5BFEATURE%5D%3A). + +### Answering questions + +If you have a question and you can't find the answer in the documentation or issues, the next step is to submit a [question.](https://github.com/ansible-collections/dellemc.enterprise_sonic/issues/new?labels=type%2Fquestion&template=ask-a-question.md&title=%5BQUESTION%5D%3A) + +We'd love your help in answering pending questions asked by other CSM users. + +## Triage issues + +Triage helps ensure that issues resolve quickly by: + +* Ensuring the issue's intent and purpose is conveyed precisely. This is necessary because it can be difficult for an issue to explain how an end user experiences a problem and what actions they took. +* Giving a contributor the information they need before they commit to resolving an issue. +* Streamlining the bug fixing process by eliminating duplicate issues. + + +If you don't have the knowledge or time to code, consider helping with _issue triage_. The Dell dellemc.enterprise_sonic community will thank you for saving them time by spending some of yours. + +Read more about the ways you can [Triage issues](ISSUE_TRIAGE.md). + +## Testing + +See [here](https://github.com/ansible-collections/dellemc.enterprise_sonic/tree/collections/README.md) for further information on testing. + +## Debugging +To debug <product> using IDE, see [here](https://docs.ansible.com/ansible/latest/dev_guide/debugging.html) + + +## Your first contribution + +Unsure where to begin contributing? Start by browsing issues labeled `beginner friendly` or `help wanted`. + +* [Beginner-friendly](https://github.com/ansible-collections/dellemc.enterprise_sonic/issues?q=is%3Aopen+is%3Aissue+label%3A%22beginner+friendly%22) issues are generally straightforward to complete. +* [Help wanted](https://github.com/ansible-collections/dellemc.enterprise_sonic/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) issues are problems we would like the community to help us with regardless of complexity. + +When you're ready to contribute, it's time to create a pull request. + +## Branching + +* [Branching Strategy](BRANCHING.md) + +## Signing your commits + +We require that developers sign off their commits to certify that they have permission to contribute the code in a pull request. This way of certifying is commonly known as the [Developer Certificate of Origin (DCO)](https://developercertificate.org/). We encourage all contributors to read the DCO text before signing a commit and making contributions. + +GitHub will prevent a pull request from being merged if there are any unsigned commits. + +### Signing a commit + +GPG (GNU Privacy Guard) will be used to sign commits. Follow the instructions [here](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/signing-commits) to create a GPG key and configure your GitHub account to use that key. + +Make sure you have your username and e-mail set. This will be required for your signed commit to be properly verified. Check the following references: + +* Setting up your github username [reference](https://help.github.com/articles/setting-your-username-in-git/) +* Setting up your e-mail address [reference](https://help.github.com/articles/setting-your-commit-email-address-in-git/) + +Once Git and your GitHub account have been properly configured, you can add the -S flag to the git commits: + +```console +$ git commit -S -m your commit message +# Creates a signed commit +``` + +### Commit message format + +This repository uses the guidelines for commit messages outlined in [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) + +## Pull Requests + +If this is your first time contributing to an open-source project on GitHub, make sure you read about [Creating a pull request](https://help.github.com/en/articles/creating-a-pull-request). + +A pull request must always link to at least one GitHub issue. If that is not the case, create a GitHub issue and link it. + +To increase the chance of having your pull request accepted, make sure your pull request follows these guidelines: + +* Title and description matches the implementation. +* Commits within the pull request follow the formatting guidelines. +* The pull request closes one related issue. +* The pull request contains necessary tests that verify the intended behavior. +* If your pull request has conflicts, rebase your branch onto the main branch. + +If the pull request fixes a bug: + +* The pull request description must include `Fixes #<issue number>`. +* To avoid regressions, the pull request should include tests that replicate the fixed bug. + +The owning team _squashes_ all commits into one when we accept a pull request. The title of the pull request becomes the subject line of the squashed commit message. We still encourage contributors to write informative commit messages, as they become a part of the Git commit body. + +We use the pull request title when we generate change logs for releases. As such, we strive to make the title as informative as possible. + +Make sure that the title for your pull request uses the same format as the subject line in the commit message. + +### Quality Gates for pull requests + +GitHub Actions are used to enforce quality gates when a pull request is created or when any commit is made to the pull request. These GitHub Actions enforce our minimum code quality requirement for any code that gets checked into the code repository. If any of the quality gates fail, it is expected that the contributor will look into the check log, understand the problem and resolve the issue. If help is needed, please feel free to reach out to the maintainers of the project for [support](https://github.com/ansible-collections/dellemc.enterprise_sonic/blob/main/docs/SUPPORT.md). + +#### Code build/test/coverage + +[GitHub action](https://github.com/ansible-collections/dellemc.enterprise_sonic/actions) that runs unit tests automatically and checks the code coverage tool, [code-coverage.yml](https://github.com/ansible-collections/dellemc.enterprise_sonic/blob/main/.github/workflows/code-coverage.yml), runs unit tests automatically and checks that the code coverage of each package meets a configured threshold (currently 90%). An error is flagged if a given pull request does not meet the test coverage threshold and blocks the pull request from being merged. When it fails, it is expected that the contributor will look into the log, understand the problem and resolve the issue. + +Alternatively, users can manually run the unit test and check the coverage using the 'ansible-test' command as given in the following example. These commands must be run from the Ansible collection "install" directory for the enterprise sonic collection (e.g. "cd ~/.ansible/collections/ansible_collections/dellemc/enterprise_sonic"). + +``` +ansible-test units --color --python 3.10 --coverage -vvvv tests/unit/modules/network/sonic/test_sonic_bgp_neighbors.py +ansible-test coverage report + +``` + +## Code Reviews + +All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on using pull requests. + +A pull request must satisfy the following for it to be merged: + +* A pull request will require at least 2 maintainer approvals. +* Maintainers must perform a review to ensure the changes adhere to guidelines laid out in this document. +* If any commits are made after the PR has been approved, the PR approval will automatically be removed and the above process must happen again. + +## Code Style + +For the Python code in the repository, we expect the code styling outlined in [Ansible python guide]( https://docs.ansible.com/ansible/latest/dev_guide/developing_python_3.html). In addition to this, we have the following supplements: + +* Contributions should adhere to ansible Coding standard guidelines as we follow these standards. +* Should include [test](https://github.com/ansible-collections/dellemc.enterprise_sonic/tree/main/tests) scripts for your changes. +* Do not submit a contribution request on our deprecated modules. They are just meant for backward compatibility. + + diff --git a/ansible_collections/dellemc/enterprise_sonic/docs/ISSUE_TRIAGE.md b/ansible_collections/dellemc/enterprise_sonic/docs/ISSUE_TRIAGE.md new file mode 100644 index 000000000..09cd93ee0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/docs/ISSUE_TRIAGE.md @@ -0,0 +1,291 @@ +<!-- +Copyright (c) 2022 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the GPL, Version 3.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.gnu.org/licenses/gpl-3.0.txt +--> + +# Triage issues + +The main goals of an issue triage are to categorize open issues based on priority and to ensure that these issues contain adequate information for contributors to work on them. + +> **Note:** This information is for project Maintainers, Owners, and Admins. If you are a Contributor, then you will not be able to perform most of the tasks in this topic. + +The core maintainers of this project are responsible for categorizing all incoming issues and delegating any critical or important issue to other maintainers. Triage provides an important way to contribute to an open source project. + +A triage helps resolve issues quickly by : + +- Ensuring the intent of an issue is clear. The issue should clearly describe the problem the end user is experiencing as well as the steps to reproduce the problem. +- Giving a contributor the information they need before they commit to resolving an issue. +- Streamlining the development process by identifying and closing duplicate issues. + +If you don't have the knowledge or time to code, consider helping with triage. The community will thank you for saving them time by spending some of yours. + +## 1. Find issues that need triage + +The easiest way to find issues that haven't been triaged is to search for issues with the `needs-triage` label. + +## 2. Ensure the issue contains basic information + +When creating a new issue, the author must provide all of the information required by the project's GitHub issue template + +### Standard issue information that must be included + +The following section describes the various issue templates and the expected content. + +#### Bug reports + +A bug report should contain explanation of what the problem is, what the expected outcome is, and how to reproduce the problem. Additionally, any other supporting information such as screenshots, console logs, and environment details should be included in the bug report. + + - Product Name/Version + - Component/Module Name + - Ansible Collection Version + - Configuration + - Steps to Reproduce + - Expected/Actual Behavior + - Logs/Screenshots + - Any other additional information... + +#### Feature requests + +Should explain what feature that the author wants to be added and why that is needed. + +#### Ask a Question requests + +In general, if the issue description and title are perceived as questions no more information is needed. + +### Good practices + +To make issue handling easier for everyone, it is suggested to: + +- Make sure that issue titles are named to explain the subject of the issue, have correct spelling, and don't include irrelevant information and/or sensitive information. +- Make sure that issue descriptions don't include irrelevant information. +- Make sure that issues do not contain sensitive information. +- Make sure that issues have all relevant fields filled in. +- If an issue is unclear, then try to edit the title and description for more clarity or leave a comment requesting that edits to the issue be made. + +### Dealing with missing information + +Depending on the issue, you might not feel all this information is needed. Use your best judgement. If you cannot triage an issue using what its author provided, explain kindly to the author that they must provide the above information to clarify the problem. Label issue with `triage/needs-information`. + +If the author provides the standard information but you are still unable to triage the issue, then request additional information. Do this kindly and politely because you are asking for more of the author's time. Label issue with `triage/needs-information`. + +If the author does not respond to the requested information within the timespan of a week, close the issue with a kind note stating that the author can request for the issue to be reopened when the necessary information is provided. + +If you receive a notification that additional information was provided but you are no longer working on issue triage, then you should delegate the issue to the current person working on issue triage. + +## 3. Categorizing an issue + +### Duplicate issues + +Make sure it's not a duplicate by searching existing issues using related terms from the issue title and description. If you think the issue may be a potential duplicate and can't find the original existing issue, then please reach out to one of the maintainers and ask for help. If you identify that the issue is a duplicate of an existing issue: + +1. Add a comment `duplicate of #<issue number>` +2. Add the `triage/duplicate` label + +### Bug reports + +If it's not perfectly clear that it's an actual bug, quickly try to reproduce it. + +**It's a bug/it can be reproduced:** + +1. Add a comment describing detailed steps for how to reproduce it, if applicable. +2. If maintainers aren't able to address the issue in a timely manner, then label the issue with `help wanted` and an optional `beginner friendly`. Also, include pointers to the problematic source code. Doing this allows us to solicit help from the community to get the issue resolved. +3. Move on to [prioritizing the issue](#4-prioritization-of-issues). + +**It can't be reproduced:** + +1. Either request that [more information](#2-ensure-the-issue-contains-basic-information) is needed to investigate the issue more thoroughly. Provide details in a comment. <br> +or <br> +2. [delegate further investigations](#investigation-of-issues) to someone else. Provide details in a comment. + +**It works as intended/by design:** + +1. Kindly and politely add a comment explaining briefly why we think it works as intended and close the issue. +2. Label the issue `triage/works-as-intended`. +3. Remove the `needs-triage` label. + +### Feature requests + +1. If the feature request does not align with the product vision, add a comment indicating so, remove the `needs-triage` label and close the issue +2. Otherwise, move on to [prioritizing the issue](#4-prioritization-of-issues). Assign the appropriate priority label to the issue, add the appropriate comments to the issue, and remove the `needs-triage` label. + +## 4. Prioritization of issues +In general, bugs and feature request issues should be labeled with a priority. + +This can be the most difficult task when triaging issues since it requires a lot of knowledge, context, and experience before being able to start feeling comfortable adding a certain priority label. + +In order to gain comfort with prioritizing issues, consulting with experienced project members on issues is highly encouraged. + +In case there is an uncertainty around the prioritization of an issue, please ask the maintainers for help. + +| Label | Description | +| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| `priority/critical` | Highest priority. Must be actively worked on as someone's immediate top priority. | +| `priority/high` | Must be worked on soon, ideally in time for the next release. | +| `priority/low` | Lowest priority. Possibly useful, but not critical to functionality. | + +### Critical priority + +1. If an issue has been categorized and any of the following criteria apply to it, then the issue should be labeled as critical and must be actively worked on as someone's immediate top priority. + + - Results in any data loss + - Critical security or performance issues + - Problem that makes a feature unusable + - Multiple users experience a severe problem affecting their business, users, etc. + +2. Label the issue `priority/critical`. +3. Escalate the problem to the maintainers. +4. Assign the issue or ask a maintainer for help assigning the issue to someone as their immediate top priority. +5. Add the issue to the next upcoming release milestone. + +### High priority + +1. Label the issue `priority/high`. +2. Add the issue to the next upcoming release milestone. +3. Prioritize it or assign someone to work on it now or very soon. +4. Consider requesting [help from the community](#5-requesting-help-from-the-community). + +### Low priority + +1. If the issue is deemed possibly useful but is a low priority, then label the issue `priority/low`. +2. The functional impact of an issue determines how high of a priority the issue is. +3. Consider requesting [help from the community](#5-requesting-help-from-the-community). + +## 5. Requesting help from the community + +Due to constraints, maintainers aren't always able to resolve issues in a timely manner, especially those of lower priority. For such issues, consider requesting help from the community. Use your best judgement. In general, requesting help from the community means that a contribution has a good chance of getting accepted and merged. + +In many cases, the issue author or the community is most suitable to contribute changes since they're experts in their domain. Also, it's very common for someone to seek help from the community on a problem. + +1. Kindly and politely add a comment which will notify the users subscribed to the issue of any updates. + - Explain that the issue would be nice to get resolved, but it isn't prioritized to be worked on by maintainers for the foreseeable future. + - If possible or applicable, try to help contributors get started by adding pointers to relevant source code or files as well as adding any helpful input on how to resolve the issue. +2. Label the issue with `help wanted`. +3. If applicable, label the issue with `beginner friendly` to denote that the issue is suitable for a beginner to work on. + +## Investigation of issues + +When an issue has all basic information provided, but the reported problem cannot be reproduced at a first glance, label the issue `triage/needs-information` and post a comment explaining why this label is being applied. Depending on the perceived severity and/or number of upvotes, the investigation will either be delegated to another maintainer for further investigation or put on hold until someone else (maintainer or contributor) picks it up and eventually starts investigating it. + + +Even if you don't have the time or the knowledge to investigate an issue we highly recommend that you upvote the issue if you happen to have the same problem. If you have further details that may help with investigating the issue please provide as much information as possible. + +## External pull requests + +Part of issue triage should also be triaging of external PRs. The main goal should be to make sure PRs from external contributors have an owner/reviewer and are not forgotten. + +1. Check new external PRs which do not have a reviewer. +1. Check if there is a link to an existing issue. +1. If not and you know which issue it is solving, add the link yourself, otherwise ask the author to link the issue or create one. +1. Assign a reviewer based on who was handling the linked issue or what code or feature the PR touches (look at who was the last to make changes there if all else fails). + +## GitHub issue management workflow + +The following section describes the triage workflow for new GitGHub issues that get created. + +### GitHub Issue: Bug + +This workflow starts off with a GitHub issue of type bug being created. + +1. Collaborator or maintainer creates a GitHub bug using the appropriate GitHub issue template +2. By default a bug will be created with the `type/bug` and `needs-triage` labels + +The following flowchart defines the workflow, + + +``` + +--------------------------+ + | New bug issue opened/more| + | information added | + +-------------|------------+ + | + | + +----------------------------------+ NO +--------------|-------------+ + | label: triage/needs-information --------- All required information | + | | | contained in issue? | + +-----------------------------|----+ +--------------|-------------+ + | | YES + | | + +--------------------------+ | +---------------------+ YES +---------------------------------------+ + |label: | | | Duplicate Issue? ------- Comment `Duplicate of #<issue number>` + |triage/needs-investigation| | NO | | | Remove needs-triage label | + +------|-------------------+ | +----------|----------+ | label: triage/duplicate | + | | | NO +-----------------|---------------------+ + YES | | | | + | +---------------|----+ NO +------------|------------+ | + | |Needs investigation?|---------- Can it be reproduced? | | + |------- | +------------|------------+ | + +--------------------+ | YES | + | +----------|----------+ + +-------------------------+ +------------|------------+ | Close Issue | + | Add release-found label |------------------ Works as intended? | | | + | label: release-found/* | NO | | +----------|----------+ + +------------|------------+ +------------|------------+ | + | | | + | | YES | + | +----------------|----------------+ | + +--------------------------+ | Add comment | | + | Add area label | | Remove needs-triage label ------------------| + | label: area/* | | label: triage/works-as-intended | + +-------------|------------+ +---------------------------------+ + | + | +----------+ + | | Done ---------------------------------------- + | +----|-----+ | + | |NO | + | | +------------------|------------------+ + +------------|-------------+ +----|----------------+ YES | Add details to issue | + | ------------ Signal Community? ---------- label: help wanted | + |Remove needs-triage label | | | | label: beginner friendly (optional)| + +--------------------------+ +---------------------+ +-------------------------------------+ + +``` + +### GitHub Issue: Feature request + +``` + +---------------------------------+ + |New feature request issue opened/| + |more information added | + +----------------|----------------+ + | + | + +---------------------------------+ NO +-------------|------------+ + | label: triage/needs-information ---------- All required information | + | | | contained in issue? | + +---------------------------------+ +-------------|------------+ + | + | + +---------------------------------------+ | + |Comment `Duplicate of #<issue number>` | YES +----------|----------+ + |Remove needs-triage label ------- Duplicate issue? | + |label: triage/duplicate | | | + +-----|---------------------------------+ +-----------|---------+ + | |NO + | +-------------------------+ NO +-----------------------------+ + | |Add comment |-------- Is this a valid feature? | + | |Remove needs-triage label| | | + | +------|------------------+ +--------------|--------------+ + | | | YES + | | | + | | +---------------|--------------+ + | | | label: type/feature | + +-|---------|---+ +--------+ | Remove needs-triage label | + | Close issue | | Done ------ Remove type/feature-request | + | | | | | milestone? | + +---------------+ +--------+ +------------------------------+ +``` +If the author does not respond to a request for more information within the timespan of a week, close the issue with a kind note stating that the author can request for the issue to be reopened when the necessary information is provided. + +In some cases you may receive a request you do not wish to accept. Perhaps the request doesn't align with the project scope or vision. It is important to tactfully handle requests that don't meet the project standards. + +1. Acknowledge the person behind the request and thank them for their interest and request. +2. Explain why it didn't fit into the scope of the project or vision. +3. Don't leave an unwanted request open. Immediately close the request you do not wish to accept. + + + diff --git a/ansible_collections/dellemc/enterprise_sonic/docs/MAINTAINER_GUIDE.md b/ansible_collections/dellemc/enterprise_sonic/docs/MAINTAINER_GUIDE.md new file mode 100644 index 000000000..4652b0ef6 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/docs/MAINTAINER_GUIDE.md @@ -0,0 +1 @@ +At this point, only the Dell team is contributing. A guide shall be provided when it is opened for external maintainers.
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/docs/SUPPORT.md b/ansible_collections/dellemc/enterprise_sonic/docs/SUPPORT.md new file mode 100644 index 000000000..8c429321a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/docs/SUPPORT.md @@ -0,0 +1,13 @@ +<!-- +Copyright (c) 2022 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the GPL, Version 3.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.gnu.org/licenses/gpl-3.0.txt +--> + +# Support + + * To report any defects, request functionality, or provide general feedback to the development team, please open an Issue using the [Github issue tracker](https://github.com/ansible-collections/dellemc.enterprise_sonic/issues).
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/meta/runtime.yml b/ansible_collections/dellemc/enterprise_sonic/meta/runtime.yml index d4f511c1f..877679f24 100644 --- a/ansible_collections/dellemc/enterprise_sonic/meta/runtime.yml +++ b/ansible_collections/dellemc/enterprise_sonic/meta/runtime.yml @@ -1,4 +1,4 @@ -requires_ansible: '>=2.9.10' +requires_ansible: '>=2.14.0' plugin_routing: action: sonic_config: @@ -51,3 +51,5 @@ plugin_routing: redirect: dellemc.enterprise_sonic.sonic sonic_system: redirect: dellemc.enterprise_sonic.sonic + sonic_pki: + redirect: dellemc.enterprise_sonic.sonic diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/mgmt_vrf_config.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/mgmt_vrf_config.yaml new file mode 100644 index 000000000..24794ad76 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/mgmt_vrf_config.yaml @@ -0,0 +1,27 @@ +--- +# Execution of a task that adds or deletes the configuration of the +# management VRF ("mgmt") causes a disruption of the +# management interface connection on which playbook configuration +# commands are executing. As a result, playbook execution is aborted +# unless the management VRF configuration task is constructed to +# ignore errors. +# +# This example demonstrates how to combine tasks that configure other +# resource modules ("sonic_vrfs", in this case) with tasks that add or remove +# "management VRF" configuration. + +- name: Example playbook of configuring with mgmt_vrf_on and mgmt_vrf_off tasks. + hosts: datacenter + gather_facts: False + connection: httpapi + collections: + - dellemc.enterprise_sonic + tasks: + - include_tasks: mgmt_vrf_on.yaml + - name: Add VLANs + sonic_vlans: + config: + - vlan_id: 11 + - vlan_id: 12 + state: merged + - include_tasks: mgmt_vrf_off.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/mgmt_vrf_off.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/mgmt_vrf_off.yaml new file mode 100644 index 000000000..c1c72f9ee --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/mgmt_vrf_off.yaml @@ -0,0 +1,6 @@ +- name: Delete mgmt VRF configuration + sonic_vrfs: + config: + - name: mgmt + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/mgmt_vrf_on.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/mgmt_vrf_on.yaml new file mode 100644 index 000000000..109b14fc0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/mgmt_vrf_on.yaml @@ -0,0 +1,6 @@ +- name: Create mgmt VRF configuration + sonic_vrfs: + config: + - name: mgmt + state: merged + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/cliconf/sonic.py b/ansible_collections/dellemc/enterprise_sonic/plugins/cliconf/sonic.py index 37f1d872a..e5cc7630f 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/cliconf/sonic.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/cliconf/sonic.py @@ -32,8 +32,6 @@ description: import json -from itertools import chain - from ansible.errors import AnsibleConnectionFailure from ansible.module_utils._text import to_bytes, to_text from ansible.module_utils.common._collections_compat import Mapping diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/aaa/aaa.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/aaa/aaa.py index 86040892a..a61b7307b 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/aaa/aaa.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/aaa/aaa.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -60,7 +60,7 @@ class AaaArgs(object): # pylint: disable=R0903 'type': 'dict' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'deleted', 'overridden', 'replaced'], 'default': 'merged', 'type': 'str' } } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/acl_interfaces/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/acl_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/acl_interfaces/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/acl_interfaces/acl_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/acl_interfaces/acl_interfaces.py new file mode 100644 index 000000000..45f7bf480 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/acl_interfaces/acl_interfaces.py @@ -0,0 +1,82 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_acl_interfaces module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class Acl_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_acl_interfaces module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'elements': 'dict', + 'options': { + 'access_groups': { + 'elements': 'dict', + 'options': { + 'acls': { + 'elements': 'dict', + 'options': { + 'direction': { + 'choices': ['in', 'out'], + 'required': True, + 'type': 'str' + }, + 'name': { + 'required': True, + 'type': 'str' + } + }, + 'type': 'list' + }, + 'type': { + 'choices': ['mac', 'ipv4', 'ipv6'], + 'required': True, + 'type': 'str' + } + }, + 'type': 'list' + }, + 'name': { + 'required': True, + 'type': 'str' + } + }, + 'type': 'list' + }, + 'state': { + 'choices': ['merged', 'replaced', 'overridden', 'deleted'], + 'default': 'merged', + 'type': 'str' + } + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bfd/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bfd/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bfd/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bfd/bfd.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bfd/bfd.py new file mode 100644 index 000000000..57532795e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bfd/bfd.py @@ -0,0 +1,89 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_bfd module +""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +class BfdArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_bfd module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'options': { + 'multi_hops': { + 'elements': 'dict', + 'options': { + 'detect_multiplier': {'default': 3, 'type': 'int'}, + 'enabled': {'default': True, 'type': 'bool'}, + 'local_address': {'required': True, 'type': 'str'}, + 'min_ttl': {'default': 254, 'type': 'int'}, + 'passive_mode': {'default': False, 'type': 'bool'}, + 'profile_name': {'type': 'str'}, + 'receive_interval': {'default': 300, 'type': 'int'}, + 'remote_address': {'required': True, 'type': 'str'}, + 'transmit_interval': {'default': 300, 'type': 'int'}, + 'vrf': {'required': True, 'type': 'str'}}, + 'type': 'list'}, + 'profiles': { + 'elements': 'dict', + 'options': { + 'detect_multiplier': {'default': 3, 'type': 'int'}, + 'echo_interval': {'default': 300, 'type': 'int'}, + 'echo_mode': {'default': False, 'type': 'bool'}, + 'enabled': {'default': True, 'type': 'bool'}, + 'min_ttl': {'default': 254, 'type': 'int'}, + 'passive_mode': {'default': False, 'type': 'bool'}, + 'profile_name': {'required': True, 'type': 'str'}, + 'receive_interval': {'default': 300, 'type': 'int'}, + 'transmit_interval': {'default': 300, 'type': 'int'}}, + 'type': 'list'}, + 'single_hops': { + 'elements': 'dict', + 'options': { + 'detect_multiplier': {'default': 3, 'type': 'int'}, + 'echo_interval': {'default': 300, 'type': 'int'}, + 'echo_mode': {'default': False, 'type': 'bool'}, + 'enabled': {'default': True, 'type': 'bool'}, + 'interface': {'required': True, 'type': 'str'}, + 'local_address': {'required': True, 'type': 'str'}, + 'passive_mode': {'default': False, 'type': 'bool'}, + 'profile_name': {'type': 'str'}, + 'receive_interval': {'default': 300, 'type': 'int'}, + 'remote_address': {'required': True, 'type': 'str'}, + 'transmit_interval': {'default': 300, 'type': 'int'}, + 'vrf': {'required': True, 'type': 'str'}}, + 'type': 'list'} + }, + 'type': 'dict' + }, + 'state': {'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged', 'type': 'str'} + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp/bgp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp/bgp.py index fb7618133..8d494dddd 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp/bgp.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp/bgp.py @@ -79,6 +79,7 @@ class BgpArgs(object): # pylint: disable=R0903 }, "type": "dict" }, + 'rt_delay': {'type': 'int'}, 'timers': { 'options': { 'holdtime': {'type': 'int'}, @@ -91,7 +92,7 @@ class BgpArgs(object): # pylint: disable=R0903 'type': 'list' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged' } } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_af/bgp_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_af/bgp_af.py index ac22210ee..336d49b47 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_af/bgp_af.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_af/bgp_af.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2019 Red Hat +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -71,6 +71,21 @@ class Bgp_afArgs(object): # pylint: disable=R0903 'required': True, 'type': 'str' }, + 'rd': {'type': 'str'}, + 'rt_in': {'type': 'list', 'elements': 'str'}, + 'rt_out': {'type': 'list', 'elements': 'str'}, + 'vnis': { + 'elements': 'dict', + 'options': { + 'advertise_default_gw': {'type': 'bool'}, + 'advertise_svi_ip': {'type': 'bool'}, + 'rd': {'type': 'str'}, + 'rt_in': {'type': 'list', 'elements': 'str'}, + 'rt_out': {'type': 'list', 'elements': 'str'}, + 'vni_number': {'required': True, 'type': 'int'} + }, + 'type': 'list' + }, 'max_path': { 'options': { 'ebgp': {'type': 'int'}, @@ -111,7 +126,7 @@ class Bgp_afArgs(object): # pylint: disable=R0903 'type': 'list' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'deleted', 'overridden', 'replaced'], 'default': 'merged' } } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_as_paths/bgp_as_paths.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_as_paths/bgp_as_paths.py index dec9b930e..d9d4ed766 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_as_paths/bgp_as_paths.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_as_paths/bgp_as_paths.py @@ -43,6 +43,6 @@ class Bgp_as_pathsArgs(object): # pylint: disable=R0903 'type': 'list'}, 'name': {'required': True, 'type': 'str'}}, 'type': 'list'}, - 'state': {'choices': ['merged', 'deleted'], + 'state': {'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged', 'type': 'str'}} # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py index 867e55204..c90fab8e9 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -54,6 +54,6 @@ class Bgp_communitiesArgs(object): # pylint: disable=R0903 'default': 'standard', 'type': 'str'}}, 'type': 'list'}, - 'state': {'choices': ['merged', 'deleted'], + 'state': {'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged', 'type': 'str'}} # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py index aec0f364a..4cee6182b 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -68,7 +68,7 @@ class Bgp_ext_communitiesArgs(object): # pylint: disable=R0903 'type': 'list' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged', 'type': 'str' } diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/copp/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/copp/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/copp/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/copp/copp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/copp/copp.py new file mode 100644 index 000000000..889c614c6 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/copp/copp.py @@ -0,0 +1,59 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_copp module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class CoppArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_copp module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'options': { + 'copp_groups': { + 'elements': 'dict', + 'options': { + 'cbs': {'type': 'str'}, + 'cir': {'type': 'str'}, + 'copp_name': {'required': True, 'type': 'str'}, + 'queue': {'type': 'int'}, + 'trap_action': {'type': 'str'}, + 'trap_priority': {'type': 'int'} + }, + 'type': 'list' + } + }, + 'type': 'dict' + }, + 'state': {'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged', 'type': 'str'} + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/dhcp_relay/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/dhcp_relay/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/dhcp_relay/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/dhcp_relay/dhcp_relay.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/dhcp_relay/dhcp_relay.py new file mode 100644 index 000000000..0ca834487 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/dhcp_relay/dhcp_relay.py @@ -0,0 +1,94 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_dhcp_relay module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class Dhcp_relayArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_dhcp_relay module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'elements': 'dict', + 'options': { + 'ipv4': { + 'options': { + 'circuit_id': { + 'choices': ['%h:%p', '%i', '%p'], + 'type': 'str' + }, + 'link_select': {'type': 'bool'}, + 'max_hop_count': {'type': 'int'}, + 'policy_action': { + 'choices': ['append', 'discard', 'replace'], + 'type': 'str' + }, + 'server_addresses': { + 'elements': 'dict', + 'options': { + 'address': {'type': 'str'} + }, + 'type': 'list' + }, + 'source_interface': {'type': 'str'}, + 'vrf_name': {'type': 'str'}, + 'vrf_select': {'type': 'bool'} + }, + 'type': 'dict' + }, + 'ipv6': { + 'options': { + 'max_hop_count': {'type': 'int'}, + 'server_addresses': { + 'elements': 'dict', + 'options': { + 'address': {'type': 'str'} + }, + 'type': 'list' + }, + 'source_interface': {'type': 'str'}, + 'vrf_name': {'type': 'str'}, + 'vrf_select': {'type': 'bool'} + }, + 'type': 'dict' + }, + 'name': {'required': True, 'type': 'str'} + }, + 'type': 'list' + }, + 'state': { + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], + 'default': 'merged', + 'type': 'str' + } + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/dhcp_snooping/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/dhcp_snooping/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/dhcp_snooping/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/dhcp_snooping/dhcp_snooping.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/dhcp_snooping/dhcp_snooping.py new file mode 100644 index 000000000..6cf2ecacd --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/dhcp_snooping/dhcp_snooping.py @@ -0,0 +1,81 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_dhcp_snooping module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class Dhcp_snoopingArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_dhcp_snooping module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'options': { + 'afis': { + 'elements': 'dict', + 'options': { + 'afi': { + 'choices': ['ipv4', 'ipv6'], + 'required': True, + 'type': 'str', + }, + 'enabled': {'type': 'bool'}, + 'source_bindings': { + 'elements': 'dict', + 'options': { + 'mac_addr': {'required': True, 'type': 'str'}, + 'ip_addr': {'type': 'str'}, + 'intf_name': {'type': 'str'}, + 'vlan_id': {'type': 'int'}, + }, + 'type': 'list', + }, + 'trusted': { + 'elements': 'dict', + 'options': { + 'intf_name': {'required': True, 'type': 'str'}, + }, + 'type': 'list', + }, + 'verify_mac': {'type': 'bool'}, + 'vlans': {'elements': 'str', 'type': 'list'}, + }, + 'type': 'list', + } + }, + 'type': 'dict', + }, + 'state': { + 'choices': ['merged', 'deleted', 'overridden', 'replaced'], + 'default': 'merged', + 'type': 'str', + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/facts/facts.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/facts/facts.py index 3a4d02989..6b27194eb 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/facts/facts.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/facts/facts.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -35,6 +35,7 @@ class FactsArgs(object): # pylint: disable=R0903 'bgp_ext_communities', 'mclag', 'prefix_lists', + 'vlan_mapping', 'vrfs', 'vxlans', 'users', @@ -44,7 +45,22 @@ class FactsArgs(object): # pylint: disable=R0903 'tacacs_server', 'radius_server', 'static_routes', - 'ntp' + 'ntp', + 'logging', + 'pki', + 'ip_neighbor', + 'port_group', + 'dhcp_relay', + 'dhcp_snooping', + 'acl_interfaces', + 'l2_acls', + 'l3_acls', + 'lldp_global', + 'mac', + 'bfd', + 'copp', + 'route_maps', + 'stp' ] argument_spec = { diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/interfaces/interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/interfaces/interfaces.py index 76c36a90b..407dbde6b 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/interfaces/interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/interfaces/interfaces.py @@ -44,12 +44,33 @@ class InterfacesArgs(object): # pylint: disable=R0903 "description": {"type": "str"}, "enabled": {"type": "bool"}, "mtu": {"type": "int"}, - "name": {"required": True, "type": "str"} + "name": {"required": True, "type": "str"}, + "speed": {"type": "str", + "choices": ["SPEED_10MB", + "SPEED_100MB", + "SPEED_1GB", + "SPEED_2500MB", + "SPEED_5GB", + "SPEED_10GB", + "SPEED_20GB", + "SPEED_25GB", + "SPEED_40GB", + "SPEED_50GB", + "SPEED_100GB", + "SPEED_400GB"]}, + "auto_negotiate": {"type": "bool"}, + "advertised_speed": {"type": "list", "elements": "str"}, + "fec": {"type": "str", + "choices": ["FEC_RS", + "FEC_FC", + "FEC_DISABLED", + "FEC_DEFAULT", + "FEC_AUTO"]} }, "type": "list" }, "state": { - "choices": ["merged", "deleted"], + "choices": ["merged", "replaced", "overridden", "deleted"], "default": "merged", "type": "str" } diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/ip_neighbor/ip_neighbor.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/ip_neighbor/ip_neighbor.py new file mode 100644 index 000000000..fef1c67c0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/ip_neighbor/ip_neighbor.py @@ -0,0 +1,56 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_ip_neighbor module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class Ip_neighborArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_ip_neighbor module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'options': { + 'ipv4_arp_timeout': {'type': 'int'}, + 'ipv4_drop_neighbor_aging_time': {'type': 'int'}, + 'ipv6_drop_neighbor_aging_time': {'type': 'int'}, + 'ipv6_nd_cache_expiry': {'type': 'int'}, + 'num_local_neigh': {'type': 'int'} + }, + 'type': 'dict' + }, + 'state': { + 'choices': ['merged', 'replaced', 'overridden', 'deleted'], + 'default': 'merged', + 'type': 'str' + } + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_acls/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_acls/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_acls/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_acls/l2_acls.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_acls/l2_acls.py new file mode 100644 index 000000000..5b8ba4f87 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_acls/l2_acls.py @@ -0,0 +1,129 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_l2_acls module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class L2_aclsArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_l2_acls module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'elements': 'dict', + 'options': { + 'name': {'required': True, 'type': 'str'}, + 'remark': {'type': 'str'}, + 'rules': { + 'elements': 'dict', + 'mutually_exclusive': [['ethertype', 'vlan_tag_format']], + 'options': { + 'action': { + 'choices': ['deny', 'discard', 'do-not-nat', 'permit', 'transit'], + 'type': 'str' + }, + 'dei': { + 'choices': [0, 1], + 'type': 'int' + }, + 'destination': { + 'mutually_exclusive': [['any', 'host', 'address']], + 'options': { + 'address': {'type': 'str'}, + 'address_mask': {'type': 'str'}, + 'any': {'type': 'bool'}, + 'host': {'type': 'str'} + }, + 'required_one_of': [['any', 'host', 'address']], + 'required_together': [['address', 'address_mask']], + 'type': 'dict' + }, + 'ethertype': { + 'mutually_exclusive': [['value', 'arp', 'ipv4', 'ipv6']], + 'options': { + 'arp': {'type': 'bool'}, + 'ipv4': {'type': 'bool'}, + 'ipv6': {'type': 'bool'}, + 'value': {'type': 'str'} + }, + 'type': 'dict' + }, + 'pcp': { + 'mutually_exclusive': [ + ['value', 'traffic_type'], + ['mask', 'traffic_type'] + ], + 'options': { + 'mask': {'type': 'int'}, + 'traffic_type': { + 'choices': ['be', 'bk', 'ee', 'ca', 'vi', 'vo', 'ic', 'nc'], + 'type': 'str' + }, + 'value': {'type': 'int'} + }, + 'required_by': {'mask': ['value']}, + 'type': 'dict' + }, + 'remark': {'type': 'str'}, + 'sequence_num': {'required': True, 'type': 'int'}, + 'source': { + 'mutually_exclusive': [['any', 'host', 'address']], + 'options': { + 'address': {'type': 'str'}, + 'address_mask': {'type': 'str'}, + 'any': {'type': 'bool'}, + 'host': {'type': 'str'} + }, + 'required_one_of': [['any', 'host', 'address']], + 'required_together': [['address', 'address_mask']], + 'type': 'dict' + }, + 'vlan_id': {'type': 'int'}, + 'vlan_tag_format': { + 'options': { + 'multi_tagged': {'type': 'bool'} + }, + 'type': 'dict' + } + }, + 'required_together': [['action', 'source', 'destination']], + 'type': 'list' + } + }, + 'type': 'list' + }, + 'state': { + 'choices': ['merged', 'replaced', 'overridden', 'deleted'], + 'default': 'merged', + 'type': 'str' + } + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_interfaces/l2_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_interfaces/l2_interfaces.py index bbebe2d54..b5ce4ad8e 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_interfaces/l2_interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_interfaces/l2_interfaces.py @@ -53,7 +53,7 @@ class L2_interfacesArgs(object): # pylint: disable=R0903 'allowed_vlans': { 'elements': 'dict', 'options': { - 'vlan': {'type': 'int'} + 'vlan': {'type': 'str'} }, 'type': 'list' } @@ -64,7 +64,7 @@ class L2_interfacesArgs(object): # pylint: disable=R0903 'type': 'list' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged', 'type': 'str' } diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_acls/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_acls/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_acls/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_acls/l3_acls.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_acls/l3_acls.py new file mode 100644 index 000000000..7339201ef --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_acls/l3_acls.py @@ -0,0 +1,223 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_l3_acls module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class L3_aclsArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_l3_acls module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'elements': 'dict', + 'options': { + 'acls': { + 'elements': 'dict', + 'options': { + 'name': {'required': True, 'type': 'str'}, + 'remark': {'type': 'str'}, + 'rules': { + 'elements': 'dict', + 'options': { + 'action': { + 'choices': ['deny', 'discard', 'do-not-nat', 'permit', 'transit'], + 'type': 'str' + }, + 'destination': { + 'mutually_exclusive': [['any', 'host', 'prefix']], + 'options': { + 'any': {'type': 'bool'}, + 'host': {'type': 'str'}, + 'port_number': { + 'mutually_exclusive': [['eq', 'gt', 'lt', 'range']], + 'options': { + 'eq': {'type': 'int'}, + 'gt': {'type': 'int'}, + 'lt': {'type': 'int'}, + 'range': { + 'options': { + 'begin': {'type': 'int'}, + 'end': {'type': 'int'} + }, + 'required_together': [['begin', 'end']], + 'type': 'dict' + } + }, + 'type': 'dict' + }, + 'prefix': {'type': 'str'} + }, + 'required_one_of': [['any', 'host', 'prefix']], + 'type': 'dict' + }, + 'dscp': { + 'mutually_exclusive': [[ + 'value', 'af11', 'af12', 'af13', 'af21', 'af22', 'af23', 'af31', 'af32', 'af33', + 'cs1', 'cs2', 'cs3', 'cs4', 'cs5', 'cs6', 'cs7', 'default', 'ef', 'voice_admit' + ]], + 'options': { + 'af11': {'type': 'bool'}, + 'af12': {'type': 'bool'}, + 'af13': {'type': 'bool'}, + 'af21': {'type': 'bool'}, + 'af22': {'type': 'bool'}, + 'af23': {'type': 'bool'}, + 'af31': {'type': 'bool'}, + 'af32': {'type': 'bool'}, + 'af33': {'type': 'bool'}, + 'af41': {'type': 'bool'}, + 'af42': {'type': 'bool'}, + 'af43': {'type': 'bool'}, + 'cs1': {'type': 'bool'}, + 'cs2': {'type': 'bool'}, + 'cs3': {'type': 'bool'}, + 'cs4': {'type': 'bool'}, + 'cs5': {'type': 'bool'}, + 'cs6': {'type': 'bool'}, + 'cs7': {'type': 'bool'}, + 'default': {'type': 'bool'}, + 'ef': {'type': 'bool'}, + 'value': {'type': 'int'}, + 'voice_admit': {'type': 'bool'} + }, + 'type': 'dict' + }, + 'protocol': { + 'mutually_exclusive': [['name', 'number']], + 'options': { + 'name': { + 'choices': ['ip', 'ipv6', 'icmp', 'icmpv6', 'tcp', 'udp'], + 'type': 'str' + }, + 'number': {'type': 'int'} + }, + 'required_one_of': [['name', 'number']], + 'type': 'dict' + }, + 'protocol_options': { + 'mutually_exclusive': [['icmp', 'icmpv6', 'tcp']], + 'options': { + 'icmp': { + 'options': { + 'code': {'type': 'int'}, + 'type': {'type': 'int'} + }, + 'type': 'dict' + }, + 'icmpv6': { + 'options': { + 'code': {'type': 'int'}, + 'type': {'type': 'int'} + }, + 'type': 'dict' + }, + 'tcp': { + 'mutually_exclusive': [ + ['established', 'ack', 'not_ack'], + ['established', 'fin', 'not_fin'], + ['established', 'psh', 'not_psh'], + ['established', 'rst', 'not_rst'], + ['established', 'syn', 'not_syn'], + ['established', 'urg', 'not_urg'] + ], + 'options': { + 'ack': {'type': 'bool'}, + 'established': {'type': 'bool'}, + 'fin': {'type': 'bool'}, + 'not_ack': {'type': 'bool'}, + 'not_fin': {'type': 'bool'}, + 'not_psh': {'type': 'bool'}, + 'not_rst': {'type': 'bool'}, + 'not_syn': {'type': 'bool'}, + 'not_urg': {'type': 'bool'}, + 'psh': {'type': 'bool'}, + 'rst': {'type': 'bool'}, + 'syn': {'type': 'bool'}, + 'urg': {'type': 'bool'} + }, + 'type': 'dict' + } + }, + 'type': 'dict' + }, + 'remark': {'type': 'str'}, + 'sequence_num': {'required': True, 'type': 'int'}, + 'source': { + 'mutually_exclusive': [['any', 'host', 'prefix']], + 'options': { + 'any': {'type': 'bool'}, + 'host': {'type': 'str'}, + 'port_number': { + 'mutually_exclusive': [['eq', 'gt', 'lt', 'range']], + 'options': { + 'eq': {'type': 'int'}, + 'gt': {'type': 'int'}, + 'lt': {'type': 'int'}, + 'range': { + 'options': { + 'begin': {'type': 'int'}, + 'end': {'type': 'int'} + }, + 'required_together': [['begin', 'end']], + 'type': 'dict' + } + }, + 'type': 'dict' + }, + 'prefix': {'type': 'str'} + }, + 'required_one_of': [['any', 'host', 'prefix']], + 'type': 'dict' + }, + 'vlan_id': {'type': 'int'} + }, + 'required_together': [['action', 'protocol', 'source', 'destination']], + 'type': 'list' + } + }, + 'type': 'list' + }, + 'address_family': { + 'choices': ['ipv4', 'ipv6'], + 'required': True, + 'type': 'str' + } + }, + 'type': 'list' + }, + 'state': { + 'choices': ['merged', 'replaced', 'overridden', 'deleted'], + 'default': 'merged', + 'type': 'str' + } + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_interfaces/l3_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_interfaces/l3_interfaces.py index 6e83289cc..b32d7f92e 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_interfaces/l3_interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_interfaces/l3_interfaces.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -74,7 +74,7 @@ class L3_interfacesArgs(object): # pylint: disable=R0903 'type': 'list' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged', 'type': 'str' } diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lag_interfaces/lag_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lag_interfaces/lag_interfaces.py index 867d61a27..fc349232f 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lag_interfaces/lag_interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lag_interfaces/lag_interfaces.py @@ -60,7 +60,7 @@ class Lag_interfacesArgs(object): # pylint: disable=R0903 "type": "list" }, "state": { - "choices": ["merged", "deleted"], + "choices": ["merged", "replaced", "overridden", "deleted"], "default": "merged", "type": "str" } diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lldp_global/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lldp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lldp_global/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lldp_global/lldp_global.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lldp_global/lldp_global.py new file mode 100644 index 000000000..8f9cd9af0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lldp_global/lldp_global.py @@ -0,0 +1,81 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_lldp_global module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class Lldp_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_lldp_global module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'options': { + 'enable': { + 'type': 'bool' + }, + 'hello_time': { + 'type': 'int' + }, + 'mode': { + 'choices': ['receive', 'transmit'], + 'type': 'str' + }, + 'multiplier': { + 'type': 'int' + }, + 'system_description': { + 'type': 'str' + }, + 'system_name': { + 'type': 'str' + }, + 'tlv_select': { + 'options': { + 'management_address': { + 'type': 'bool' + }, + 'system_capabilities': { + 'type': 'bool' + } + }, + 'type': 'dict' + } + }, + 'type': 'dict' + }, + 'state': { + 'choices': ['merged', 'deleted'], + 'default': 'merged', + 'type': 'str' + } + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/logging/logging.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/logging/logging.py new file mode 100644 index 000000000..a83d9eef4 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/logging/logging.py @@ -0,0 +1,64 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_logging module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class LoggingArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_logging module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'options': { + 'remote_servers': { + 'elements': 'dict', + 'options': { + 'host': {'required': True, + 'type': 'str'}, + 'message_type': {'choices': ['log', 'event'], + 'type': 'str'}, + 'remote_port': {'type': 'int'}, + 'source_interface': {'type': 'str'}, + 'vrf': {'type': 'str'} + }, + 'type': 'list' + } + }, + 'type': 'dict' + }, + 'state': { + 'choices': ['merged', "replaced", "overridden", 'deleted'], + 'default': 'merged', + 'type': 'str' + } + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mac/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mac/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mac/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mac/mac.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mac/mac.py new file mode 100644 index 000000000..d46155377 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mac/mac.py @@ -0,0 +1,66 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_mac module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class MacArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_mac module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'elements': 'dict', + 'options': { + 'mac': { + 'options': { + 'aging_time': {'default': '600', 'type': 'int'}, + 'dampening_interval': {'default': '5', 'type': 'int'}, + 'dampening_threshold': {'default': '5', 'type': 'int'}, + 'mac_table_entries': { + 'elements': 'dict', + 'options': { + 'interface': {'type': 'str'}, + 'mac_address': {'required': True, 'type': 'str'}, + 'vlan_id': {'required': True, 'type': 'int'} + }, + 'type': 'list' + } + }, + 'type': 'dict' + }, + 'vrf_name': {'default': 'default', 'type': 'str'} + }, + 'type': 'list' + }, + 'state': {'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged', 'type': 'str'} + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mclag/mclag.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mclag/mclag.py index be3c38ca2..0d9b45eb0 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mclag/mclag.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mclag/mclag.py @@ -41,6 +41,8 @@ class MclagArgs(object): # pylint: disable=R0903 'config': { 'options': { 'domain_id': {'required': True, 'type': 'int'}, + 'gateway_mac': {'type': 'str'}, + 'delay_restore': {'type': 'int'}, 'keepalive': {'type': 'int'}, 'peer_address': {'type': 'str'}, 'peer_link': {'type': 'str'}, @@ -56,6 +58,18 @@ class MclagArgs(object): # pylint: disable=R0903 }, 'type': 'dict' }, + 'peer_gateway': { + 'options': { + 'vlans': { + 'elements': 'dict', + 'options': { + 'vlan': {'type': 'str'} + }, + 'type': 'list' + } + }, + 'type': 'dict' + }, 'session_timeout': {'type': 'int'}, 'source_address': {'type': 'str'}, 'system_mac': {'type': 'str'}, @@ -75,7 +89,7 @@ class MclagArgs(object): # pylint: disable=R0903 'type': 'dict' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged', 'type': 'str' } diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/ntp/ntp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/ntp/ntp.py index 062520af9..0d357cc2e 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/ntp/ntp.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/ntp/ntp.py @@ -64,8 +64,10 @@ class NtpArgs(object): # pylint: disable=R0903 'type': 'str'}, 'key_id': {'type': 'int', 'no_log': True}, 'maxpoll': {'type': 'int'}, - 'minpoll': {'type': 'int'} + 'minpoll': {'type': 'int'}, + 'prefer': {'type': 'bool'} }, + 'required_together': [['minpoll', 'maxpoll']], 'type': 'list' }, 'source_interfaces': { @@ -82,7 +84,7 @@ class NtpArgs(object): # pylint: disable=R0903 'type': 'dict' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', "replaced", "overridden", 'deleted'], 'default': 'merged', 'type': 'str' } diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/pki/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/pki/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/pki/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/pki/pki.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/pki/pki.py new file mode 100644 index 000000000..5f4aa32af --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/pki/pki.py @@ -0,0 +1,78 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell EMC +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_pki module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class PkiArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_pki module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'options': { + 'security_profiles': { + 'elements': 'dict', + 'options': + { + 'cdp_list': {'elements': 'str', 'type': 'list'}, + 'certificate_name': {'type': 'str'}, + 'key_usage_check': {'type': 'bool'}, + 'ocsp_responder_list': + { + 'elements': 'str', + 'type': 'list' + }, + 'peer_name_check': {'type': 'bool'}, + 'profile_name': {'required': True, 'type': 'str'}, + 'revocation_check': {'type': 'bool'}, + 'trust_store': {'type': 'str'} + }, + 'type': 'list' + }, + 'trust_stores': { + 'elements': 'dict', + 'options': { + 'ca_name': {'elements': 'str', 'type': 'list'}, + 'name': {'required': True, 'type': 'str'} + }, + 'type': 'list' + } + }, + 'type': 'dict' + }, + 'state': { + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], + 'default': 'merged', + 'type': 'str' + } + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_breakout/port_breakout.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_breakout/port_breakout.py index 3b8f4a5a3..90d736ff3 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_breakout/port_breakout.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_breakout/port_breakout.py @@ -42,8 +42,10 @@ class Port_breakoutArgs(object): # pylint: disable=R0903 'elements': 'dict', 'options': { 'mode': { - 'choices': ['1x100G', '1x400G', '1x40G', '2x100G', '2x200G', - '2x50G', '4x100G', '4x10G', '4x25G', '4x50G'], + 'choices': ['1x10G', '1x25G', '1x40G', '1x50G', '1x100G', + '1x200G', '1x400G', '2x10G', '2x25G', '2x40G', + '2x50G', '2x100G', '2x200G', '4x10G', '4x25G', + '4x50G', '4x100G', '8x10G', '8x25G', '8x50G'], 'type': 'str' }, 'name': {'required': True, 'type': 'str'} @@ -51,7 +53,7 @@ class Port_breakoutArgs(object): # pylint: disable=R0903 'type': 'list' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged' } } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_group/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_group/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_group/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_group/port_group.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_group/port_group.py new file mode 100644 index 000000000..9db29de2e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_group/port_group.py @@ -0,0 +1,66 @@ +# +# -*- coding: utf-8 -*- +# © Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_port_group module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class Port_groupArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_port_group module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'elements': 'dict', + 'options': { + 'id': {'required': True, 'type': 'str'}, + 'speed': {'choices': ['SPEED_10MB', + 'SPEED_100MB', + 'SPEED_1GB', + 'SPEED_2500MB', + 'SPEED_5GB', + 'SPEED_10GB', + 'SPEED_20GB', + 'SPEED_25GB', + 'SPEED_40GB', + 'SPEED_50GB', + 'SPEED_100GB', + 'SPEED_400GB'], + 'type': 'str'} + }, + 'type': 'list' + }, + 'state': { + 'choices': ['merged', 'replaced', 'overridden', 'deleted'], + 'default': 'merged', + 'type': 'str' + } + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/prefix_lists/prefix_lists.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/prefix_lists/prefix_lists.py index d043ae6f8..17de2eeae 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/prefix_lists/prefix_lists.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/prefix_lists/prefix_lists.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2019 Red Hat +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -64,7 +64,7 @@ class Prefix_listsArgs: # pylint: disable=R0903 'type': 'list' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged', 'type': 'str' } diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/radius_server/radius_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/radius_server/radius_server.py index a56147a5b..0ef029d7b 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/radius_server/radius_server.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/radius_server/radius_server.py @@ -59,7 +59,7 @@ class Radius_serverArgs(object): # pylint: disable=R0903 }, 'key': {'type': 'str', 'no_log': True}, 'name': {'type': 'str'}, - 'port': {'type': 'int'}, + 'port': {'type': 'int', 'default': 1812}, 'priority': {'type': 'int'}, 'retransmit': {'type': 'int'}, 'source_interface': {'type': 'str'}, @@ -72,12 +72,12 @@ class Radius_serverArgs(object): # pylint: disable=R0903 'type': 'dict' }, 'statistics': {'type': 'bool'}, - 'timeout': {'type': 'int'} + 'timeout': {'type': 'int', 'default': 5} }, 'type': 'dict' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'replaced', 'overridden', 'deleted'], 'default': 'merged' } } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/route_maps/route_maps.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/route_maps/route_maps.py new file mode 100644 index 000000000..f36fbbcb0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/route_maps/route_maps.py @@ -0,0 +1,196 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_route_maps module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class Route_mapsArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_route_maps module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'elements': 'dict', + 'options': { + 'map_name': {'required': True, 'type': 'str'}, + 'action': { + 'choices': ['permit', 'deny'], + 'type': 'str' + }, + 'sequence_num': { + 'type': 'int' + }, + 'match': { + 'options': { + 'as_path': {'type': 'str'}, + 'community': {'type': 'str'}, + 'evpn': { + 'options': { + 'default_route': {'type': 'bool'}, + 'route_type': { + 'choices': ['macip', 'multicast', 'prefix'], + 'type': 'str' + }, + 'vni': {'type': 'int'} + }, + 'required_one_of': [['default_route', 'route_type', 'vni']], + 'type': 'dict' + }, + 'ext_comm': {'type': 'str'}, + 'interface': {'type': 'str'}, + 'ip': { + 'options': { + 'address': {'type': 'str'}, + 'next_hop': {'type': 'str'} + }, + 'required_one_of': [['address', 'next_hop']], + 'type': 'dict' + }, + 'ipv6': { + 'options': { + 'address': { + 'required': True, + 'type': 'str' + } + }, + 'type': 'dict' + }, + 'local_preference': {'type': 'int'}, + 'metric': {'type': 'int'}, + 'origin': { + 'choices': ['egp', 'igp', 'incomplete'], + 'type': 'str' + }, + 'peer': { + 'mutually_exclusive': [['ip', 'ipv6', 'interface']], + 'options': { + 'interface': {'type': 'str'}, + 'ip': {'type': 'str'}, + 'ipv6': {'type': 'str'} + }, + 'required_one_of': [['ip', 'ipv6', 'interface']], + 'type': 'dict' + }, + 'source_protocol': { + 'choices': ['bgp', 'connected', 'ospf', 'static'], + 'type': 'str' + }, + 'source_vrf': {'type': 'str'}, + 'tag': {'type': 'int'} + }, + 'type': 'dict' + }, + 'set': { + 'options': { + 'as_path_prepend': {'type': 'str'}, + 'comm_list_delete': {'type': 'str'}, + 'community': { + 'options': { + 'community_number': { + 'elements': 'str', + 'type': 'list' + }, + 'community_attributes': { + 'elements': 'str', + 'type': 'list', + 'mutually_exclusive': [ + ['none', 'local_as'], + ['none', 'no_advertise'], + ['none', 'no_export'], + ['none', 'no_peer'], + ['none', 'additive'] + ], + 'choices': [ + 'local_as', + 'no_advertise', + 'no_export', + 'no_peer', + 'additive', + 'none' + ] + }, + }, + 'type': 'dict' + }, + 'extcommunity': { + 'options': { + 'rt': { + 'elements': 'str', + 'type': 'list' + }, + 'soo': { + 'elements': 'str', + 'type': 'list' + } + }, + 'required_one_of': [['rt', 'soo']], + 'type': 'dict' + }, + 'ip_next_hop': {'type': 'str'}, + 'ipv6_next_hop': { + 'options': { + 'global_addr': {'type': 'str'}, + 'prefer_global': {'type': 'bool'} + }, + 'required_one_of': [['global_addr', 'prefer_global']], + 'type': 'dict'}, + 'local_preference': {'type': 'int'}, + 'metric': { + 'mutually_exclusive': [['value', 'rtt_action']], + 'required_one_of': [['value', 'rtt_action']], + 'options': { + 'rtt_action': { + 'choices': ['set', 'add', 'subtract'], + 'type': 'str' + }, + 'value': {'type': 'int'} + }, + 'type': 'dict' + }, + 'origin': { + 'choices': ['egp', 'igp', 'incomplete'], + 'type': 'str' + }, + 'weight': {'type': 'int'} + }, + 'type': 'dict' + }, + 'call': {'type': 'str'}, + }, + 'type': 'list' + }, + 'state': { + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], + 'default': 'merged', + 'type': 'str' + } + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/static_routes/static_routes.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/static_routes/static_routes.py index a146f1ecd..3dbaf3045 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/static_routes/static_routes.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/static_routes/static_routes.py @@ -72,7 +72,7 @@ class Static_routesArgs(object): # pylint: disable=R0903 'type': 'list' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'deleted', 'overridden', 'replaced'], 'default': 'merged', 'type': 'str' } diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/stp/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/stp/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/stp/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/stp/stp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/stp/stp.py new file mode 100644 index 000000000..145632051 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/stp/stp.py @@ -0,0 +1,152 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_stp module +""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +class StpArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_stp module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'mutually_exclusive': [['mstp', 'pvst', 'rapid_pvst']], + 'options': { + 'global': { + 'options': { + 'bpdu_filter': {'default': False, 'type': 'bool'}, + 'bridge_priority': {'default': 32768, 'type': 'int'}, + 'disabled_vlans': {'elements': 'str', 'type': 'list'}, + 'enabled_protocol': {'choices': ['mst', 'pvst', 'rapid_pvst'], 'type': 'str'}, + 'fwd_delay': {'default': 15, 'type': 'int'}, + 'hello_time': {'default': 2, 'type': 'int'}, + 'loop_guard': {'default': False, 'type': 'bool'}, + 'max_age': {'default': 20, 'type': 'int'}, + 'portfast': {'default': False, 'type': 'bool'}, + 'root_guard_timeout': {'type': 'int'} + }, + 'type': 'dict' + }, + 'interfaces': { + 'elements': 'dict', + 'options': { + 'bpdu_filter': {'default': False, 'type': 'bool'}, + 'bpdu_guard': {'default': False, 'type': 'bool'}, + 'cost': {'type': 'int'}, + 'edge_port': {'default': False, 'type': 'bool'}, + 'guard': {'choices': ['loop', 'root', 'none'], 'type': 'str'}, + 'intf_name': {'required': True, 'type': 'str'}, + 'link_type': {'choices': ['point-to-point', 'shared'], 'type': 'str'}, + 'port_priority': {'type': 'int'}, + 'portfast': {'default': False, 'type': 'bool'}, + 'shutdown': {'default': False, 'type': 'bool'}, + 'stp_enable': {'default': True, 'type': 'bool'}, + 'uplink_fast': {'default': False, 'type': 'bool'} + }, + 'type': 'list' + }, + 'mstp': { + 'options': { + 'fwd_delay': {'type': 'int'}, + 'hello_time': {'type': 'int'}, + 'max_age': {'type': 'int'}, + 'max_hop': {'type': 'int'}, + 'mst_instances': { + 'elements': 'dict', + 'options': { + 'bridge_priority': {'type': 'int'}, + 'mst_id': {'required': True, 'type': 'int'}, + 'vlans': {'elements': 'str', 'type': 'list'}, + 'interfaces': { + 'elements': 'dict', + 'options': { + 'cost': {'type': 'int'}, + 'intf_name': {'required': True, 'type': 'str'}, + 'port_priority': {'type': 'int'} + }, + 'type': 'list' + } + }, + 'type': 'list' + }, + 'mst_name': {'type': 'str'}, + 'revision': {'type': 'int'} + }, + 'type': 'dict' + }, + 'pvst': { + 'elements': 'dict', + 'options': { + 'bridge_priority': {'type': 'int'}, + 'fwd_delay': {'type': 'int'}, + 'hello_time': {'type': 'int'}, + 'vlan_id': {'required': True, 'type': 'int'}, + 'max_age': {'type': 'int'}, + 'interfaces': { + 'elements': 'dict', + 'options': { + 'cost': {'type': 'int'}, + 'intf_name': {'required': True, 'type': 'str'}, + 'port_priority': {'type': 'int'} + }, + 'type': 'list' + } + }, + 'type': 'list' + }, + 'rapid_pvst': { + 'elements': 'dict', + 'options': { + 'bridge_priority': {'type': 'int'}, + 'fwd_delay': {'type': 'int'}, + 'hello_time': {'type': 'int'}, + 'vlan_id': {'required': True, 'type': 'int'}, + 'max_age': {'type': 'int'}, + 'interfaces': { + 'elements': 'dict', + 'options': { + 'cost': {'type': 'int'}, + 'intf_name': {'required': True, 'type': 'str'}, + 'port_priority': {'type': 'int'} + }, + 'type': 'list' + } + }, + 'type': 'list' + } + }, + 'type': 'dict' + }, + 'state': { + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], + 'default': 'merged', 'type': 'str' + } + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/system/system.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/system/system.py index b08c5f4bc..df835c156 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/system/system.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/system/system.py @@ -57,7 +57,7 @@ class SystemArgs(object): # pylint: disable=R0903 'type': 'dict' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'replaced', 'overridden', 'deleted'], 'default': 'merged', 'type': 'str' } diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/tacacs_server/tacacs_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/tacacs_server/tacacs_server.py index aad1746d4..98df26913 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/tacacs_server/tacacs_server.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/tacacs_server/tacacs_server.py @@ -69,12 +69,12 @@ class Tacacs_serverArgs(object): # pylint: disable=R0903 'type': 'dict' }, 'source_interface': {'type': 'str'}, - 'timeout': {'type': 'int'} + 'timeout': {'type': 'int', 'default': 5} }, 'type': 'dict' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'replaced', 'overridden', 'deleted'], 'default': 'merged' } } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/users/users.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/users/users.py index db23d78e0..7adca72a1 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/users/users.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/users/users.py @@ -44,7 +44,7 @@ class UsersArgs(object): # pylint: disable=R0903 'name': {'required': True, 'type': 'str'}, 'password': {'type': 'str', 'no_log': True}, 'role': { - 'choices': ['admin', 'operator'], + 'choices': ['admin', 'operator', 'netadmin', 'secadmin'], 'type': 'str' }, 'update_password': { @@ -56,7 +56,7 @@ class UsersArgs(object): # pylint: disable=R0903 'type': 'list' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'deleted', 'overridden', 'replaced'], 'default': 'merged' } } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlan_mapping/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlan_mapping/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlan_mapping/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlan_mapping/vlan_mapping.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlan_mapping/vlan_mapping.py new file mode 100644 index 000000000..ced5833fa --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlan_mapping/vlan_mapping.py @@ -0,0 +1,64 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the sonic_vlan_mapping module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class Vlan_mappingArgs(object): # pylint: disable=R0903 + """The arg spec for the sonic_vlan_mapping module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'elements': 'dict', + 'options': { + 'mapping': { + 'elements': 'dict', + 'options': { + 'dot1q_tunnel': {'type': 'bool', 'default': False}, + 'inner_vlan': {'type': 'int'}, + 'priority': {'type': 'int'}, + 'service_vlan': {'required': True, 'type': 'int'}, + 'vlan_ids': {'elements': 'str', 'type': 'list'} + }, + 'type': 'list' + }, + 'name': {'required': True, 'type': 'str'} + }, + 'type': 'list' + }, + 'state': { + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], + 'default': 'merged', + 'type': 'str' + } + } # pylint: disable=C0301 diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlans/vlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlans/vlans.py index 971fc8571..7ae8b5a47 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlans/vlans.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlans/vlans.py @@ -47,7 +47,7 @@ class VlansArgs(object): # pylint: disable=R0903 'type': 'list' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'replaced', 'overridden', 'deleted'], 'default': 'merged', 'type': 'str' } diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vrfs/vrfs.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vrfs/vrfs.py index e074936a7..992906044 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vrfs/vrfs.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vrfs/vrfs.py @@ -59,7 +59,7 @@ class VrfsArgs(object): # pylint: disable=R0903 "type": "list" }, "state": { - "choices": ["merged", "deleted"], + "choices": ["merged", "replaced", "overridden", "deleted"], "default": "merged", "type": "str" } diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vxlans/vxlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vxlans/vxlans.py index dd475b78a..e610eaca8 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vxlans/vxlans.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vxlans/vxlans.py @@ -62,11 +62,10 @@ class VxlansArgs(object): # pylint: disable=R0903 'type': 'list' } }, - 'required_together': [['source_ip', 'evpn_nvo']], 'type': 'list' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged', 'type': 'str' } diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/aaa/aaa.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/aaa/aaa.py index 85f93bc73..036567f0a 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/aaa/aaa.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/aaa/aaa.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -31,6 +31,10 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s to_request, edit_config ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + get_new_config, + get_formatted_config_diff +) PATCH = 'patch' DELETE = 'delete' @@ -89,6 +93,16 @@ class Aaa(ConfigBase): if result['changed']: result['after'] = changed_aaa_facts + new_config = changed_aaa_facts + old_config = existing_aaa_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_aaa_facts) + result['after(generated)'] = new_config + if self._module._diff: + result['diff'] = get_formatted_config_diff(old_config, + new_config, + self._module._verbosity) result['warnings'] = warnings return result @@ -123,15 +137,22 @@ class Aaa(ConfigBase): state = self._module.params['state'] if not want: want = {} + if not have: + have = {} + + diff = self.get_diff_aaa(want, have) if state == 'deleted': commands = self._state_deleted(want, have) elif state == 'merged': - diff = get_diff(want, have) - commands = self._state_merged(want, have, diff) + commands = self._state_merged(diff) + elif state == 'replaced': + commands = self._state_replaced(diff) + elif state == 'overridden': + commands = self._state_overridden(want, have) return commands - def _state_merged(self, want, have, diff): + def _state_merged(self, diff): """ The command generator when state is merged :rtype: A list @@ -171,6 +192,49 @@ class Aaa(ConfigBase): commands = update_states(diff_want, "deleted") return commands, requests + def _state_replaced(self, diff): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + requests = [] + if diff: + requests = self.get_create_aaa_request(diff) + if len(requests) > 0: + commands = update_states(diff, "replaced") + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + + if have and have != want: + del_requests = self.get_delete_all_aaa_request(have) + requests.extend(del_requests) + commands.extend(update_states(have, "deleted")) + have = [] + + if not have and want: + mod_commands = want + mod_requests = self.get_create_aaa_request(mod_commands) + + if len(mod_requests) > 0: + requests.extend(mod_requests) + commands.extend(update_states(mod_commands, "overridden")) + + return commands, requests + def get_create_aaa_request(self, commands): requests = [] aaa_path = 'data/openconfig-system:system/aaa' @@ -183,13 +247,17 @@ class Aaa(ConfigBase): def build_create_aaa_payload(self, commands): payload = {} + auth_method_list = [] if "authentication" in commands and commands["authentication"]: - payload = {"openconfig-system:aaa": {"authentication": {"config": {"authentication-method": []}}}} + payload = {"openconfig-system:aaa": {"authentication": {"config": {}}}} if "local" in commands["authentication"]["data"] and commands["authentication"]["data"]["local"]: - payload['openconfig-system:aaa']['authentication']['config']['authentication-method'].append("local") + auth_method_list.append('local') if "group" in commands["authentication"]["data"] and commands["authentication"]["data"]["group"]: auth_method = commands["authentication"]["data"]["group"] - payload['openconfig-system:aaa']['authentication']['config']['authentication-method'].append(auth_method) + auth_method_list.append(auth_method) + if auth_method_list: + cfg = {'authentication-method': auth_method_list} + payload['openconfig-system:aaa']['authentication']['config'].update(cfg) if "fail_through" in commands["authentication"]["data"]: cfg = {'failthrough': str(commands["authentication"]["data"]["fail_through"])} payload['openconfig-system:aaa']['authentication']['config'].update(cfg) @@ -234,3 +302,53 @@ class Aaa(ConfigBase): method = DELETE request = {'path': path, 'method': method} return request + + # Current SONiC code behavior for patch overwrites the OC authentication-method leaf-list + # This function serves as a workaround for the issue, allowing the user to append to the + # OC authentication-method leaf-list. + def get_diff_aaa(self, want, have): + diff_cfg = {} + diff_authentication = {} + diff_data = {} + + authentication = want.get('authentication', None) + if authentication: + data = authentication.get('data', None) + if data: + fail_through = data.get('fail_through', None) + local = data.get('local', None) + group = data.get('group', None) + + cfg_authentication = have.get('authentication', None) + if cfg_authentication: + cfg_data = cfg_authentication.get('data', None) + if cfg_data: + cfg_fail_through = cfg_data.get('fail_through', None) + cfg_local = cfg_data.get('local', None) + cfg_group = cfg_data.get('group', None) + + if fail_through is not None and fail_through != cfg_fail_through: + diff_data['fail_through'] = fail_through + if local and local != cfg_local: + diff_data['local'] = local + if group and group != cfg_group: + diff_data['group'] = group + + diff_local = diff_data.get('local', None) + diff_group = diff_data.get('group', None) + if diff_local and not diff_group and cfg_group: + diff_data['group'] = cfg_group + if diff_group and not diff_local and cfg_local: + diff_data['local'] = cfg_local + else: + if fail_through is not None: + diff_data['fail_through'] = fail_through + if local: + diff_data['local'] = local + if group: + diff_data['group'] = group + if diff_data: + diff_authentication['data'] = diff_data + diff_cfg['authentication'] = diff_authentication + + return diff_cfg diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/acl_interfaces/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/acl_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/acl_interfaces/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/acl_interfaces/acl_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/acl_interfaces/acl_interfaces.py new file mode 100644 index 000000000..0413a585d --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/acl_interfaces/acl_interfaces.py @@ -0,0 +1,499 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_acl_interfaces class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + remove_empties, + validate_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + get_diff, + update_states, + normalize_interface_name +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff +) +from ansible.module_utils.connection import ConnectionError + +DELETE = 'delete' +POST = 'post' + +TEST_KEYS = [ + {'config': {'name': ''}}, + {'access_groups': {'type': ''}}, + {'acls': {'name': ''}} +] + +TEST_KEYS_formatted_diff = [ + {'config': {'name': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, + {'access_groups': {'type': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, + {'acls': {'name': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, +] + +acl_type_to_payload_map = { + 'mac': 'ACL_L2', + 'ipv4': 'ACL_IPV4', + 'ipv6': 'ACL_IPV6' +} + + +class Acl_interfaces(ConfigBase): + """ + The sonic_acl_interfaces class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'acl_interfaces', + ] + + acl_interfaces_path = 'data/openconfig-acl:acl/interfaces/interface={intf_name}' + ingress_acl_set_path = acl_interfaces_path + '/ingress-acl-sets/ingress-acl-set={acl_name},{acl_type}' + egress_acl_set_path = acl_interfaces_path + '/egress-acl-sets/egress-acl-set={acl_name},{acl_type}' + + def __init__(self, module): + super(Acl_interfaces, self).__init__(module) + + def get_acl_interfaces_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + acl_interfaces_facts = facts['ansible_network_resources'].get('acl_interfaces') + if not acl_interfaces_facts: + return [] + return acl_interfaces_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = [] + + existing_acl_interfaces_facts = self.get_acl_interfaces_facts() + commands, requests = self.set_config(existing_acl_interfaces_facts) + if commands: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + result['changed'] = True + + changed_acl_interfaces_facts = self.get_acl_interfaces_facts() + + result['before'] = existing_acl_interfaces_facts + if result['changed']: + result['after'] = changed_acl_interfaces_facts + + result['commands'] = commands + + new_config = changed_acl_interfaces_facts + old_config = existing_acl_interfaces_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_acl_interfaces_facts, + TEST_KEYS_formatted_diff) + self.post_process_generated_config(new_config) + result['after(generated)'] = new_config + if self._module._diff: + self.sort_config(new_config) + self.sort_config(old_config) + result['diff'] = get_formatted_config_diff(old_config, + new_config, + self._module._verbosity) + result['warnings'] = warnings + return result + + def set_config(self, existing_acl_interfaces_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + if want: + want = self.validate_and_normalize_config(want) + else: + want = [] + + have = existing_acl_interfaces_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + state = self._module.params['state'] + if state == 'overridden': + commands, requests = self._state_overridden(want, have) + elif state == 'deleted': + commands, requests = self._state_deleted(want, have) + elif state == 'merged': + commands, requests = self._state_merged(want, have) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have) + + return commands, requests + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + del_commands = [] + add_commands = [] + + have_interfaces = self.get_interface_names(have) + want_interfaces = self.get_interface_names(want) + interfaces_to_replace = have_interfaces.intersection(want_interfaces) + + del_diff = get_diff(have, want, TEST_KEYS) + for cmd in del_diff: + if cmd['name'] in interfaces_to_replace: + del_commands.append(cmd) + + if del_commands: + commands = update_states(del_commands, 'deleted') + requests.extend(self.get_interfaces_acl_unbind_requests(del_commands)) + + add_diff = get_diff(want, have, TEST_KEYS) + # Handle scenarios in replaced state, when only the interface + # name is specified for deleting all ACL bindings in it. + for cmd in add_diff: + if cmd.get('access_groups'): + add_commands.append(cmd) + + if add_commands: + commands.extend(update_states(add_commands, 'replaced')) + requests.extend(self.get_interfaces_acl_bind_requests(add_commands)) + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + del_commands = [] + + have_interfaces = self.get_interface_names(have) + want_interfaces = self.get_interface_names(want) + interfaces_to_delete = have_interfaces.difference(want_interfaces) + interfaces_to_override = have_interfaces.intersection(want_interfaces) + + del_diff = get_diff(have, want, TEST_KEYS) + for cmd in del_diff: + if cmd['name'] in interfaces_to_delete: + del_commands.append({'name': cmd['name']}) + elif cmd['name'] in interfaces_to_override: + del_commands.append(cmd) + + if del_commands: + commands = update_states(del_commands, 'deleted') + requests.extend(self.get_interfaces_acl_unbind_requests(del_commands)) + + diff = get_diff(want, have, TEST_KEYS) + if diff: + commands.extend(update_states(diff, 'overridden')) + requests.extend(self.get_interfaces_acl_bind_requests(diff)) + + return commands, requests + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + requests = [] + + diff = get_diff(want, have, TEST_KEYS) + if diff: + requests = self.get_interfaces_acl_bind_requests(diff) + commands = update_states(diff, 'merged') + + return commands, requests + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + requests = [] + + if not want: + # Delete all interface ACL bindings in the chassis + for cfg in have: + commands.append({'name': cfg['name']}) + else: + want_dict = self._convert_config_list_to_dict(want) + have_dict = self._convert_config_list_to_dict(have) + + for intf_name, access_groups in want_dict.items(): + have_obj = have_dict.get(intf_name) + if not have_obj: + continue + + if not access_groups: + commands.append({'name': intf_name}) + else: + access_groups_to_del = [] + for acl_type, acls in access_groups.items(): + acls_to_delete = [] + if not have_obj.get(acl_type): + continue + + # Delete all bindings of ACLs belonging to a type in an + # interface, if only the ACL type is provided + if not acls: + for acl_name, direction in have_obj[acl_type].items(): + acls_to_delete.append({'name': acl_name, 'direction': direction}) + else: + for acl_name, direction in acls.items(): + if have_obj[acl_type].get(acl_name) and direction == have_obj[acl_type][acl_name]: + acls_to_delete.append({'name': acl_name, 'direction': direction}) + + if acls_to_delete: + access_groups_to_del.append({'type': acl_type, 'acls': acls_to_delete}) + + if access_groups_to_del: + commands.append({'name': intf_name, 'access_groups': access_groups_to_del}) + + if commands: + requests = self.get_interfaces_acl_unbind_requests(commands) + commands = update_states(commands, 'deleted') + + return commands, requests + + def get_interfaces_acl_bind_requests(self, commands): + """Get requests to bind specified ACLs for all interfaces + specified the commands + """ + requests = [] + + for command in commands: + intf_name = command['name'] + url = self.acl_interfaces_path.format(intf_name=intf_name) + for access_group in command['access_groups']: + for acl in access_group['acls']: + if acl['direction'] == 'in': + payload = { + 'openconfig-acl:config': { + 'id': intf_name + }, + 'openconfig-acl:interface-ref': { + 'config': { + 'interface': intf_name.split('.')[0] + } + }, + 'openconfig-acl:ingress-acl-sets': { + 'ingress-acl-set': [ + { + 'set-name': acl['name'], + 'type': acl_type_to_payload_map[access_group['type']], + 'config': { + 'set-name': acl['name'], + 'type': acl_type_to_payload_map[access_group['type']] + } + } + ] + } + } + else: + payload = { + 'openconfig-acl:config': { + 'id': intf_name + }, + 'openconfig-acl:interface-ref': { + 'config': { + 'interface': intf_name.split('.')[0] + } + }, + 'openconfig-acl:egress-acl-sets': { + 'egress-acl-set': [ + { + 'set-name': acl['name'], + 'type': acl_type_to_payload_map[access_group['type']], + 'config': { + 'set-name': acl['name'], + 'type': acl_type_to_payload_map[access_group['type']] + } + } + ] + } + } + + # Update the payload for subinterfaces + if '.' in intf_name: + payload['openconfig-acl:interface-ref']['config']['subinterface'] = int(intf_name.split('.')[1]) + + requests.append({'path': url, 'method': POST, 'data': payload}) + + return requests + + def get_interfaces_acl_unbind_requests(self, commands): + """Get requests to unbind specified ACLs for all interfaces + specified in the commands + """ + requests = [] + + for command in commands: + intf_name = command['name'] + # Delete all acl bindings in an interface, if only the + # interface name is provided + if not command.get('access_groups'): + url = self.acl_interfaces_path.format(intf_name=intf_name) + requests.append({'path': url, 'method': DELETE}) + else: + for access_group in command['access_groups']: + for acl in access_group['acls']: + if acl['direction'] == 'in': + url = self.ingress_acl_set_path.format(intf_name=intf_name, acl_name=acl['name'], + acl_type=acl_type_to_payload_map[access_group['type']]) + requests.append({'path': url, 'method': DELETE}) + else: + url = self.egress_acl_set_path.format(intf_name=intf_name, acl_name=acl['name'], + acl_type=acl_type_to_payload_map[access_group['type']]) + requests.append({'path': url, 'method': DELETE}) + + return requests + + def validate_and_normalize_config(self, config_list): + """Validate and normalize the given config""" + # Remove empties and validate the config with argument spec + config_list = [remove_empties(config) for config in config_list] + validate_config(self._module.argument_spec, {'config': config_list}) + normalize_interface_name(config_list, self._module) + + state = self._module.params['state'] + # When state is deleted, empty access_groups and acls are + # supported and therefore no futher changes are required. + if state == 'deleted': + return config_list + + updated_config_list = [] + for config in config_list: + if not config.get('access_groups'): + # When state is replaced, if only the interface name is + # specified for deleting all ACL bindings in it do not + # remove that config. + if state == 'replaced': + updated_config_list.append(config) + else: + access_group_list = [] + for access_group in config['access_groups']: + if access_group.get('acls'): + access_group_list.append(access_group) + + if access_group_list: + updated_config_list.append({'name': config['name'], 'access_groups': access_group_list}) + + return updated_config_list + + @staticmethod + def get_interface_names(config_list): + """Get a set of interface names available in the given + config_list dict + """ + interface_names = set() + for config in config_list: + interface_names.add(config['name']) + + return interface_names + + @staticmethod + def _convert_config_list_to_dict(config_list): + config_dict = {} + + for config in config_list: + config_dict[config['name']] = {} + if config.get('access_groups'): + for access_group in config['access_groups']: + config_dict[config['name']][access_group['type']] = {} + if access_group.get('acls'): + for acl in access_group['acls']: + config_dict[config['name']][access_group['type']][acl['name']] = acl['direction'] + + return config_dict + + def sort_config(self, configs): + # natsort provides better result. + # The use of natsort causes sanity error due to it is not available in + # python version currently used. + # new_config = natsorted(new_config, key=lambda x: x['name']) + # For time-being, use simple "sort" + configs.sort(key=lambda x: x['name']) + + for conf in configs: + ags = conf.get('access_groups', []) + if ags: + ags.sort(key=lambda x: x['type']) + for ag in ags: + if ag.get('acls', []): + ag['acls'].sort(key=lambda x: x['name']) + + def post_process_generated_config(self, configs): + for conf in configs[:]: + ags = conf.get('access_groups', []) + if ags: + for ag in ags[:]: + if not ag.get('acls', []): + ags.remove(ag) + + if not conf.get('access_groups', []): + configs.remove(conf) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bfd/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bfd/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bfd/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bfd/bfd.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bfd/bfd.py new file mode 100644 index 000000000..484c4203d --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bfd/bfd.py @@ -0,0 +1,734 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_bfd class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + get_diff, + get_replaced_config, + send_requests, + remove_empties, + update_states +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) + +from copy import deepcopy + + +BFD_PATH = '/data/openconfig-bfd:bfd' +PATCH = 'patch' +DELETE = 'delete' +TEST_KEYS = [ + {'profiles': {'profile_name': ''}}, + {'single_hops': {'remote_address': '', 'vrf': '', 'interface': '', 'local_address': ''}}, + {'multi_hops': {'remote_address': '', 'vrf': '', 'local_address': ''}} +] + + +class Bfd(ConfigBase): + """ + The sonic_bfd class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'bfd', + ] + + def __init__(self, module): + super(Bfd, self).__init__(module) + + def get_bfd_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + bfd_facts = facts['ansible_network_resources'].get('bfd') + if not bfd_facts: + return {} + return bfd_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = [] + commands = [] + + existing_bfd_facts = self.get_bfd_facts() + commands, requests = self.set_config(existing_bfd_facts) + if commands and len(requests) > 0: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + result['changed'] = True + result['commands'] = commands + + changed_bfd_facts = self.get_bfd_facts() + + result['before'] = existing_bfd_facts + if result['changed']: + result['after'] = changed_bfd_facts + + result['warnings'] = warnings + return result + + def set_config(self, existing_bfd_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + have = existing_bfd_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + state = self._module.params['state'] + diff = get_diff(want, have, TEST_KEYS) + + if state == 'overridden': + commands, requests = self._state_overridden(want, have) + elif state == 'deleted': + commands, requests = self._state_deleted(want, have) + elif state == 'merged': + commands, requests = self._state_merged(diff) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have, diff) + return commands, requests + + def _state_replaced(self, want, have, diff): + """ The command generator when state is replaced + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + replaced_config = get_replaced_config(want, have, TEST_KEYS) + + if replaced_config: + self.sort_lists_in_config(replaced_config) + self.sort_lists_in_config(have) + is_delete_all = (replaced_config == have) + requests = self.get_delete_bfd_requests(replaced_config, have, is_delete_all) + send_requests(self._module, requests) + + commands = want + else: + commands = diff + + requests = [] + + if commands: + requests = self.get_modify_bfd_request(commands) + + if len(requests) > 0: + commands = update_states(commands, "replaced") + else: + commands = [] + else: + commands = [] + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + self.sort_lists_in_config(want) + self.sort_lists_in_config(have) + + if have and have != want: + is_delete_all = True + requests = self.get_delete_bfd_requests(have, None, is_delete_all) + send_requests(self._module, requests) + have = [] + + commands = [] + requests = [] + + if not have and want: + commands = want + requests = self.get_modify_bfd_request(commands) + + if len(requests) > 0: + commands = update_states(commands, "overridden") + else: + commands = [] + + return commands, requests + + def _state_merged(self, diff): + """ The command generator when state is merged + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = diff + requests = self.get_modify_bfd_request(commands) + + if commands and len(requests) > 0: + commands = update_states(commands, "merged") + else: + commands = [] + + return commands, requests + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + :param want: the objects from which the configuration should be removed + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + is_delete_all = False + want = remove_empties(want) + if not want: + commands = deepcopy(have) + is_delete_all = True + else: + commands = deepcopy(want) + + self.remove_default_entries(commands) + requests = self.get_delete_bfd_requests(commands, have, is_delete_all) + + if commands and len(requests) > 0: + commands = update_states(commands, "deleted") + else: + commands = [] + return commands, requests + + def get_modify_bfd_request(self, commands): + request = None + + profiles = commands.get('profiles', None) + single_hops = commands.get('single_hops', None) + multi_hops = commands.get('multi_hops', None) + bfd_dict = {} + bfd_profile_dict = {} + bfd_shop_dict = {} + bfd_mhop_dict = {} + + if profiles: + profile_list = [] + for profile in profiles: + profile_dict = {} + config_dict = {} + profile_name = profile.get('profile_name', None) + enabled = profile.get('enabled', None) + transmit_interval = profile.get('transmit_interval', None) + receive_interval = profile.get('receive_interval', None) + detect_multiplier = profile.get('detect_multiplier', None) + passive_mode = profile.get('passive_mode', None) + min_ttl = profile.get('min_ttl', None) + echo_interval = profile.get('echo_interval', None) + echo_mode = profile.get('echo_mode', None) + + if profile_name: + profile_dict['profile-name'] = profile_name + config_dict['profile-name'] = profile_name + if enabled is not None: + config_dict['enabled'] = enabled + if transmit_interval: + config_dict['desired-minimum-tx-interval'] = transmit_interval + if receive_interval: + config_dict['required-minimum-receive'] = receive_interval + if detect_multiplier: + config_dict['detection-multiplier'] = detect_multiplier + if passive_mode is not None: + config_dict['passive-mode'] = passive_mode + if min_ttl: + config_dict['minimum-ttl'] = min_ttl + if echo_interval: + config_dict['desired-minimum-echo-receive'] = echo_interval + if echo_mode is not None: + config_dict['echo-active'] = echo_mode + if config_dict: + profile_dict['config'] = config_dict + profile_list.append(profile_dict) + if profile_list: + bfd_profile_dict['profile'] = profile_list + + if single_hops: + single_hop_list = [] + for hop in single_hops: + hop_dict = {} + config_dict = {} + remote_address = hop.get('remote_address', None) + vrf = hop.get('vrf', None) + interface = hop.get('interface', None) + local_address = hop.get('local_address', None) + enabled = hop.get('enabled', None) + transmit_interval = hop.get('transmit_interval', None) + receive_interval = hop.get('receive_interval', None) + detect_multiplier = hop.get('detect_multiplier', None) + passive_mode = hop.get('passive_mode', None) + echo_interval = hop.get('echo_interval', None) + echo_mode = hop.get('echo_mode', None) + profile_name = hop.get('profile_name', None) + + if remote_address: + hop_dict['remote-address'] = remote_address + config_dict['remote-address'] = remote_address + if vrf: + hop_dict['vrf'] = vrf + config_dict['vrf'] = vrf + if interface: + hop_dict['interface'] = interface + config_dict['interface'] = interface + if local_address: + hop_dict['local-address'] = local_address + config_dict['local-address'] = local_address + if enabled is not None: + config_dict['enabled'] = enabled + if transmit_interval: + config_dict['desired-minimum-tx-interval'] = transmit_interval + if receive_interval: + config_dict['required-minimum-receive'] = receive_interval + if detect_multiplier: + config_dict['detection-multiplier'] = detect_multiplier + if passive_mode is not None: + config_dict['passive-mode'] = passive_mode + if echo_interval: + config_dict['desired-minimum-echo-receive'] = echo_interval + if echo_mode is not None: + config_dict['echo-active'] = echo_mode + if profile_name: + config_dict['profile-name'] = profile_name + if config_dict: + hop_dict['config'] = config_dict + single_hop_list.append(hop_dict) + if single_hop_list: + bfd_shop_dict['single-hop'] = single_hop_list + + if multi_hops: + multi_hop_list = [] + for hop in multi_hops: + hop_dict = {} + config_dict = {} + remote_address = hop.get('remote_address', None) + vrf = hop.get('vrf', None) + local_address = hop.get('local_address', None) + enabled = hop.get('enabled', None) + transmit_interval = hop.get('transmit_interval', None) + receive_interval = hop.get('receive_interval', None) + detect_multiplier = hop.get('detect_multiplier', None) + passive_mode = hop.get('passive_mode', None) + min_ttl = hop.get('min_ttl', None) + profile_name = hop.get('profile_name', None) + + if remote_address: + hop_dict['remote-address'] = remote_address + config_dict['remote-address'] = remote_address + if vrf: + hop_dict['vrf'] = vrf + config_dict['vrf'] = vrf + if local_address: + hop_dict['local-address'] = local_address + config_dict['local-address'] = local_address + if enabled is not None: + config_dict['enabled'] = enabled + if transmit_interval: + config_dict['desired-minimum-tx-interval'] = transmit_interval + if receive_interval: + config_dict['required-minimum-receive'] = receive_interval + if detect_multiplier: + config_dict['detection-multiplier'] = detect_multiplier + if passive_mode is not None: + config_dict['passive-mode'] = passive_mode + if min_ttl: + config_dict['minimum-ttl'] = min_ttl + if profile_name: + config_dict['profile-name'] = profile_name + if config_dict: + config_dict['interface'] = 'null' + hop_dict['interface'] = 'null' + hop_dict['config'] = config_dict + multi_hop_list.append(hop_dict) + if multi_hop_list: + bfd_mhop_dict['multi-hop'] = multi_hop_list + + if bfd_profile_dict: + bfd_dict['openconfig-bfd-ext:bfd-profile'] = bfd_profile_dict + if bfd_shop_dict: + bfd_dict['openconfig-bfd-ext:bfd-shop-sessions'] = bfd_shop_dict + if bfd_mhop_dict: + bfd_dict['openconfig-bfd-ext:bfd-mhop-sessions'] = bfd_mhop_dict + if bfd_dict: + payload = {'openconfig-bfd:bfd': bfd_dict} + request = {'path': BFD_PATH, 'method': PATCH, 'data': payload} + + return request + + def get_delete_bfd_requests(self, commands, have, is_delete_all): + requests = [] + + if not commands: + return requests + + if is_delete_all: + requests.extend(self.get_delete_all_bfd_cfg_requests(commands)) + else: + requests.extend(self.get_delete_bfd_profile_requests(commands, have)) + requests.extend(self.get_delete_bfd_shop_requests(commands, have)) + requests.extend(self.get_delete_bfd_mhop_requests(commands, have)) + + return requests + + def get_delete_bfd_profile_requests(self, commands, have): + requests = [] + + profiles = commands.get('profiles', None) + if profiles: + for profile in profiles: + profile_name = profile.get('profile_name', None) + enabled = profile.get('enabled', None) + transmit_interval = profile.get('transmit_interval', None) + receive_interval = profile.get('receive_interval', None) + detect_multiplier = profile.get('detect_multiplier', None) + passive_mode = profile.get('passive_mode', None) + min_ttl = profile.get('min_ttl', None) + echo_interval = profile.get('echo_interval', None) + echo_mode = profile.get('echo_mode', None) + + cfg_profiles = have.get('profiles', None) + if cfg_profiles: + for cfg_profile in cfg_profiles: + cfg_profile_name = cfg_profile.get('profile_name', None) + cfg_enabled = cfg_profile.get('enabled', None) + cfg_transmit_interval = cfg_profile.get('transmit_interval', None) + cfg_receive_interval = cfg_profile.get('receive_interval', None) + cfg_detect_multiplier = cfg_profile.get('detect_multiplier', None) + cfg_passive_mode = cfg_profile.get('passive_mode', None) + cfg_min_ttl = cfg_profile.get('min_ttl', None) + cfg_echo_interval = cfg_profile.get('echo_interval', None) + cfg_echo_mode = cfg_profile.get('echo_mode', None) + + if profile_name == cfg_profile_name: + if enabled is not None and enabled == cfg_enabled: + requests.append(self.get_delete_profile_attr_request(profile_name, 'enabled')) + if transmit_interval and transmit_interval == cfg_transmit_interval: + requests.append(self.get_delete_profile_attr_request(profile_name, 'desired-minimum-tx-interval')) + if receive_interval and receive_interval == cfg_receive_interval: + requests.append(self.get_delete_profile_attr_request(profile_name, 'required-minimum-receive')) + if detect_multiplier and detect_multiplier == cfg_detect_multiplier: + requests.append(self.get_delete_profile_attr_request(profile_name, 'detection-multiplier')) + if passive_mode is not None and passive_mode == cfg_passive_mode: + requests.append(self.get_delete_profile_attr_request(profile_name, 'passive-mode')) + if min_ttl and min_ttl == cfg_min_ttl: + requests.append(self.get_delete_profile_attr_request(profile_name, 'minimum-ttl')) + if echo_interval and echo_interval == cfg_echo_interval: + requests.append(self.get_delete_profile_attr_request(profile_name, 'desired-minimum-echo-receive')) + if echo_mode is not None and echo_mode == cfg_echo_mode: + requests.append(self.get_delete_profile_attr_request(profile_name, 'echo-active')) + if (enabled is None and not transmit_interval and not receive_interval and not detect_multiplier and passive_mode is None + and not min_ttl and not echo_interval and echo_mode is None): + requests.append(self.get_delete_profile_request(profile_name)) + + return requests + + def get_delete_bfd_shop_requests(self, commands, have): + requests = [] + + single_hops = commands.get('single_hops', None) + if single_hops: + for hop in single_hops: + remote_address = hop.get('remote_address', None) + vrf = hop.get('vrf', None) + interface = hop.get('interface', None) + local_address = hop.get('local_address', None) + enabled = hop.get('enabled', None) + transmit_interval = hop.get('transmit_interval', None) + receive_interval = hop.get('receive_interval', None) + detect_multiplier = hop.get('detect_multiplier', None) + passive_mode = hop.get('passive_mode', None) + echo_interval = hop.get('echo_interval', None) + echo_mode = hop.get('echo_mode', None) + profile_name = hop.get('profile_name', None) + + cfg_single_hops = have.get('single_hops', None) + if cfg_single_hops: + for cfg_hop in cfg_single_hops: + cfg_remote_address = cfg_hop.get('remote_address', None) + cfg_vrf = cfg_hop.get('vrf', None) + cfg_interface = cfg_hop.get('interface', None) + cfg_local_address = cfg_hop.get('local_address', None) + cfg_enabled = cfg_hop.get('enabled', None) + cfg_transmit_interval = cfg_hop.get('transmit_interval', None) + cfg_receive_interval = cfg_hop.get('receive_interval', None) + cfg_detect_multiplier = cfg_hop.get('detect_multiplier', None) + cfg_passive_mode = cfg_hop.get('passive_mode', None) + cfg_echo_interval = cfg_hop.get('echo_interval', None) + cfg_echo_mode = cfg_hop.get('echo_mode', None) + cfg_profile_name = cfg_hop.get('profile_name', None) + + if remote_address == cfg_remote_address and vrf == cfg_vrf and interface == cfg_interface and local_address == cfg_local_address: + if enabled is not None and enabled == cfg_enabled: + requests.append(self.get_delete_shop_attr_request(remote_address, interface, vrf, local_address, 'enabled')) + if transmit_interval and transmit_interval == cfg_transmit_interval: + requests.append(self.get_delete_shop_attr_request(remote_address, interface, vrf, local_address, + 'desired-minimum-tx-interval')) + if receive_interval and receive_interval == cfg_receive_interval: + requests.append(self.get_delete_shop_attr_request(remote_address, interface, vrf, local_address, 'required-minimum-receive')) + if detect_multiplier and detect_multiplier == cfg_detect_multiplier: + requests.append(self.get_delete_shop_attr_request(remote_address, interface, vrf, local_address, 'detection-multiplier')) + if passive_mode is not None and passive_mode == cfg_passive_mode: + requests.append(self.get_delete_shop_attr_request(remote_address, interface, vrf, local_address, 'passive-mode')) + if echo_interval and echo_interval == cfg_echo_interval: + requests.append(self.get_delete_shop_attr_request(remote_address, interface, vrf, local_address, + 'desired-minimum-echo-receive')) + if echo_mode is not None and echo_mode == cfg_echo_mode: + requests.append(self.get_delete_shop_attr_request(remote_address, interface, vrf, local_address, 'echo-active')) + if profile_name and profile_name == cfg_profile_name: + requests.append(self.get_delete_shop_attr_request(remote_address, interface, vrf, local_address, 'profile-name')) + if (enabled is None and not transmit_interval and not receive_interval and not detect_multiplier and passive_mode is None + and not echo_interval and echo_mode is None and not profile_name): + requests.append(self.get_delete_shop_request(remote_address, interface, vrf, local_address)) + + return requests + + def get_delete_bfd_mhop_requests(self, commands, have): + requests = [] + + multi_hops = commands.get('multi_hops', None) + if multi_hops: + for hop in multi_hops: + remote_address = hop.get('remote_address', None) + vrf = hop.get('vrf', None) + local_address = hop.get('local_address', None) + enabled = hop.get('enabled', None) + transmit_interval = hop.get('transmit_interval', None) + receive_interval = hop.get('receive_interval', None) + detect_multiplier = hop.get('detect_multiplier', None) + passive_mode = hop.get('passive_mode', None) + min_ttl = hop.get('min_ttl', None) + profile_name = hop.get('profile_name', None) + + cfg_multi_hops = have.get('multi_hops', None) + if cfg_multi_hops: + for cfg_hop in cfg_multi_hops: + cfg_remote_address = cfg_hop.get('remote_address', None) + cfg_vrf = cfg_hop.get('vrf', None) + cfg_local_address = cfg_hop.get('local_address', None) + cfg_enabled = cfg_hop.get('enabled', None) + cfg_transmit_interval = cfg_hop.get('transmit_interval', None) + cfg_receive_interval = cfg_hop.get('receive_interval', None) + cfg_detect_multiplier = cfg_hop.get('detect_multiplier', None) + cfg_passive_mode = cfg_hop.get('passive_mode', None) + cfg_min_ttl = cfg_hop.get('min_ttl', None) + cfg_profile_name = cfg_hop.get('profile_name', None) + + if remote_address == cfg_remote_address and vrf == cfg_vrf and local_address == cfg_local_address: + if enabled is not None and enabled == cfg_enabled: + requests.append(self.get_delete_mhop_attr_request(remote_address, vrf, local_address, 'enabled')) + if transmit_interval and transmit_interval == cfg_transmit_interval: + requests.append(self.get_delete_mhop_attr_request(remote_address, vrf, local_address, 'desired-minimum-tx-interval')) + if receive_interval and receive_interval == cfg_receive_interval: + requests.append(self.get_delete_mhop_attr_request(remote_address, vrf, local_address, 'required-minimum-receive')) + if detect_multiplier and detect_multiplier == cfg_detect_multiplier: + requests.append(self.get_delete_mhop_attr_request(remote_address, vrf, local_address, 'detection-multiplier')) + if passive_mode is not None and passive_mode == cfg_passive_mode: + requests.append(self.get_delete_mhop_attr_request(remote_address, vrf, local_address, 'passive-mode')) + if min_ttl and min_ttl == cfg_min_ttl: + requests.append(self.get_delete_mhop_attr_request(remote_address, vrf, local_address, 'minimum-ttl')) + if profile_name and profile_name == cfg_profile_name: + requests.append(self.get_delete_mhop_attr_request(remote_address, vrf, local_address, 'profile-name')) + if (enabled is None and not transmit_interval and not receive_interval and not detect_multiplier and passive_mode is None + and not min_ttl and not profile_name): + requests.append(self.get_delete_mhop_request(remote_address, vrf, local_address)) + + return requests + + def get_delete_all_bfd_cfg_requests(self, commands): + requests = [] + profiles = commands.get('profiles', None) + single_hops = commands.get('single_hops', None) + multi_hops = commands.get('multi_hops', None) + + if profiles: + url = '%s/openconfig-bfd-ext:bfd-profile/profile' % (BFD_PATH) + requests.append({'path': url, 'method': DELETE}) + if single_hops: + url = '%s/openconfig-bfd-ext:bfd-shop-sessions/single-hop' % (BFD_PATH) + requests.append({'path': url, 'method': DELETE}) + if multi_hops: + url = '%s/openconfig-bfd-ext:bfd-mhop-sessions/multi-hop' % (BFD_PATH) + requests.append({'path': url, 'method': DELETE}) + + return requests + + def get_delete_profile_request(self, profile_name): + url = '%s/openconfig-bfd-ext:bfd-profile/profile=%s' % (BFD_PATH, profile_name) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_profile_attr_request(self, profile_name, attr): + url = '%s/openconfig-bfd-ext:bfd-profile/profile=%s/config/%s' % (BFD_PATH, profile_name, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_shop_request(self, remote_address, interface, vrf, local_address): + url = '%s/openconfig-bfd-ext:bfd-shop-sessions/single-hop=%s,%s,%s,%s' % (BFD_PATH, remote_address, interface, vrf, local_address) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_shop_attr_request(self, remote_address, interface, vrf, local_address, attr): + url = '%s/openconfig-bfd-ext:bfd-shop-sessions/single-hop=%s,%s,%s,%s/config/%s' % (BFD_PATH, remote_address, interface, vrf, local_address, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_mhop_request(self, remote_address, vrf, local_address): + url = '%s/openconfig-bfd-ext:bfd-mhop-sessions/multi-hop=%s,null,%s,%s' % (BFD_PATH, remote_address, vrf, local_address) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_mhop_attr_request(self, remote_address, vrf, local_address, attr): + url = '%s/openconfig-bfd-ext:bfd-mhop-sessions/multi-hop=%s,null,%s,%s/config/%s' % (BFD_PATH, remote_address, vrf, local_address, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_profile_name(self, profile_name): + return profile_name.get('profile_name') + + def sort_lists_in_config(self, config): + if 'profiles' in config and config['profiles'] is not None: + config['profiles'].sort(key=self.get_profile_name) + if 'single_hops' in config and config['single_hops'] is not None: + config['single_hops'].sort(key=lambda x: (x['remote_address'], x['interface'], x['vrf'], x['local_address'])) + if 'multi_hops' in config and config['multi_hops'] is not None: + config['multi_hops'].sort(key=lambda x: (x['remote_address'], x['vrf'], x['local_address'])) + + def remove_default_entries(self, data): + + profiles = data.get('profiles', None) + single_hops = data.get('single_hops', None) + multi_hops = data.get('multi_hops', None) + + if profiles: + for profile in profiles: + enabled = profile.get('enabled', None) + transmit_interval = profile.get('transmit_interval', None) + receive_interval = profile.get('receive_interval', None) + detect_multiplier = profile.get('detect_multiplier', None) + passive_mode = profile.get('passive_mode', None) + min_ttl = profile.get('min_ttl', None) + echo_interval = profile.get('echo_interval', None) + echo_mode = profile.get('echo_mode', None) + + if enabled: + profile.pop('enabled') + if transmit_interval == 300: + profile.pop('transmit_interval') + if receive_interval == 300: + profile.pop('receive_interval') + if detect_multiplier == 3: + profile.pop('detect_multiplier') + if passive_mode is False: + profile.pop('passive_mode') + if min_ttl == 254: + profile.pop('min_ttl') + if echo_interval == 300: + profile.pop('echo_interval') + if echo_mode is False: + profile.pop('echo_mode') + + if single_hops: + for hop in single_hops: + enabled = hop.get('enabled', None) + transmit_interval = hop.get('transmit_interval', None) + receive_interval = hop.get('receive_interval', None) + detect_multiplier = hop.get('detect_multiplier', None) + passive_mode = hop.get('passive_mode', None) + echo_interval = hop.get('echo_interval', None) + echo_mode = hop.get('echo_mode', None) + + if enabled: + hop.pop('enabled') + if transmit_interval == 300: + hop.pop('transmit_interval') + if receive_interval == 300: + hop.pop('receive_interval') + if detect_multiplier == 3: + hop.pop('detect_multiplier') + if passive_mode is False: + hop.pop('passive_mode') + if echo_interval == 300: + hop.pop('echo_interval') + if echo_mode is False: + hop.pop('echo_mode') + + if multi_hops: + for hop in multi_hops: + enabled = hop.get('enabled', None) + transmit_interval = hop.get('transmit_interval', None) + receive_interval = hop.get('receive_interval', None) + detect_multiplier = hop.get('detect_multiplier', None) + passive_mode = hop.get('passive_mode', None) + min_ttl = hop.get('min_ttl', None) + + if enabled: + hop.pop('enabled') + if transmit_interval == 300: + hop.pop('transmit_interval') + if receive_interval == 300: + hop.pop('receive_interval') + if detect_multiplier == 3: + hop.pop('detect_multiplier') + if passive_mode is False: + hop.pop('passive_mode') + if min_ttl == 254: + hop.pop('min_ttl') diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp/bgp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp/bgp.py index fd4d5c57e..69c7ca455 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp/bgp.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp/bgp.py @@ -13,18 +13,12 @@ created from __future__ import absolute_import, division, print_function __metaclass__ = type -try: - from urllib import quote -except ImportError: - from urllib.parse import quote - from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, to_list, - search_obj_in_list, - remove_empties ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( @@ -32,10 +26,8 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s edit_config ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( - dict_to_set, update_states, get_diff, - remove_empties_from_list ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import to_request from ansible.module_utils.connection import ConnectionError @@ -120,6 +112,11 @@ class Bgp(ConfigBase): to the desired configuration """ want = self._module.params['config'] + if want: + want = [remove_empties(conf) for conf in want] + else: + want = [] + have = existing_bgp_facts resp = self.set_state(want, have) return to_list(resp) @@ -137,19 +134,85 @@ class Bgp(ConfigBase): requests = [] state = self._module.params['state'] - diff = get_diff(want, have, TEST_KEYS) - if state == 'overridden': - commands, requests = self._state_overridden(want, have, diff) + commands, requests = self._state_overridden(want, have) elif state == 'deleted': - commands, requests = self._state_deleted(want, have, diff) + commands, requests = self._state_deleted(want, have) elif state == 'merged': - commands, requests = self._state_merged(want, have, diff) + commands, requests = self._state_merged(want, have) elif state == 'replaced': - commands, requests = self._state_replaced(want, have, diff) + commands, requests = self._state_replaced(want, have) return commands, requests - def _state_merged(self, want, have, diff): + def _state_replaced(self, want, have): + """ The command generator when state is replaced + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + + del_commands, del_requests = self.get_delete_commands_requests_for_replaced_overridden(want, have, 'replaced') + if del_commands: + commands = update_states(del_commands, 'deleted') + requests = del_requests + + add_commands = get_diff(want, have, TEST_KEYS) + if add_commands: + for command in add_commands: + as_val = command['bgp_as'] + vrf_name = command['vrf_name'] + + # max_med -> on_startup options are modified or deleted at once. + # Diff might not reflect the correct commands if only one of + # them is modified. So, update the command with want value. + if command.get('max_med'): + for cfg in want: + if cfg['vrf_name'] == vrf_name and cfg['bgp_as'] == as_val: + command['max_med'] = cfg['max_med'] + break + + commands.extend(update_states(add_commands, 'replaced')) + requests.extend(self.get_modify_bgp_requests(add_commands, have)) + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + + del_commands, del_requests = self.get_delete_commands_requests_for_replaced_overridden(want, have, 'overridden') + if del_commands: + commands = update_states(del_commands, 'deleted') + requests = del_requests + + add_commands = get_diff(want, have, TEST_KEYS) + if add_commands: + for command in add_commands: + as_val = command['bgp_as'] + vrf_name = command['vrf_name'] + + # max_med -> on_startup options are modified or deleted at once. + # Diff will not reflect the correct commands if only one of + # them is modified. So, update the command with want value. + if command.get('max_med'): + for cfg in want: + if cfg['vrf_name'] == vrf_name and cfg['bgp_as'] == as_val: + command['max_med'] = cfg['max_med'] + break + + commands.extend(update_states(add_commands, 'overridden')) + requests.extend(self.get_modify_bgp_requests(add_commands, have)) + + return commands, requests + + def _state_merged(self, want, have): """ The command generator when state is merged :param want: the additive configuration as a dictionary @@ -158,7 +221,7 @@ class Bgp(ConfigBase): :returns: the commands necessary to merge the provided into the current configuration """ - commands = diff + commands = get_diff(want, have, TEST_KEYS) requests = self.get_modify_bgp_requests(commands, have) if commands and len(requests) > 0: commands = update_states(commands, "merged") @@ -167,7 +230,7 @@ class Bgp(ConfigBase): return commands, requests - def _state_deleted(self, want, have, diff): + def _state_deleted(self, want, have): """ The command generator when state is deleted :param want: the objects from which the configuration should be removed @@ -269,6 +332,7 @@ class Bgp(ConfigBase): requests = [] router_id = command.get('router_id', None) + rt_delay = command.get('rt_delay', None) timers = command.get('timers', None) holdtime = None keepalive = None @@ -282,6 +346,10 @@ class Bgp(ConfigBase): url = '%s=%s/%s/global/config/router-id' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) requests.append({"path": url, "method": DELETE}) + if rt_delay and match.get('rt_delay', None): + url = '%s=%s/%s/global/config/route-map-process-delay' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) + requests.append({"path": url, "method": DELETE}) + if holdtime and match['timers'].get('holdtime', None) != 180: url = '%s=%s/%s/global/config/hold-time' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) requests.append({"path": url, "method": DELETE}) @@ -320,7 +388,7 @@ class Bgp(ConfigBase): if not match: continue # if there is specific parameters to delete then delete those alone - if cmd.get('router_id', None) or cmd.get('log_neighbor_changes', None) or cmd.get('bestpath', None): + if cmd.get('router_id', None) or cmd.get('log_neighbor_changes', None) or cmd.get('bestpath', None) or cmd.get('rt_delay', None): requests.extend(self.get_delete_specific_bgp_param_request(cmd, match)) else: # delete entire bgp @@ -471,7 +539,7 @@ class Bgp(ConfigBase): payload = {} if holdtime is not None: - payload['hold-time'] = str(holdtime) + payload['hold-time'] = holdtime if payload: url = '%s=%s/%s/global/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path, self.holdtime_path) @@ -485,7 +553,7 @@ class Bgp(ConfigBase): payload = {} if keepalive_interval is not None: - payload['keepalive-interval'] = str(keepalive_interval) + payload['keepalive-interval'] = keepalive_interval if payload: url = '%s=%s/%s/global/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path, self.keepalive_path) @@ -514,7 +582,7 @@ class Bgp(ConfigBase): return request - def get_modify_global_config_request(self, vrf_name, router_id, as_val): + def get_modify_global_config_request(self, vrf_name, router_id, as_val, rt_delay): request = None method = PATCH payload = {} @@ -524,6 +592,8 @@ class Bgp(ConfigBase): cfg['router-id'] = router_id if as_val: cfg['as'] = float(as_val) + if rt_delay: + cfg['route-map-process-delay'] = rt_delay if cfg: payload['openconfig-network-instance:config'] = cfg @@ -547,6 +617,7 @@ class Bgp(ConfigBase): max_med = None holdtime = None keepalive_interval = None + rt_delay = None if 'bgp_as' in conf: as_val = conf['bgp_as'] @@ -558,6 +629,8 @@ class Bgp(ConfigBase): bestpath = conf['bestpath'] if 'max_med' in conf: max_med = conf['max_med'] + if 'rt_delay' in conf: + rt_delay = conf['rt_delay'] if 'timers' in conf and conf['timers']: if 'holdtime' in conf['timers']: holdtime = conf['timers']['holdtime'] @@ -569,7 +642,7 @@ class Bgp(ConfigBase): if new_bgp_req: requests.append(new_bgp_req) - global_req = self.get_modify_global_config_request(vrf_name, router_id, as_val) + global_req = self.get_modify_global_config_request(vrf_name, router_id, as_val, rt_delay) if global_req: requests.append(global_req) @@ -596,3 +669,112 @@ class Bgp(ConfigBase): requests.extend(max_med_reqs) return requests + + def get_delete_commands_requests_for_replaced_overridden(self, want, have, state): + """Returns the commands and requests necessary to remove applicable + current configurations when state is replaced or overridden + """ + commands = [] + requests = [] + if not have: + return commands, requests + + for conf in have: + as_val = conf['bgp_as'] + vrf_name = conf['vrf_name'] + + match_cfg = next((cfg for cfg in want if cfg['vrf_name'] == vrf_name and cfg['bgp_as'] == as_val), None) + # Delete entire BGP if not specified in overridden + if not match_cfg: + if state == 'overridden': + commands.append(conf) + requests.append(self.get_delete_single_bgp_request(vrf_name)) + continue + + # Delete config in BGP AS that are replaced/overridden + # - Modified attributes are not deleted, since they will be + # updated by merge. + # - log_neighbor_changes is enabled by default, therefore + # it will be enabled if not specified and currently + # disabled for an existing BGP AS. + command = {} + + if conf.get('router_id') and not match_cfg.get('router_id'): + command['router_id'] = conf['router_id'] + + if conf.get('rt_delay') and match_cfg.get('rt_delay') is None: + command['rt_delay'] = conf['rt_delay'] + + if not conf.get('log_neighbor_changes') and match_cfg.get('log_neighbor_changes') is None: + command['log_neighbor_changes'] = False + requests.append(self.get_modify_log_change_request(vrf_name, True)) + + # max_med -> on_startup options are deleted at once. + # Update the commands appropriately. + if conf.get('max_med') and (not match_cfg.get('max_med') or conf['max_med']['on_startup'] != match_cfg['max_med']['on_startup']): + command['max_med'] = conf['max_med'] + + if conf.get('timers'): + timer_command = {} + timers = conf['timers'] + match_timers = match_cfg.get('timers', {}) + if timers.get('holdtime') is not None and match_timers.get('holdtime') is None and timers['holdtime'] != 180: + timer_command['holdtime'] = timers['holdtime'] + if timers.get('keepalive_interval') is not None and match_timers.get('keepalive_interval') is None and timers['keepalive_interval'] != 60: + timer_command['keepalive_interval'] = timers['keepalive_interval'] + + if timer_command: + command['timers'] = timer_command + + if conf.get('bestpath'): + bestpath_command = {} + bestpath = conf['bestpath'] + match_bestpath = match_cfg.get('bestpath', {}) + if bestpath.get('as_path'): + as_path_command = {} + as_path = bestpath['as_path'] + match_as_path = match_bestpath.get('as_path', {}) + for option in ('confed', 'ignore', 'multipath_relax', 'multipath_relax_as_set'): + if as_path.get(option) and match_as_path.get(option) is None: + as_path_command[option] = True + + if as_path_command: + bestpath_command['as_path'] = as_path_command + + if bestpath.get('compare_routerid') and match_bestpath.get('compare_routerid') is None: + bestpath_command['compare_routerid'] = True + + if bestpath.get('med'): + med_command = {} + med = bestpath['med'] + match_med = match_bestpath.get('med', {}) + for option in ('confed', 'missing_as_worst', 'always_compare_med'): + if med.get(option) and match_med.get(option) is None: + med_command[option] = True + + if med_command: + bestpath_command['med'] = med_command + + if bestpath_command: + command['bestpath'] = bestpath_command + + if command: + command['bgp_as'] = as_val + command['vrf_name'] = vrf_name + commands.append(command) + requests.extend(self.get_delete_specific_bgp_param_request(command, command)) + + if requests: + # reorder the requests to get default vrfs at end of the requests. so deletion will get success + default_vrf_reqs = [] + other_vrf_reqs = [] + for req in requests: + if '=default/' in req['path']: + default_vrf_reqs.append(req) + else: + other_vrf_reqs.append(req) + requests.clear() + requests.extend(other_vrf_reqs) + requests.extend(default_vrf_reqs) + + return commands, requests diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_af/bgp_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_af/bgp_af.py index 2a5c4cfca..05d74fff2 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_af/bgp_af.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_af/bgp_af.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2019 Red Hat +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -14,17 +14,16 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type try: - from urllib import quote + from urllib import quote_plus except ImportError: - from urllib.parse import quote + from urllib.parse import quote_plus from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( - ConfigBase, + ConfigBase ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, to_list, - search_obj_in_list, - remove_empties ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( @@ -32,14 +31,11 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s edit_config ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( - dict_to_set, update_states, - get_diff, - remove_empties_from_list, + get_diff ) -from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import to_request from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils import ( - validate_bgps, + validate_bgps ) from ansible.module_utils.connection import ConnectionError @@ -49,7 +45,8 @@ TEST_KEYS = [ {'config': {'vrf_name': '', 'bgp_as': ''}}, {'afis': {'afi': '', 'safi': ''}}, {'redistribute': {'protocol': ''}}, - {'route_advertise_list': {'advertise_afi': ''}} + {'route_advertise_list': {'advertise_afi': ''}}, + {'vnis': {'vni_number': ''}} ] @@ -71,9 +68,31 @@ class Bgp_af(ConfigBase): protocol_bgp_path = 'protocols/protocol=BGP,bgp/bgp' l2vpn_evpn_config_path = 'l2vpn-evpn/openconfig-bgp-evpn-ext:config' l2vpn_evpn_route_advertise_path = 'l2vpn-evpn/openconfig-bgp-evpn-ext:route-advertise' + l2vpn_evpn_vnis_path = 'l2vpn-evpn/openconfig-bgp-evpn-ext:vnis' afi_safi_path = 'global/afi-safis/afi-safi' table_connection_path = 'table-connections/table-connection' + advertise_attrs_map = { + 'advertise_pip': 'advertise-pip', + 'advertise_pip_ip': 'advertise-pip-ip', + 'advertise_pip_peer_ip': 'advertise-pip-peer-ip', + 'advertise_svi_ip': 'advertise-svi-ip', + 'advertise_default_gw': 'advertise-default-gw', + 'advertise_all_vni': 'advertise-all-vni', + 'rd': 'route-distinguisher', + 'rt_in': 'import-rts', + 'rt_out': 'export-rts' + } + non_list_advertise_attrs = ( + 'advertise_pip', + 'advertise_pip_ip', + 'advertise_pip_peer_ip', + 'advertise_svi_ip', + 'advertise_default_gw', + 'advertise_all_vni', + 'rd' + ) + def __init__(self, module): super(Bgp_af, self).__init__(module) @@ -125,7 +144,15 @@ class Bgp_af(ConfigBase): :returns: the commands necessary to migrate the current configuration to the desired configuration """ + state = self._module.params['state'] want = self._module.params['config'] + if want: + # In state deleted, specific empty parameters are supported + if state != 'deleted': + want = [remove_empties(conf) for conf in want] + else: + want = [] + have = existing_bgp_af_facts resp = self.set_state(want, have) return to_list(resp) @@ -143,19 +170,64 @@ class Bgp_af(ConfigBase): requests = [] state = self._module.params['state'] - diff = get_diff(want, have, TEST_KEYS) - if state == 'overridden': - commands, requests = self._state_overridden(want, have, diff) + commands, requests = self._state_overridden(want, have) elif state == 'deleted': - commands, requests = self._state_deleted(want, have, diff) + commands, requests = self._state_deleted(want, have) elif state == 'merged': - commands, requests = self._state_merged(want, have, diff) + commands, requests = self._state_merged(want, have) elif state == 'replaced': - commands, requests = self._state_replaced(want, have, diff) + commands, requests = self._state_replaced(want, have) + return commands, requests - def _state_merged(self, want, have, diff): + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + validate_bgps(self._module, want, have) + + del_commands, del_requests = self.get_delete_commands_requests_for_replaced_overridden(want, have, 'replaced') + if del_commands: + commands = update_states(del_commands, 'deleted') + requests = del_requests + + add_commands = get_diff(want, have, TEST_KEYS) + if add_commands: + commands.extend(update_states(add_commands, 'replaced')) + requests.extend(self.get_modify_bgp_af_requests(add_commands, have)) + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + validate_bgps(self._module, want, have) + + del_commands, del_requests = self.get_delete_commands_requests_for_replaced_overridden(want, have, 'overridden') + if del_commands: + commands = update_states(del_commands, 'deleted') + requests = del_requests + + add_commands = get_diff(want, have, TEST_KEYS) + if add_commands: + commands.extend(update_states(add_commands, 'overridden')) + requests.extend(self.get_modify_bgp_af_requests(add_commands, have)) + + return commands, requests + + def _state_merged(self, want, have): """ The command generator when state is merged :param want: the additive configuration as a dictionary @@ -164,7 +236,7 @@ class Bgp_af(ConfigBase): :returns: the commands necessary to merge the provided into the current configuration """ - commands = diff + commands = get_diff(want, have, TEST_KEYS) validate_bgps(self._module, commands, have) requests = self.get_modify_bgp_af_requests(commands, have) if commands and len(requests) > 0: @@ -173,7 +245,7 @@ class Bgp_af(ConfigBase): commands = [] return commands, requests - def _state_deleted(self, want, have, diff): + def _state_deleted(self, want, have): """ The command generator when state is deleted :param want: the objects from which the configuration should be removed @@ -191,7 +263,6 @@ class Bgp_af(ConfigBase): commands = want requests = self.get_delete_bgp_af_requests(commands, have, is_delete_all) - requests.extend(self.get_delete_route_advertise_requests(commands, have, is_delete_all)) if commands and len(requests) > 0: commands = update_states(commands, "deleted") @@ -208,7 +279,7 @@ class Bgp_af(ConfigBase): return ({"path": url, "method": PATCH, "data": pay_load}) - def get_modify_advertise_request(self, vrf_name, conf_afi, conf_safi, conf_addr_fam): + def get_modify_evpn_adv_cfg_request(self, vrf_name, conf_afi, conf_safi, conf_addr_fam): request = None conf_adv_pip = conf_addr_fam.get('advertise_pip', None) conf_adv_pip_ip = conf_addr_fam.get('advertise_pip_ip', None) @@ -216,26 +287,30 @@ class Bgp_af(ConfigBase): conf_adv_svi_ip = conf_addr_fam.get('advertise_svi_ip', None) conf_adv_all_vni = conf_addr_fam.get('advertise_all_vni', None) conf_adv_default_gw = conf_addr_fam.get('advertise_default_gw', None) + conf_rd = conf_addr_fam.get('rd', None) + conf_rt_in = conf_addr_fam.get('rt_in', []) + conf_rt_out = conf_addr_fam.get('rt_out', []) afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() evpn_cfg = {} - if conf_adv_pip: + if conf_adv_pip is not None: evpn_cfg['advertise-pip'] = conf_adv_pip - if conf_adv_pip_ip: evpn_cfg['advertise-pip-ip'] = conf_adv_pip_ip - if conf_adv_pip_peer_ip: evpn_cfg['advertise-pip-peer-ip'] = conf_adv_pip_peer_ip - - if conf_adv_svi_ip: + if conf_adv_svi_ip is not None: evpn_cfg['advertise-svi-ip'] = conf_adv_svi_ip - - if conf_adv_all_vni: + if conf_adv_all_vni is not None: evpn_cfg['advertise-all-vni'] = conf_adv_all_vni - - if conf_adv_default_gw: + if conf_adv_default_gw is not None: evpn_cfg['advertise-default-gw'] = conf_adv_default_gw + if conf_rd: + evpn_cfg['route-distinguisher'] = conf_rd + if conf_rt_in: + evpn_cfg['import-rts'] = conf_rt_in + if conf_rt_out: + evpn_cfg['export-rts'] = conf_rt_out if evpn_cfg: url = '%s=%s/%s/global' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) @@ -247,6 +322,52 @@ class Bgp_af(ConfigBase): return request + def get_modify_evpn_vnis_request(self, vrf_name, conf_afi, conf_safi, conf_addr_fam): + request = None + conf_vnis = conf_addr_fam.get('vnis', []) + afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() + vnis_dict = {} + vni_list = [] + + if conf_vnis: + for vni in conf_vnis: + vni_dict = {} + cfg = {} + vni_number = vni.get('vni_number', None) + adv_default_gw = vni.get('advertise_default_gw', None) + adv_svi_ip = vni.get('advertise_svi_ip', None) + rd = vni.get('rd', None) + rt_in = vni.get('rt_in', []) + rt_out = vni.get('rt_out', []) + + if vni_number: + cfg['vni-number'] = vni_number + if adv_default_gw is not None: + cfg['advertise-default-gw'] = adv_default_gw + if adv_svi_ip is not None: + cfg['advertise-svi-ip'] = adv_svi_ip + if rd: + cfg['route-distinguisher'] = rd + if rt_in: + cfg['import-rts'] = rt_in + if rt_out: + cfg['export-rts'] = rt_out + if cfg: + vni_dict['config'] = cfg + vni_dict['vni-number'] = vni_number + vni_list.append(vni_dict) + + if vni_list: + vnis_dict['vni'] = vni_list + url = '%s=%s/%s/global' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) + afi_safi_load = {'afi-safi-name': ("openconfig-bgp-types:%s" % (afi_safi))} + afi_safi_load['l2vpn-evpn'] = {'openconfig-bgp-evpn-ext:vnis': vnis_dict} + afi_safis_load = {'afi-safis': {'afi-safi': [afi_safi_load]}} + pay_load = {'openconfig-network-instance:global': afi_safis_load} + request = {"path": url, "method": PATCH, "data": pay_load} + + return request + def get_modify_route_advertise_list_request(self, vrf_name, conf_afi, conf_safi, conf_addr_fam): request = [] route_advertise = [] @@ -259,7 +380,7 @@ class Bgp_af(ConfigBase): if advertise_afi: advertise_afi_safi = '%s_UNICAST' % advertise_afi.upper() url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) - url += '/%s=%s/%s' % (self.afi_safi_path, afi_safi, self.l2vpn_evpn_route_advertise_path) + url += '/%s=%s/%s/route-advertise-list' % (self.afi_safi_path, afi_safi, self.l2vpn_evpn_route_advertise_path) cfg = None if route_map: route_map_list = [route_map] @@ -267,7 +388,7 @@ class Bgp_af(ConfigBase): else: cfg = {'advertise-afi-safi': advertise_afi_safi} route_advertise.append({'advertise-afi-safi': advertise_afi_safi, 'config': cfg}) - pay_load = {'openconfig-bgp-evpn-ext:route-advertise': {'route-advertise-list': route_advertise}} + pay_load = {'openconfig-bgp-evpn-ext:route-advertise-list': route_advertise} request = {"path": url, "method": PATCH, "data": pay_load} return request @@ -373,9 +494,15 @@ class Bgp_af(ConfigBase): if request: requests.append(request) elif conf_afi == "l2vpn" and conf_safi == 'evpn': - adv_req = self.get_modify_advertise_request(vrf_name, conf_afi, conf_safi, conf_addr_fam) - if adv_req: - requests.append(adv_req) + cfg_req = self.get_modify_evpn_adv_cfg_request(vrf_name, conf_afi, conf_safi, conf_addr_fam) + vni_req = self.get_modify_evpn_vnis_request(vrf_name, conf_afi, conf_safi, conf_addr_fam) + rt_adv_req = self.get_modify_route_advertise_list_request(vrf_name, conf_afi, conf_safi, conf_addr_fam) + if cfg_req: + requests.append(cfg_req) + if vni_req: + requests.append(vni_req) + if rt_adv_req: + requests.append(rt_adv_req) return requests def get_modify_all_af_requests(self, conf_addr_fams, vrf_name): @@ -418,16 +545,19 @@ class Bgp_af(ConfigBase): if conf_afi == 'ipv4' and conf_safi == 'unicast': conf_dampening = conf_addr_fam.get('dampening', None) - if conf_dampening: + if conf_dampening is not None: request = self.get_modify_dampening_request(vrf_name, conf_afi, conf_safi, conf_dampening) if request: requests.append(request) if conf_afi == "l2vpn" and conf_safi == "evpn": - adv_req = self.get_modify_advertise_request(vrf_name, conf_afi, conf_safi, conf_addr_fam) + cfg_req = self.get_modify_evpn_adv_cfg_request(vrf_name, conf_afi, conf_safi, conf_addr_fam) + vni_req = self.get_modify_evpn_vnis_request(vrf_name, conf_afi, conf_safi, conf_addr_fam) rt_adv_req = self.get_modify_route_advertise_list_request(vrf_name, conf_afi, conf_safi, conf_addr_fam) - if adv_req: - requests.append(adv_req) + if cfg_req: + requests.append(cfg_req) + if vni_req: + requests.append(vni_req) if rt_adv_req: requests.append(rt_adv_req) @@ -451,13 +581,14 @@ class Bgp_af(ConfigBase): have_redis_arr = mat_addr_fam.get('redistribute', []) have_redis = None have_route_map = None - # Check the route_map, if existing route_map is different from required route_map, delete the existing route map - if conf_route_map and have_redis_arr: + if have_redis_arr: have_redis = next((redis_cfg for redis_cfg in have_redis_arr if conf_redis['protocol'] == redis_cfg['protocol']), None) - if have_redis: - have_route_map = have_redis.get('route_map', None) - if have_route_map and have_route_map != conf_route_map: - requests.append(self.get_delete_route_map_request(vrf_name, conf_afi, have_redis, have_route_map)) + + # Check the route_map, if existing route_map is different from required route_map, delete the existing route map + if conf_route_map and have_redis: + have_route_map = have_redis.get('route_map', None) + if have_route_map and have_route_map != conf_route_map: + requests.append(self.get_delete_redistribute_route_map_request(vrf_name, conf_afi, have_redis, have_route_map)) modify_redis = {} if conf_metric is not None: @@ -465,7 +596,7 @@ class Bgp_af(ConfigBase): if conf_route_map: modify_redis['route_map'] = conf_route_map - if modify_redis: + if modify_redis or have_redis is None: modify_redis['protocol'] = conf_redis['protocol'] modify_redis_arr.append(modify_redis) @@ -531,50 +662,100 @@ class Bgp_af(ConfigBase): return ({'path': url, 'method': DELETE}) - def get_delete_route_advertise_requests(self, commands, have, is_delete_all): + def get_delete_all_vnis_request(self, vrf_name, conf_afi, conf_safi, conf_vnis): + requests = [] + for vni in conf_vnis: + requests.append(self.get_delete_vni_request(vrf_name, conf_afi, conf_safi, vni['vni_number'])) + + return requests + + def get_delete_vni_request(self, vrf_name, conf_afi, conf_safi, vni_number): + afi_safi = ('%s_%s' % (conf_afi, conf_safi)).upper() + url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) + url += '/%s=%s/%s/vni=%s' % (self.afi_safi_path, afi_safi, self.l2vpn_evpn_vnis_path, vni_number) + + return ({'path': url, 'method': DELETE}) + + def get_delete_vni_cfg_attr_request(self, vrf_name, conf_afi, conf_safi, vni_number, attr): + afi_safi = ('%s_%s' % (conf_afi, conf_safi)).upper() + url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) + url += '/%s=%s/%s/vni=%s' % (self.afi_safi_path, afi_safi, self.l2vpn_evpn_vnis_path, vni_number) + url += '/config/%s' % attr + + return ({'path': url, 'method': DELETE}) + + def get_delete_rt(self, conf_rt, mat_rt): + del_rt_list = [] + for rt in conf_rt: + if mat_rt and rt in mat_rt: + del_rt_list.append(rt) + encoded_del_rt_list = quote_plus(','.join(del_rt_list)) + + return encoded_del_rt_list + + def get_delete_route_advertise_requests(self, vrf_name, conf_afi, conf_safi, conf_route_adv_list, is_delete_all, mat_route_adv_list): requests = [] - if not is_delete_all: - for cmd in commands: - vrf_name = cmd['vrf_name'] - addr_fams = cmd.get('address_family', None) - if addr_fams: - addr_fams = addr_fams.get('afis', []) - if not addr_fams: - return requests - for addr_fam in addr_fams: - afi = addr_fam.get('afi', None) - safi = addr_fam.get('safi', None) - route_advertise_list = addr_fam.get('route_advertise_list', []) - if route_advertise_list: - for rt_adv in route_advertise_list: - advertise_afi = rt_adv.get('advertise_afi', None) - route_map = rt_adv.get('route_map', None) - # Check if the commands to be deleted are configured - for conf in have: - conf_vrf_name = conf['vrf_name'] - conf_addr_fams = conf.get('address_family', None) - if conf_addr_fams: - conf_addr_fams = conf_addr_fams.get('afis', []) - for conf_addr_fam in conf_addr_fams: - conf_afi = conf_addr_fam.get('afi', None) - conf_safi = conf_addr_fam.get('safi', None) - conf_route_advertise_list = conf_addr_fam.get('route_advertise_list', []) - if conf_route_advertise_list: - for conf_rt_adv in conf_route_advertise_list: - conf_advertise_afi = conf_rt_adv.get('advertise_afi', None) - conf_route_map = conf_rt_adv.get('route_map', None) - # Deletion at route-advertise level - if (not advertise_afi and vrf_name == conf_vrf_name and afi == conf_afi and safi == conf_safi): - requests.append(self.get_delete_route_advertise_request(vrf_name, afi, safi)) - # Deletion at advertise-afi-safi level - if (advertise_afi and not route_map and vrf_name == conf_vrf_name and afi == conf_afi and safi == - conf_safi and advertise_afi == conf_advertise_afi): - requests.append(self.get_delete_route_advertise_list_request(vrf_name, afi, safi, advertise_afi)) - # Deletion at route-map level - if (route_map and vrf_name == conf_vrf_name and afi == conf_afi and safi == conf_safi - and advertise_afi == conf_advertise_afi and route_map == conf_route_map): - requests.append(self.get_delete_route_advertise_route_map_request(vrf_name, afi, safi, - advertise_afi, route_map)) + if is_delete_all: + requests.append(self.get_delete_route_advertise_request(vrf_name, conf_afi, conf_safi)) + else: + for conf_rt_adv in conf_route_adv_list: + conf_advertise_afi = conf_rt_adv.get('advertise_afi', None) + conf_route_map = conf_rt_adv.get('route_map', None) + # Check if the commands to be deleted are configured + for mat_rt_adv in mat_route_adv_list: + mat_advertise_afi = mat_rt_adv.get('advertise_afi', None) + mat_route_map = mat_rt_adv.get('route_map', None) + # Deletion at advertise-afi-safi level + if (not conf_route_map and conf_advertise_afi == mat_advertise_afi): + requests.append(self.get_delete_route_advertise_list_request(vrf_name, conf_afi, conf_safi, conf_advertise_afi)) + # Deletion at route-map level + if (conf_route_map and conf_advertise_afi == mat_advertise_afi and conf_route_map == mat_route_map): + requests.append(self.get_delete_route_advertise_route_map_request(vrf_name, conf_afi, conf_safi, conf_advertise_afi, conf_route_map)) + + return requests + + def get_delete_vnis_requests(self, vrf_name, conf_afi, conf_safi, conf_vnis, is_delete_all, mat_vnis): + requests = [] + if is_delete_all: + requests.extend(self.get_delete_all_vnis_request(vrf_name, conf_afi, conf_safi, conf_vnis)) + else: + for conf_vni in conf_vnis: + conf_vni_number = conf_vni.get('vni_number', None) + conf_adv_default_gw = conf_vni.get('advertise_default_gw', None) + conf_adv_svi_ip = conf_vni.get('advertise_svi_ip', None) + conf_rd = conf_vni.get('rd', None) + conf_rt_in = conf_vni.get('rt_in', None) + conf_rt_out = conf_vni.get('rt_out', None) + # Check if the commands to be deleted are configured + for mat_vni in mat_vnis: + mat_vni_number = mat_vni.get('vni_number', None) + mat_adv_default_gw = mat_vni.get('advertise_default_gw', None) + mat_adv_svi_ip = mat_vni.get('advertise_svi_ip', None) + mat_rd = mat_vni.get('rd', None) + mat_rt_in = mat_vni.get('rt_in', None) + mat_rt_out = mat_vni.get('rt_out', None) + # Deletion at vni-number level + if (conf_vni_number and conf_vni_number == mat_vni_number and not conf_adv_default_gw and not conf_adv_svi_ip and not conf_rd and not + conf_rt_in and not conf_rt_out): + requests.append(self.get_delete_vni_request(vrf_name, conf_afi, conf_safi, conf_vni_number)) + # Deletion at config/attribute level + if conf_vni_number == mat_vni_number: + if conf_adv_default_gw is not None and conf_adv_default_gw == mat_adv_default_gw: + requests.append(self.get_delete_vni_cfg_attr_request(vrf_name, conf_afi, conf_safi, conf_vni_number, 'advertise-default-gw')) + if conf_adv_svi_ip is not None and conf_adv_svi_ip == mat_adv_svi_ip: + requests.append(self.get_delete_vni_cfg_attr_request(vrf_name, conf_afi, conf_safi, conf_vni_number, 'advertise-svi-ip')) + if conf_rd and conf_rd == mat_rd: + requests.append(self.get_delete_vni_cfg_attr_request(vrf_name, conf_afi, conf_safi, conf_vni_number, 'route-distinguisher')) + if conf_rt_in: + del_rt_list = self.get_delete_rt(conf_rt_in, mat_rt_in) + if del_rt_list: + requests.append(self.get_delete_vni_cfg_attr_request(vrf_name, conf_afi, conf_safi, conf_vni_number, 'import-rts=%s' % + del_rt_list)) + if conf_rt_out: + del_rt_list = self.get_delete_rt(conf_rt_out, mat_rt_out) + if del_rt_list: + requests.append(self.get_delete_vni_cfg_attr_request(vrf_name, conf_afi, conf_safi, conf_vni_number, 'export-rts=%s' % + del_rt_list)) return requests @@ -588,11 +769,10 @@ class Bgp_af(ConfigBase): def get_delete_address_family_request(self, vrf_name, conf_afi, conf_safi): request = None - if conf_afi != "l2vpn": - afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() - url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) - url += '/%s=openconfig-bgp-types:%s' % (self.afi_safi_path, afi_safi) - request = {"path": url, "method": DELETE} + afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() + url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) + url += '/%s=openconfig-bgp-types:%s' % (self.afi_safi_path, afi_safi) + request = {"path": url, "method": DELETE} return request @@ -630,27 +810,42 @@ class Bgp_af(ConfigBase): conf_max_path = conf_addr_fam.get('max_path', None) conf_dampening = conf_addr_fam.get('dampening', None) conf_network = conf_addr_fam.get('network', []) + conf_route_adv_list = conf_addr_fam.get('route_advertise_list', []) + conf_rd = conf_addr_fam.get('rd', None) + conf_rt_in = conf_addr_fam.get('rt_in', []) + conf_rt_out = conf_addr_fam.get('rt_out', []) + conf_vnis = conf_addr_fam.get('vnis', []) if is_delete_all: - if conf_adv_pip: - requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip')) if conf_adv_pip_ip: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip-ip')) if conf_adv_pip_peer_ip: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip-peer-ip')) - if conf_adv_svi_ip: + if conf_adv_pip is not None: + requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip')) + if conf_adv_svi_ip is not None: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-svi-ip')) - if conf_adv_all_vni: + if conf_adv_all_vni is not None: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-all-vni')) if conf_dampening: requests.append(self.get_delete_dampening_request(vrf_name, conf_afi, conf_safi)) if conf_network: requests.extend(self.get_delete_network_request(vrf_name, conf_afi, conf_safi, conf_network, is_delete_all, None)) - if conf_adv_default_gw: + if conf_adv_default_gw is not None: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-default-gw')) + if conf_route_adv_list: + requests.extend(self.get_delete_route_advertise_requests(vrf_name, conf_afi, conf_safi, conf_route_adv_list, is_delete_all, None)) + if conf_rd: + requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'route-distinguisher')) + if conf_rt_in: + requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'import-rts')) + if conf_rt_out: + requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'export-rts')) if conf_redis_arr: requests.extend(self.get_delete_redistribute_requests(vrf_name, conf_afi, conf_safi, conf_redis_arr, is_delete_all, None)) if conf_max_path: requests.extend(self.get_delete_max_path_requests(vrf_name, conf_afi, conf_safi, conf_max_path, is_delete_all, None)) + if conf_vnis: + requests.extend(self.get_delete_vnis_requests(vrf_name, conf_afi, conf_safi, conf_vnis, is_delete_all, None)) addr_family_del_req = self.get_delete_address_family_request(vrf_name, conf_afi, conf_safi) if addr_family_del_req: requests.append(addr_family_del_req) @@ -674,54 +869,87 @@ class Bgp_af(ConfigBase): mat_max_path = match_addr_fam.get('max_path', None) mat_dampening = match_addr_fam.get('dampening', None) mat_network = match_addr_fam.get('network', []) - - if (conf_adv_pip is None and conf_adv_pip_ip is None and conf_adv_pip_peer_ip is None and conf_adv_svi_ip is None - and conf_adv_all_vni is None and not conf_redis_arr and conf_adv_default_gw is None - and not conf_max_path and conf_dampening is None and not conf_network): - if mat_advt_pip: - requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip')) + mat_route_adv_list = match_addr_fam.get('route_advertise_list', None) + mat_rd = match_addr_fam.get('rd', None) + mat_rt_in = match_addr_fam.get('rt_in', []) + mat_rt_out = match_addr_fam.get('rt_out', []) + mat_vnis = match_addr_fam.get('vnis', []) + + if (conf_adv_pip is None and not conf_adv_pip_ip and not conf_adv_pip_peer_ip and conf_adv_svi_ip is None + and conf_adv_all_vni is None and not conf_redis_arr and conf_adv_default_gw is None and not conf_max_path and conf_dampening is + None and not conf_network and not conf_route_adv_list and not conf_rd and not conf_rt_in and not conf_rt_out and not conf_vnis): if mat_advt_pip_ip: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip-ip')) if mat_advt_pip_peer_ip: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip-peer-ip')) - if mat_advt_svi_ip: + if mat_advt_pip is not None: + requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip')) + if mat_advt_svi_ip is not None: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-svi-ip')) if mat_advt_all_vni is not None: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-all-vni')) - if mat_dampening is not None: + if mat_dampening: requests.append(self.get_delete_dampening_request(vrf_name, conf_afi, conf_safi)) - if mat_advt_defaut_gw: + if mat_advt_defaut_gw is not None: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-default-gw')) + if mat_route_adv_list: + requests.extend(self.get_delete_route_advertise_requests(vrf_name, conf_afi, conf_safi, mat_route_adv_list, is_delete_all, + mat_route_adv_list)) + if mat_rd: + requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'route-distinguisher')) + if mat_rt_in: + requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'import-rts')) + if mat_rt_out: + requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'export-rts')) if mat_redis_arr: requests.extend(self.get_delete_redistribute_requests(vrf_name, conf_afi, conf_safi, mat_redis_arr, False, mat_redis_arr)) if mat_max_path: requests.extend(self.get_delete_max_path_requests(vrf_name, conf_afi, conf_safi, mat_max_path, is_delete_all, mat_max_path)) if mat_network: requests.extend(self.get_delete_network_request(vrf_name, conf_afi, conf_safi, mat_network, False, mat_network)) + if mat_vnis: + requests.extend(self.get_delete_vnis_requests(vrf_name, conf_afi, conf_safi, mat_vnis, is_delete_all, mat_vnis)) addr_family_del_req = self.get_delete_address_family_request(vrf_name, conf_afi, conf_safi) if addr_family_del_req: requests.append(addr_family_del_req) else: - if conf_adv_pip and mat_advt_pip: - requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip')) - if conf_adv_pip_ip and mat_advt_pip_ip: + if conf_adv_pip_ip and conf_adv_pip_ip == mat_advt_pip_ip: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip-ip')) - if conf_adv_pip_peer_ip and mat_advt_pip_peer_ip: + if conf_adv_pip_peer_ip and conf_adv_pip_peer_ip == mat_advt_pip_peer_ip: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip-peer-ip')) - if conf_adv_svi_ip and mat_advt_svi_ip: + if conf_adv_pip is not None and conf_adv_pip == mat_advt_pip: + requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip')) + if conf_adv_svi_ip is not None and conf_adv_svi_ip == mat_advt_svi_ip: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-svi-ip')) - if conf_adv_all_vni and mat_advt_all_vni: + if conf_adv_all_vni is not None and conf_adv_all_vni == mat_advt_all_vni: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-all-vni')) - if conf_dampening and mat_dampening: + if conf_dampening and conf_dampening == mat_dampening: requests.append(self.get_delete_dampening_request(vrf_name, conf_afi, conf_safi)) - if conf_adv_default_gw and mat_advt_defaut_gw: + if conf_adv_default_gw is not None and conf_adv_default_gw == mat_advt_defaut_gw: requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-default-gw')) + if conf_route_adv_list and mat_route_adv_list: + requests.extend(self.get_delete_route_advertise_requests(vrf_name, conf_afi, conf_safi, conf_route_adv_list, is_delete_all, + mat_route_adv_list)) + if conf_rd and conf_rd == mat_rd: + requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'route-distinguisher')) + if conf_rt_in: + del_rt_list = self.get_delete_rt(conf_rt_in, mat_rt_in) + if del_rt_list: + requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'import-rts=%s' % + del_rt_list)) + if conf_rt_out: + del_rt_list = self.get_delete_rt(conf_rt_out, mat_rt_out) + if del_rt_list: + requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'export-rts=%s' % + del_rt_list)) if conf_redis_arr and mat_redis_arr: requests.extend(self.get_delete_redistribute_requests(vrf_name, conf_afi, conf_safi, conf_redis_arr, False, mat_redis_arr)) if conf_max_path and mat_max_path: requests.extend(self.get_delete_max_path_requests(vrf_name, conf_afi, conf_safi, conf_max_path, is_delete_all, mat_max_path)) if conf_network and mat_network: requests.extend(self.get_delete_network_request(vrf_name, conf_afi, conf_safi, conf_network, False, mat_network)) + if conf_vnis and mat_vnis: + requests.extend(self.get_delete_vnis_requests(vrf_name, conf_afi, conf_safi, conf_vnis, is_delete_all, mat_vnis)) break return requests @@ -761,14 +989,14 @@ class Bgp_af(ConfigBase): mat_ebgp = mat_max_path.get('ebgp', None) mat_ibgp = mat_max_path.get('ibgp', None) - if (conf_ebgp and mat_ebgp) or is_delete_all: - requests.append({'path': url + 'ebgp', 'method': DELETE}) - if (conf_ibgp and mat_ibgp) or is_delete_all: - requests.append({'path': url + 'ibgp', 'method': DELETE}) + if (conf_ebgp and mat_ebgp and mat_ebgp != 1) or (is_delete_all and conf_ebgp != 1): + requests.append({'path': url + 'ebgp/config/maximum-paths', 'method': DELETE}) + if (conf_ibgp and mat_ibgp and mat_ibgp != 1) or (is_delete_all and conf_ibgp != 1): + requests.append({'path': url + 'ibgp/config/maximum-paths', 'method': DELETE}) return requests - def get_delete_route_map_request(self, vrf_name, conf_afi, conf_redis, conf_route_map): + def get_delete_redistribute_route_map_request(self, vrf_name, conf_afi, conf_redis, conf_route_map): addr_family = "openconfig-types:%s" % (conf_afi.upper()) conf_protocol = conf_redis['protocol'].upper() if conf_protocol == 'CONNECTED': @@ -779,6 +1007,17 @@ class Bgp_af(ConfigBase): url += '%s,%s,%s/config/import-policy=%s' % (src_protocol, dst_protocol, addr_family, conf_route_map) return ({'path': url, 'method': DELETE}) + def get_delete_redistribute_metric_request(self, vrf_name, conf_afi, conf_redis): + addr_family = "openconfig-types:%s" % (conf_afi.upper()) + conf_protocol = conf_redis['protocol'].upper() + if conf_protocol == 'CONNECTED': + conf_protocol = "DIRECTLY_CONNECTED" + src_protocol = "openconfig-policy-types:%s" % (conf_protocol) + dst_protocol = "openconfig-policy-types:BGP" + url = '%s=%s/%s=' % (self.network_instance_path, vrf_name, self.table_connection_path) + url += '%s,%s,%s/config/metric' % (src_protocol, dst_protocol, addr_family) + return {'path': url, 'method': DELETE} + def get_delete_redistribute_requests(self, vrf_name, conf_afi, conf_safi, conf_redis_arr, is_delete_all, mat_redis_arr): requests = [] for conf_redis in conf_redis_arr: @@ -846,3 +1085,190 @@ class Bgp_af(ConfigBase): match_cfg = next((have_cfg for have_cfg in have if have_cfg['vrf_name'] == vrf_name and have_cfg['bgp_as'] == as_val), None) requests.extend(self.get_delete_single_bgp_af_request(cmd, is_delete_all, match_cfg)) return requests + + def get_delete_commands_requests_for_replaced_overridden(self, want, have, state): + """Returns the commands and requests necessary to remove applicable + current configurations when state is replaced or overridden + """ + commands = [] + requests = [] + if not have: + return commands, requests + + for conf in have: + as_val = conf['bgp_as'] + vrf_name = conf['vrf_name'] + if conf.get('address_family') and conf['address_family'].get('afis'): + afi_list = conf['address_family']['afis'] + else: + continue + + match_cfg = next((cfg for cfg in want if cfg['vrf_name'] == vrf_name and cfg['bgp_as'] == as_val), None) + if not match_cfg: + # Delete all address-families in BGPs that are not + # specified in overridden + if state == 'overridden': + commands.append(conf) + requests.extend(self.get_delete_single_bgp_af_request(conf, True)) + continue + + match_afi_list = [] + if match_cfg.get('address_family') and match_cfg['address_family'].get('afis'): + match_afi_list = match_cfg['address_family']['afis'] + + # Delete AF configs in BGPs that are replaced/overridden + afi_command_list = [] + for afi_conf in afi_list: + afi_command = {} + afi = afi_conf['afi'] + safi = afi_conf['safi'] + + match_afi_cfg = next((afi_cfg for afi_cfg in match_afi_list if afi_cfg['afi'] == afi and afi_cfg['safi'] == safi), None) + # Delete address-families that are not specified + if not match_afi_cfg: + afi_command_list.append(afi_conf) + requests.extend(self.get_delete_single_bgp_af_request({'bgp_as': as_val, 'vrf_name': vrf_name, 'address_family': {'afis': [afi_conf]}}, + True)) + continue + + if afi == 'ipv4' and safi == 'unicast': + if afi_conf.get('dampening') and match_afi_cfg.get('dampening') is None: + afi_command['dampening'] = afi_conf['dampening'] + requests.append(self.get_delete_dampening_request(vrf_name, afi, safi)) + + if afi == 'l2vpn' and safi == 'evpn': + for option in self.non_list_advertise_attrs: + if afi_conf.get(option) is not None and match_afi_cfg.get(option) is None: + afi_command[option] = afi_conf[option] + requests.append(self.get_delete_advertise_attribute_request(vrf_name, afi, safi, self.advertise_attrs_map[option])) + + for option in ('rt_in', 'rt_out'): + if afi_conf.get(option): + del_rt = self._get_diff_list(afi_conf[option], match_afi_cfg.get(option, [])) + if del_rt: + afi_command[option] = del_rt + requests.append(self.get_delete_advertise_attribute_request(vrf_name, afi, safi, + '{0}={1}'.format(self.advertise_attrs_map[option], + quote_plus(','.join(del_rt))))) + + if afi_conf.get('route_advertise_list'): + route_adv_list = [] + match_route_adv_list = match_afi_cfg.get('route_advertise_list', []) + for route_adv in afi_conf['route_advertise_list']: + advertise_afi = route_adv['advertise_afi'] + route_map = route_adv.get('route_map') + match_route_adv = next((adv_cfg for adv_cfg in match_route_adv_list if adv_cfg['advertise_afi'] == advertise_afi), None) + if not match_route_adv: + route_adv_list.append(route_adv) + requests.append(self.get_delete_route_advertise_list_request(vrf_name, afi, safi, advertise_afi)) + # Delete existing route-map before configuring + # new route-map. + elif route_map and route_map != match_route_adv.get('route_map'): + route_adv_list.append(route_adv) + requests.append(self.get_delete_route_advertise_route_map_request(vrf_name, afi, safi, advertise_afi, route_map)) + + if route_adv_list: + afi_command['route_advertise_list'] = route_adv_list + + if afi_conf.get('vnis'): + vni_command_list = [] + match_vni_list = match_afi_cfg.get('vnis', []) + for vni_conf in afi_conf['vnis']: + vni_number = vni_conf['vni_number'] + match_vni = next((vni_cfg for vni_cfg in match_vni_list if vni_cfg['vni_number'] == vni_number), None) + # Delete entire VNIs that are not specified + if not match_vni: + vni_command_list.append(vni_conf) + requests.append(self.get_delete_vni_request(vrf_name, afi, safi, vni_number)) + else: + vni_command = {} + for option in ('advertise_default_gw', 'advertise_svi_ip', 'rd'): + if vni_conf.get(option) is not None and match_vni.get(option) is None: + vni_command[option] = vni_conf[option] + requests.append(self.get_delete_vni_cfg_attr_request(vrf_name, afi, safi, vni_number, + self.advertise_attrs_map[option])) + + for option in ('rt_in', 'rt_out'): + if vni_conf.get(option): + del_rt = self._get_diff_list(vni_conf[option], match_vni.get(option, [])) + if del_rt: + vni_command[option] = del_rt + requests.append(self.get_delete_vni_cfg_attr_request(vrf_name, afi, safi, vni_number, + '{0}={1}'.format(self.advertise_attrs_map[option], + quote_plus(','.join(del_rt))))) + + if vni_command: + vni_command['vni_number'] = vni_number + vni_command_list.append(vni_command) + + if vni_command_list: + afi_command['vnis'] = vni_command_list + + elif afi in ['ipv4', 'ipv6'] and safi == 'unicast': + if afi_conf.get('network'): + del_network = self._get_diff_list(afi_conf['network'], match_afi_cfg.get('network', [])) + if del_network: + afi_command['network'] = del_network + requests.extend(self.get_delete_network_request(vrf_name, afi, safi, del_network, True, None)) + + if afi_conf.get('redistribute'): + match_redis_list = match_afi_cfg.get('redistribute') + if not match_redis_list: + afi_command['redistribute'] = afi_conf['redistribute'] + requests.extend(self.get_delete_redistribute_requests(vrf_name, afi, safi, afi_conf['redistribute'], True, None)) + else: + redis_command_list = [] + for redis_conf in afi_conf['redistribute']: + protocol = redis_conf['protocol'] + match_redis = next((redis_cfg for redis_cfg in match_redis_list if redis_cfg['protocol'] == protocol), None) + # Delete complete protocol redistribute + # configuration if not specified + if not match_redis: + redis_command_list.append(redis_conf) + requests.extend(self.get_delete_redistribute_requests(vrf_name, afi, safi, [redis_conf], True, None)) + # Delete metric, route_map for specified + # protocol if they are not specified. + else: + redis_command = {} + if redis_conf.get('metric') is not None and match_redis.get('metric') is None: + redis_command['metric'] = redis_conf['metric'] + requests.append(self.get_delete_redistribute_metric_request(vrf_name, afi, redis_conf)) + if redis_conf.get('route_map') is not None and match_redis.get('route_map') is None: + redis_command['route_map'] = redis_conf['route_map'] + requests.append(self.get_delete_redistribute_route_map_request(vrf_name, afi, redis_conf, redis_command['route_map'])) + + if redis_command: + redis_command['protocol'] = protocol + redis_command_list.append(redis_command) + + if redis_command_list: + afi_command['redistribute'] = redis_command_list + + if afi_conf.get('max_path'): + max_path_command = {} + match_max_path = match_afi_cfg.get('max_path', {}) + if afi_conf['max_path'].get('ibgp') and afi_conf['max_path']['ibgp'] != 1 and match_max_path.get('ibgp') is None: + max_path_command['ibgp'] = afi_conf['max_path']['ibgp'] + if afi_conf['max_path'].get('ebgp') and afi_conf['max_path']['ebgp'] != 1 and match_max_path.get('ebgp') is None: + max_path_command['ebgp'] = afi_conf['max_path']['ebgp'] + + if max_path_command: + afi_command['max_path'] = max_path_command + requests.extend(self.get_delete_max_path_requests(vrf_name, afi, safi, afi_command['max_path'], False, afi_command['max_path'])) + + if afi_command: + afi_command['afi'] = afi + afi_command['safi'] = safi + afi_command_list.append(afi_command) + + if afi_command_list: + commands.append({'bgp_as': as_val, 'vrf_name': vrf_name, 'address_family': {'afis': afi_command_list}}) + + return commands, requests + + @staticmethod + def _get_diff_list(base_list, compare_with_list): + if not compare_with_list: + return base_list + + return [item for item in base_list if item not in compare_with_list] diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_as_paths/bgp_as_paths.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_as_paths/bgp_as_paths.py index dc2b023b1..d57cf36e2 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_as_paths/bgp_as_paths.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_as_paths/bgp_as_paths.py @@ -17,13 +17,13 @@ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.c ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( to_list, + search_obj_in_list ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( update_states, get_diff, ) -from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import to_request from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( to_request, edit_config @@ -120,50 +120,142 @@ class Bgp_as_paths(ConfigBase): commands = [] requests = [] state = self._module.params['state'] - for i in want: - if i.get('members'): - temp = [] - for j in i['members']: - temp.append(j.replace('\\\\', '\\')) - i['members'] = temp - diff = get_diff(want, have) - for i in want: - if i.get('members'): - temp = [] - for j in i['members']: - temp.append(j.replace('\\', '\\\\')) - i['members'] = temp if state == 'overridden': - commands, requests = self._state_overridden(want, have, diff) + commands, requests = self._state_overridden(want, have) elif state == 'deleted': - commands, requests = self._state_deleted(want, have, diff) + commands, requests = self._state_deleted(want, have) elif state == 'merged': + diff = get_diff(want, have) commands, requests = self._state_merged(want, have, diff) elif state == 'replaced': - commands, requests = self._state_replaced(want, have, diff) + commands, requests = self._state_replaced(want, have) return commands, requests - @staticmethod - def _state_replaced(**kwargs): + def _state_replaced(self, want, have): """ The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ + add_commands = [] + del_commands = [] commands = [] - return commands + requests = [] + + for cmd in want: + # Set action to deny if not specfied for as-path-list + if cmd.get('permit') is None: + cmd['permit'] = False + + match = search_obj_in_list(cmd['name'], have, 'name') + # Replace existing as-path-list + if match: + # Delete entire as-path-list if no members are specified + if not cmd.get('members'): + del_commands.append(match) + requests.append(self.get_delete_single_as_path_request(cmd['name'])) + else: + if cmd['permit'] != match['permit']: + # If action is changed, delete the entire as-path list + # and add the given configuration + del_commands.append(match) + requests.append(self.get_delete_single_as_path_request(cmd['name'])) + add_commands.append(cmd) + requests.append(self.get_new_add_request(cmd)) + else: + want_members_set = set(cmd['members']) + have_members_set = set(match['members']) + members_to_delete = list(have_members_set.difference(want_members_set)) + members_to_add = list(want_members_set.difference(have_members_set)) + if members_to_delete: + del_commands.append({'name': cmd['name'], 'permit': cmd['permit'], 'members': members_to_delete}) + if len(members_to_delete) == len(match['members']): + requests.append(self.get_delete_single_as_path_request(cmd['name'])) + else: + requests.append(self.get_delete_single_as_path_member_request(cmd['name'], members_to_delete)) + + if members_to_add: + add_commands.append({'name': cmd['name'], 'permit': cmd['permit'], 'members': members_to_add}) + requests.append(self.get_new_add_request({'name': cmd['name'], 'permit': cmd['permit'], 'members': members_to_add})) + else: + if cmd.get('members'): + add_commands.append(cmd) + requests.append(self.get_new_add_request(cmd)) + + if del_commands: + commands = update_states(del_commands, 'deleted') + + if add_commands: + commands.extend(update_states(add_commands, 'replaced')) + + return commands, requests - @staticmethod - def _state_overridden(**kwargs): + def _state_overridden(self, want, have): """ The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ + add_commands = [] + del_commands = [] commands = [] - return commands + requests = [] + + # Delete as-path-lists that are not specified + for cfg in have: + if not search_obj_in_list(cfg['name'], want, 'name'): + del_commands.append(cfg) + requests.append(self.get_delete_single_as_path_request(cfg['name'])) + + for cmd in want: + # Set action to deny if not specfied for as-path-list + if cmd.get('permit') is None: + cmd['permit'] = False + + match = search_obj_in_list(cmd['name'], have, 'name') + # Override existing as-path-list + if match: + # Delete entire as-path-list if no members are specified + if not cmd.get('members'): + del_commands.append(match) + requests.append(self.get_delete_single_as_path_request(cmd['name'])) + else: + if cmd['permit'] != match['permit']: + # If action is changed, delete the entire as-path list + # and add the given configuration + del_commands.append(match) + requests.append(self.get_delete_single_as_path_request(cmd['name'])) + add_commands.append(cmd) + requests.append(self.get_new_add_request(cmd)) + else: + want_members_set = set(cmd['members']) + have_members_set = set(match['members']) + members_to_delete = list(have_members_set.difference(want_members_set)) + members_to_add = list(want_members_set.difference(have_members_set)) + if members_to_delete: + del_commands.append({'name': cmd['name'], 'permit': cmd['permit'], 'members': members_to_delete}) + if len(members_to_delete) == len(match['members']): + requests.append(self.get_delete_single_as_path_request(cmd['name'])) + else: + requests.append(self.get_delete_single_as_path_member_request(cmd['name'], members_to_delete)) + + if members_to_add: + add_commands.append({'name': cmd['name'], 'permit': cmd['permit'], 'members': members_to_add}) + requests.append(self.get_new_add_request({'name': cmd['name'], 'permit': cmd['permit'], 'members': members_to_add})) + else: + if cmd.get('members'): + add_commands.append(cmd) + requests.append(self.get_new_add_request(cmd)) + + if del_commands: + commands = update_states(del_commands, 'deleted') + + if add_commands: + commands.extend(update_states(add_commands, 'overridden')) + + return commands, requests def _state_merged(self, want, have, diff): """ The command generator when state is merged @@ -173,6 +265,19 @@ class Bgp_as_paths(ConfigBase): the current configuration """ commands = diff + for cmd in commands: + match = next((item for item in have if item['name'] == cmd['name']), None) + if match: + # Use existing action if not specified + if cmd.get('permit') is None: + cmd['permit'] = match['permit'] + elif cmd['permit'] != match['permit']: + action = 'permit' if match['permit'] else 'deny' + self._module.fail_json(msg='Cannot override existing action {0} of {1}'.format(action, cmd['name'])) + # Set action to deny if not specfied for a new as-path-list + elif cmd.get('permit') is None: + cmd['permit'] = False + requests = self.get_modify_as_path_list_requests(commands, have) if commands and len(requests) > 0: commands = update_states(commands, "merged") @@ -181,7 +286,7 @@ class Bgp_as_paths(ConfigBase): return commands, requests - def _state_deleted(self, want, have, diff): + def _state_deleted(self, want, have): """ The command generator when state is deleted :rtype: A list @@ -239,7 +344,7 @@ class Bgp_as_paths(ConfigBase): requests.append(request) return requests - def get_delete_single_as_path_member_requests(self, name, members): + def get_delete_single_as_path_member_request(self, name, members): url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:" url = url + "bgp-defined-sets/as-path-sets/as-path-set={name}/config/{members_param}" method = "DELETE" @@ -248,15 +353,8 @@ class Bgp_as_paths(ConfigBase): request = {"path": url.format(name=name, members_param=members_str), "method": method} return request - def get_delete_single_as_path_requests(self, name): - url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set={}" - method = "DELETE" - request = {"path": url.format(name), "method": method} - return request - - def get_delete_single_as_path_action_requests(self, name): + def get_delete_single_as_path_request(self, name): url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set={}" - url = url + "/openconfig-bgp-policy-ext:action" method = "DELETE" request = {"path": url.format(name), "method": method} return request @@ -270,25 +368,18 @@ class Bgp_as_paths(ConfigBase): name = cmd['name'] members = cmd['members'] permit = cmd['permit'] - if members: - diff_members = [] - for item in have: - if item['name'] == name: - for member_want in cmd['members']: - if item['members']: - if str(member_want) in item['members']: - diff_members.append(member_want) - if diff_members: - requests.append(self.get_delete_single_as_path_member_requests(name, diff_members)) - - elif permit: - for item in have: - if item['name'] == name: - requests.append(self.get_delete_single_as_path_action_requests(name)) - else: - for item in have: - if item['name'] == name: - requests.append(self.get_delete_single_as_path_requests(name)) + match = next((item for item in have if item['name'] == cmd['name']), None) + if match: + if members: + if match.get('members'): + del_members = set(match['members']).intersection(set(members)) + if del_members: + if len(del_members) == len(match['members']): + requests.append(self.get_delete_single_as_path_request(name)) + else: + requests.append(self.get_delete_single_as_path_member_request(name, del_members)) + else: + requests.append(self.get_delete_single_as_path_request(name)) return requests diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py index 670fb26d3..82ed70a3f 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -62,6 +62,13 @@ class Bgp_communities(ConfigBase): 'bgp_communities', ] + standard_communities_map = { + 'no_peer': 'NOPEER', + 'no_export': 'NO_EXPORT', + 'no_advertise': 'NO_ADVERTISE', + 'local_as': 'NO_EXPORT_SUBCONFED' + } + def __init__(self, module): super(Bgp_communities, self).__init__(module) @@ -89,6 +96,7 @@ class Bgp_communities(ConfigBase): existing_bgp_communities_facts = self.get_bgp_communities_facts() commands, requests = self.set_config(existing_bgp_communities_facts) + if commands and len(requests) > 0: if not self._module.check_mode: try: @@ -116,6 +124,13 @@ class Bgp_communities(ConfigBase): to the desired configuration """ want = self._module.params['config'] + if want: + for conf in want: + if conf.get("match", None): + conf["match"] = conf["match"].upper() + if conf.get("members", {}) and conf['members'].get("regex", []): + conf['members']['regex'].sort() + have = existing_bgp_communities_facts resp = self.set_state(want, have) return to_list(resp) @@ -138,17 +153,16 @@ class Bgp_communities(ConfigBase): # fp.write('comm: have: ' + str(have) + '\n') # fp.write('comm: diff: ' + str(diff) + '\n') if state == 'overridden': - commands, requests = self._state_overridden(want, have, diff) + commands, requests = self._state_overridden(want, have) elif state == 'deleted': - commands, requests = self._state_deleted(want, have, diff) + commands, requests = self._state_deleted(want, have) elif state == 'merged': commands, requests = self._state_merged(want, have, diff) elif state == 'replaced': - commands, requests = self._state_replaced(want, have, diff) + commands, requests = self._state_replaced(want, have) return commands, requests - @staticmethod - def _state_replaced(**kwargs): + def _state_replaced(self, want, have): """ The command generator when state is replaced :rtype: A list @@ -156,10 +170,13 @@ class Bgp_communities(ConfigBase): to the desired configuration """ commands = [] - return commands + requests = [] + + commands, requests = self.get_replaced_overridden_config(want, have, "replaced") - @staticmethod - def _state_overridden(**kwargs): + return commands, requests + + def _state_overridden(self, want, have): """ The command generator when state is overridden :rtype: A list @@ -167,7 +184,11 @@ class Bgp_communities(ConfigBase): to the desired configuration """ commands = [] - return commands + requests = [] + + commands, requests = self.get_replaced_overridden_config(want, have, "overridden") + + return commands, requests def _state_merged(self, want, have, diff): """ The command generator when state is merged @@ -177,7 +198,7 @@ class Bgp_communities(ConfigBase): the current configuration """ commands = diff - requests = self.get_modify_bgp_community_requests(commands, have) + requests = self.get_modify_bgp_community_requests(commands, have, "merged") if commands and len(requests) > 0: commands = update_states(commands, "merged") else: @@ -185,7 +206,7 @@ class Bgp_communities(ConfigBase): return commands, requests - def _state_deleted(self, want, have, diff): + def _state_deleted(self, want, have): """ The command generator when state is deleted :rtype: A list @@ -217,28 +238,18 @@ class Bgp_communities(ConfigBase): return commands, requests - def get_delete_single_bgp_community_member_requests(self, name, type, members): + def get_delete_single_bgp_community_member_requests(self, name, members): requests = [] for member in members: url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:" url = url + "bgp-defined-sets/community-sets/community-set={name}/config/{members_param}" method = "DELETE" - memberstr = member - if type == 'expanded': - memberstr = 'REGEX:' + member - members_params = {'community-member': memberstr} + members_params = {'community-member': member} members_str = urlencode(members_params) request = {"path": url.format(name=name, members_param=members_str), "method": method} requests.append(request) return requests - def get_delete_all_members_bgp_community_requests(self, name): - url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:" - url = url + "bgp-defined-sets/community-sets/community-set={}/config/community-member" - method = "DELETE" - request = {"path": url.format(name), "method": method} - return request - def get_delete_single_bgp_community_requests(self, name): url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set={}" method = "DELETE" @@ -255,70 +266,90 @@ class Bgp_communities(ConfigBase): return requests def get_delete_bgp_communities(self, commands, have, is_delete_all): - # with open('/root/ansible_log.log', 'a+') as fp: - # fp.write('bgp_commmunities: delete requests ************** \n') requests = [] if is_delete_all: requests = self.get_delete_all_bgp_communities(commands) else: for cmd in commands: name = cmd['name'] - type = cmd['type'] - members = cmd['members'] - if members: - if members['regex']: - diff_members = [] - for item in have: - if item['name'] == name and item['members']: - for member_want in members['regex']: - if str(member_want) in item['members']['regex']: - diff_members.append(member_want) - if diff_members: - requests.extend(self.get_delete_single_bgp_community_member_requests(name, type, diff_members)) - else: - for item in have: - if item['name'] == name: - if item['members']: - requests.append(self.get_delete_all_members_bgp_community_requests(name)) - else: - for item in have: - if item['name'] == name: + members = cmd.get('members', None) + cmd_type = cmd['type'] + diff_members = [] + + for item in have: + if item['name'] == name: + if 'permit' not in cmd or cmd['permit'] is None: + cmd['permit'] = item['permit'] + + if cmd == item: requests.append(self.get_delete_single_bgp_community_requests(name)) + break + + if cmd_type == "standard": + for attr in self.standard_communities_map: + if cmd.get(attr, None) and item[attr] and cmd[attr] == item[attr]: + diff_members.append(self.standard_communities_map[attr]) + + if members: + if members.get('regex', []): + for member_want in members['regex']: + if item.get('members', None) and item['members'].get('regex', []): + if str(member_want) in item['members']['regex']: + diff_members.append("REGEX:" + str(member_want)) + else: + requests.append(self.get_delete_single_bgp_community_requests(name)) + + else: + if cmd_type == "standard": + no_attr = True + for attr in self.standard_communities_map: + if cmd.get(attr, None): + no_attr = False + break + if no_attr: + requests.append(self.get_delete_single_bgp_community_requests(name)) + else: + requests.append(self.get_delete_single_bgp_community_requests(name)) + break + + if diff_members: + requests.extend(self.get_delete_single_bgp_community_member_requests(name, diff_members)) - # with open('/root/ansible_log.log', 'a+') as fp: - # fp.write('bgp_commmunities: delete requests' + str(requests) + '\n') return requests def get_new_add_request(self, conf): url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" method = "PATCH" - # members = conf['members'] - # members_str = ', '.join(members) - # members_list = list() - # for member in members.split(','): - # members_list.append(str(member)) + community_members = [] + community_action = "" if 'match' not in conf: conf['match'] = "ANY" - # with open('/root/ansible_log.log', 'a+') as fp: - # fp.write('bgp_communities: conf' + str(conf) + '\n') - if 'local_as' in conf and conf['local_as']: - conf['members']['regex'].append("NO_EXPORT_SUBCONFED") - if 'no_peer' in conf and conf['no_peer']: - conf['members']['regex'].append("NOPEER") - if 'no_export' in conf and conf['no_export']: - conf['members']['regex'].append("NO_EXPORT") - if 'no_advertise' in conf and conf['no_advertise']: - conf['members']['regex'].append("NO_ADVERTISE") - input_data = {'name': conf['name'], 'members_list': conf['members']['regex'], 'match': conf['match']} - if conf['type'] == 'expanded': - input_data['regex'] = "REGEX:" - else: - input_data['regex'] = "" + + if conf['type'] == 'standard': + for attr in self.standard_communities_map: + if attr in conf and conf[attr]: + community_members.append(self.standard_communities_map[attr]) + if 'members' in conf and conf['members'] and conf['members'].get('regex', []): + for i in conf['members']['regex']: + community_members.extend([str(i)]) + if not community_members: + self._module.fail_json(msg='Cannot create standard community-list {0} without community attributes'.format(conf['name'])) + + elif conf['type'] == 'expanded': + if 'members' in conf and conf['members'] and conf['members'].get('regex', []): + for i in conf['members']['regex']: + community_members.extend(["REGEX:" + str(i)]) + if not community_members: + self._module.fail_json(msg='Cannot create expanded community-list {0} without community attributes'.format(conf['name'])) + if conf['permit']: - input_data['permit'] = "PERMIT" + community_action = "PERMIT" else: - input_data['permit'] = "DENY" + community_action = "DENY" + + input_data = {'name': conf['name'], 'members_list': community_members, 'match': conf['match'].upper(), 'permit': community_action} + payload_template = """ { "openconfig-bgp-policy:community-sets": { @@ -328,7 +359,7 @@ class Bgp_communities(ConfigBase): "config": { "community-set-name": "{{name}}", "community-member": [ - {% for member in members_list %}"{{regex}}{{member}}"{%- if not loop.last -%},{% endif %}{%endfor%} + {% for member in members_list %}"{{member}}"{%- if not loop.last -%},{% endif %}{%endfor%} ], "openconfig-bgp-policy-ext:action": "{{permit}}", "match-set-options": "{{match}}" @@ -342,27 +373,118 @@ class Bgp_communities(ConfigBase): intended_payload = t.render(input_data) ret_payload = json.loads(intended_payload) request = {"path": url, "method": method, "data": ret_payload} - # with open('/root/ansible_log.log', 'a+') as fp: - # fp.write('bgp_communities: request' + str(request) + '\n') + return request - def get_modify_bgp_community_requests(self, commands, have): + def get_modify_bgp_community_requests(self, commands, have, cur_state): requests = [] if not commands: return requests for conf in commands: - for item in have: - if item['name'] == conf['name']: - if 'type' not in conf: - conf['type'] = item['type'] - if 'permit' not in conf: - conf['permit'] = item['permit'] - if 'match' not in conf: - conf['match'] = item['match'] - if 'members' not in conf: - conf['members'] = item['members'] + if cur_state == "merged": + for item in have: + if item['name'] == conf['name']: + if 'type' not in conf: + conf['type'] = item['type'] + if 'permit' not in conf or conf['permit'] is None: + conf['permit'] = item['permit'] + if 'match' not in conf: + conf['match'] = item['match'] + if conf['type'] == "standard": + for attr in self.standard_communities_map: + if attr not in conf and attr in item: + conf[attr] = item[attr] + else: + if 'members' not in conf: + if item.get('members', {}) and item['members'].get('regex', []): + conf['members'] = {'regex': item['members']['regex']} + else: + conf['members'] = item['members'] + break + new_req = self.get_new_add_request(conf) if new_req: requests.append(new_req) return requests + + def get_replaced_overridden_config(self, want, have, cur_state): + commands, requests = [], [] + + commands_del, requests_del = [], [] + commands_add, requests_add = [], [] + + for conf in want: + name = conf['name'] + in_have = False + for have_conf in have: + if have_conf['name'] == name: + in_have = True + if have_conf['type'] != conf['type']: + # If both community list are of same name but different types + commands_del.append(have_conf) + commands_add.append(conf) + else: + is_change = False + + if have_conf['permit'] != conf['permit']: + is_change = True + + if have_conf['match'] != conf['match']: + is_change = is_delete = True + + if conf["type"] == "standard": + no_attr = True + for attr in self.standard_communities_map: + if not conf.get(attr, None): + if have_conf.get(attr, None): + is_change = True + else: + no_attr = False + if not have_conf.get(attr, None): + is_change = True + + if no_attr: + # Since standard type needs atleast one attribute to exist + self._module.fail_json(msg='Cannot create standard community-list {0} without community attributes'.format(conf['name'])) + else: + members = conf.get('members', {}) + if members and members.get('regex', []): + if have_conf.get('members', {}) and have_conf['members'].get('regex', []): + if set(have_conf['members']['regex']).symmetric_difference(set(members['regex'])): + is_change = True + else: + # If there are no members in any community list of want, then + # that particular community list request to be ignored since + # expanded type needs community-member to exist + self._module.fail_json(msg='Cannot create expanded community-list {0} without community attributes'.format(conf['name'])) + + if is_change: + commands_add.append(conf) + commands_del.append(have_conf) + break + + if not in_have: + commands_add.append(conf) + + if cur_state == "overridden": + for have_conf in have: + in_want = next((conf for conf in want if conf['name'] == have_conf['name']), None) + if not in_want: + commands_del.append(have_conf) + + if commands_del: + requests_del = self.get_delete_bgp_communities(commands_del, have, False) + + if len(requests_del) > 0: + commands.extend(update_states(commands_del, "deleted")) + requests.extend(requests_del) + + if commands_add: + requests_add = self.get_modify_bgp_community_requests(commands_add, have, cur_state) + + if len(requests_add) > 0: + commands.extend(update_states(commands_add, cur_state)) + requests.extend(requests_add) + + return commands, requests diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py index 751f88e48..8cd9953e6 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -60,6 +60,11 @@ class Bgp_ext_communities(ConfigBase): 'bgp_ext_communities', ] + standard_communities_map = { + "route_origin": "route-origin", + "route_target": "route-target" + } + def __init__(self, module): super(Bgp_ext_communities, self).__init__(module) @@ -87,6 +92,7 @@ class Bgp_ext_communities(ConfigBase): existing_bgp_ext_communities_facts = self.get_bgp_ext_communities_facts() commands, requests = self.set_config(existing_bgp_ext_communities_facts) + if commands and len(requests) > 0: if not self._module.check_mode: try: @@ -114,6 +120,21 @@ class Bgp_ext_communities(ConfigBase): to the desired configuration """ want = self._module.params['config'] + if want: + for conf in want: + cmd_type = conf.get("type", None) + if cmd_type and conf.get("match", None): + conf['match'] = conf['match'].lower() + if cmd_type and conf.get("members", {}): + if cmd_type == "expanded": + if conf['members'].get("regex", []): + conf['members']['regex'].sort() + else: + if conf['members'].get("route_origin", []): + conf['members']['route_origin'].sort() + if conf['members'].get("route_target", []): + conf['members']['route_target'].sort() + have = existing_bgp_ext_communities_facts resp = self.set_state(want, have) return to_list(resp) @@ -133,17 +154,16 @@ class Bgp_ext_communities(ConfigBase): new_want = self.validate_type(want) diff = get_diff(new_want, have) if state == 'overridden': - commands, requests = self._state_overridden(want, have, diff) + commands, requests = self._state_overridden(want, have) elif state == 'deleted': - commands, requests = self._state_deleted(want, have, diff) + commands, requests = self._state_deleted(want, have) elif state == 'merged': commands, requests = self._state_merged(want, have, diff) elif state == 'replaced': - commands, requests = self._state_replaced(want, have, diff) + commands, requests = self._state_replaced(want, have) return commands, requests - @staticmethod - def _state_replaced(**kwargs): + def _state_replaced(self, want, have): """ The command generator when state is replaced :rtype: A list @@ -151,10 +171,13 @@ class Bgp_ext_communities(ConfigBase): to the desired configuration """ commands = [] - return commands + requests = [] + + commands, requests = self.get_replaced_overridden_config(want, have, "replaced") - @staticmethod - def _state_overridden(**kwargs): + return commands, requests + + def _state_overridden(self, want, have): """ The command generator when state is overridden :rtype: A list @@ -162,7 +185,11 @@ class Bgp_ext_communities(ConfigBase): to the desired configuration """ commands = [] - return commands + requests = [] + + commands, requests = self.get_replaced_overridden_config(want, have, "overridden") + + return commands, requests def _state_merged(self, want, have, diff): """ The command generator when state is merged @@ -172,7 +199,7 @@ class Bgp_ext_communities(ConfigBase): the current configuration """ commands = diff - requests = self.get_modify_bgp_ext_community_requests(commands, have) + requests = self.get_modify_bgp_ext_community_requests(commands, have, "merged") if commands and len(requests) > 0: commands = update_states(commands, "merged") else: @@ -180,7 +207,7 @@ class Bgp_ext_communities(ConfigBase): return commands, requests - def _state_deleted(self, want, have, diff): + def _state_deleted(self, want, have): """ The command generator when state is deleted :rtype: A list @@ -204,7 +231,7 @@ class Bgp_ext_communities(ConfigBase): return commands, requests - def get_delete_single_bgp_ext_community_member_requests(self, name, type, members): + def get_delete_single_bgp_ext_community_member_requests(self, name, members): requests = [] for member in members: url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:" @@ -216,13 +243,6 @@ class Bgp_ext_communities(ConfigBase): requests.append(request) return requests - def get_delete_all_members_bgp_ext_community_requests(self, name): - url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:" - url = url + "bgp-defined-sets/ext-community-sets/ext-community-set={}/config/ext-community-member" - method = "DELETE" - request = {"path": url.format(name), "method": method} - return request - def get_delete_single_bgp_ext_community_requests(self, name): url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set={}" method = "DELETE" @@ -245,71 +265,79 @@ class Bgp_ext_communities(ConfigBase): else: for cmd in commands: name = cmd['name'] - type = cmd['type'] - members = cmd['members'] - if members: - if members['regex'] or members['route_origin'] or members['route_target']: - diff_members = [] - for item in have: - if item['name'] == name and item['members']: - if members['regex']: + cmd_type = cmd['type'] + members = cmd.get('members', None) + diff_members = [] + + for item in have: + if item["name"] == name: + if 'permit' not in cmd or cmd['permit'] is None: + cmd['permit'] = item['permit'] + if cmd == item: + requests.append(self.get_delete_single_bgp_ext_community_requests(name)) + break + + if members: + if cmd_type == "expanded": + if members.get('regex', []): for member_want in members['regex']: - if str(member_want) in item['members']['regex']: - diff_members.append('REGEX:' + str(member_want)) - if members['route_origin']: - for member_want in members['route_origin']: - if str(member_want) in item['members']['route_origin']: - diff_members.append("route-origin:" + str(member_want)) - if members['route_target']: - for member_want in members['route_target']: - if str(member_want) in item['members']['route_target']: - diff_members.append("route-target:" + str(member_want)) - if diff_members: - requests.extend(self.get_delete_single_bgp_ext_community_member_requests(name, type, diff_members)) - else: - for item in have: - if item['name'] == name: - if item['members']: - requests.append(self.get_delete_all_members_bgp_ext_community_requests(name)) - else: - for item in have: - if item['name'] == name: + if item.get("members", None) and item['members'].get('regex', []): + if str(member_want) in item['members']['regex']: + diff_members.append("REGEX:" + str(member_want)) + else: + requests.append(self.get_delete_single_bgp_ext_community_requests(name)) + else: + no_members = True + for attr in self.standard_communities_map: + if members.get(attr, []): + no_members = False + for member_want in members[attr]: + if item.get("members", None) and item['members'].get(attr, []): + if str(member_want) in item['members'][attr]: + diff_members.append(self.standard_communities_map[attr] + ":" + str(member_want)) + if no_members: + requests.append(self.get_delete_single_bgp_ext_community_requests(name)) + else: requests.append(self.get_delete_single_bgp_ext_community_requests(name)) + break + + if diff_members: + requests.extend(self.get_delete_single_bgp_ext_community_member_requests(name, diff_members)) + return requests def get_new_add_request(self, conf): url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" method = "PATCH" - members = conf.get('members', None) + community_members = [] + community_action = "" + if 'match' not in conf: conf['match'] = "ANY" - else: - conf['match'] = conf['match'].upper() - input_data = {'name': conf['name'], 'match': conf['match']} - - input_data['members_list'] = list() - if members: - regex = members.get('regex', None) - if regex: - input_data['members_list'].extend(["REGEX:" + cfg for cfg in regex]) - else: - route_target = members.get('route_target', None) - if route_target: - input_data['members_list'].extend(["route-target:" + cfg for cfg in route_target]) - route_origin = members.get('route_origin', None) - if route_origin: - input_data['members_list'].extend(["route-origin:" + cfg for cfg in route_origin]) if conf['type'] == 'expanded': - input_data['regex'] = "REGEX:" - else: - input_data['regex'] = "" + if 'members' in conf and conf['members'] and conf['members'].get('regex', []): + for i in conf['members']['regex']: + community_members.extend(["REGEX:" + str(i)]) + elif conf['type'] == 'standard': + for attr in self.standard_communities_map: + if 'members' in conf and conf['members'] and conf['members'].get(attr, []): + for i in conf['members'][attr]: + community_members.extend([self.standard_communities_map[attr] + ":" + str(i)]) + + if not community_members: + self._module.fail_json(msg='Cannot create {0} community-list {1} without community attributes'.format(conf['type'], conf['name'])) + return {} + if conf['permit']: - input_data['permit'] = "PERMIT" + community_action = "PERMIT" else: - input_data['permit'] = "DENY" + community_action = "DENY" + + input_data = {'name': conf['name'], 'members_list': community_members, 'match': conf['match'].upper(), 'permit': community_action} + payload_template = """ { "openconfig-bgp-policy:ext-community-sets": { @@ -335,23 +363,37 @@ class Bgp_ext_communities(ConfigBase): request = {"path": url, "method": method, "data": ret_payload} return request - def get_modify_bgp_ext_community_requests(self, commands, have): + def get_modify_bgp_ext_community_requests(self, commands, have, cur_state): requests = [] if not commands: return requests for conf in commands: - for item in have: - if item['name'] == conf['name']: - if 'type' not in conf: - conf['type'] = item['type'] - if 'permit' not in conf: - conf['permit'] = item['permit'] - if 'match' not in conf: - conf['match'] = item['match'] - if 'members' not in conf: - conf['members'] = item['members'] - break + if cur_state == "merged": + for item in have: + if item['name'] == conf['name']: + if 'type' not in conf: + conf['type'] = item['type'] + if 'permit' not in conf or conf['permit'] is None: + conf['permit'] = item['permit'] + if 'match' not in conf: + conf['match'] = item['match'] + if 'members' not in conf: + if conf['type'] == "expanded": + if item.get('members', {}) and item['members'].get('regex', []): + conf['members'] = {'regex': item['members']['regex']} + else: + conf['members'] = item['members'] + else: + no_members = True + for attr in self.standard_communities_map: + if item.get('members', {}) and item['members'].get(attr, []): + no_members = False + conf['members'] = {attr: item['members'][attr]} + if no_members: + conf['members'] = item['members'] + break + new_req = self.get_new_add_request(conf) if new_req: requests.append(new_req) @@ -369,3 +411,84 @@ class Bgp_ext_communities(ConfigBase): new_want.append(cfg) return new_want + + def get_replaced_overridden_config(self, want, have, cur_state): + commands, requests = [], [] + + commands_del, requests_del = [], [] + commands_add, requests_add = [], [] + + for conf in want: + name = conf['name'] + in_have = False + for have_conf in have: + if have_conf['name'] == name: + in_have = True + if have_conf['type'] != conf['type']: + # If both extended community list are of same name but different types + commands_del.append(have_conf) + commands_add.append(conf) + else: + is_change = False + + if have_conf['permit'] != conf['permit']: + is_change = True + + if have_conf['match'] != conf['match']: + is_change = True + + if conf["type"] == "expanded": + members = conf.get('members', {}) + if members and conf['members'].get('regex', []): + if have_conf.get('members', {}) and have_conf['members'].get('regex', []): + if set(have_conf['members']['regex']).symmetric_difference(set(members['regex'])): + is_change = True + else: + # If there are no members in any expanded ext community list of want, then + # abort the playbook with an error message explaining why the specified command is not valid + self._module.fail_json(msg='Cannot create expanded extended community-list ' + '{0} without community attributes'.format(conf['name'])) + else: + members = conf.get('members', {}) + no_members = True + for attr in self.standard_communities_map: + if members and conf['members'].get(attr, []): + no_members = False + if have_conf.get('members', {}) and have_conf['members'].get(attr, []): + if set(have_conf['members'][attr]).symmetric_difference(set(members[attr])): + is_change = True + + if no_members: + # If there are no members in any standard ext community list of want, then + # abort the playbook with an error message explaining why the specified command is not valid + self._module.fail_json(msg='Cannot create standard extended community-list ' + '{0} without community attributes'.format(conf['name'])) + + if is_change: + commands_add.append(conf) + commands_del.append(have_conf) + break + if not in_have: + commands_add.append(conf) + + if cur_state == "overridden": + for have_conf in have: + in_want = next((conf for conf in want if conf['name'] == have_conf['name']), None) + if not in_want: + commands_del.append(have_conf) + + if commands_del: + requests_del = self.get_delete_bgp_ext_communities(commands_del, have, False) + + if len(requests_del) > 0: + commands.extend(update_states(commands_del, "deleted")) + requests.extend(requests_del) + + if commands_add: + requests_add = self.get_modify_bgp_ext_community_requests(commands_add, have, cur_state) + + if len(requests_add) > 0: + commands.extend(update_states(commands_add, cur_state)) + requests.extend(requests_add) + + return commands, requests diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors/bgp_neighbors.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors/bgp_neighbors.py index 31bbec78d..9c0920832 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors/bgp_neighbors.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors/bgp_neighbors.py @@ -27,6 +27,7 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( update_states, get_diff, + remove_matching_defaults ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils import ( validate_bgps, @@ -37,6 +38,8 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import to_request from ansible.module_utils.connection import ConnectionError +from copy import deepcopy + PATCH = 'patch' DELETE = 'delete' @@ -47,6 +50,95 @@ TEST_KEYS = [ {'afis': {'afi': '', 'safi': ''}}, ] +default_entries = [ + [ + {'name': 'peer_group'}, + {'name': 'timers'}, + {'name': 'keepalive', 'default': 60} + ], + [ + {'name': 'peer_group'}, + {'name': 'timers'}, + {'name': 'holdtime', 'default': 180} + ], + [ + {'name': 'peer_group'}, + {'name': 'timers'}, + {'name': 'connect_retry', 'default': 30} + ], + [ + {'name': 'peer_group'}, + {'name': 'advertisement_interval', 'default': 30} + ], + [ + {'name': 'peer_group'}, + {'name': 'auth_pwd'}, + {'name': 'encrypted', 'default': False} + ], + [ + {'name': 'peer_group'}, + {'name': 'ebgp_multihop'}, + {'name': 'enabled', 'default': False} + ], + [ + {'name': 'peer_group'}, + {'name': 'passive', 'default': False} + ], + [ + {'name': 'peer_group'}, + {'name': 'address_family'}, + {'name': 'afis'}, + {'name': 'ip_afi'}, + {'name': 'send_default_route', 'default': False} + ], + [ + {'name': 'peer_group'}, + {'name': 'address_family'}, + {'name': 'afis'}, + {'name': 'activate', 'default': False} + ], + [ + {'name': 'peer_group'}, + {'name': 'address_family'}, + {'name': 'afis'}, + {'name': 'prefix_limit'}, + {'name': 'prevent_teardown', 'default': False} + ], + [ + {'name': 'neighbors'}, + {'name': 'timers'}, + {'name': 'keepalive', 'default': 60} + ], + [ + {'name': 'neighbors'}, + {'name': 'timers'}, + {'name': 'holdtime', 'default': 180} + ], + [ + {'name': 'neighbors'}, + {'name': 'timers'}, + {'name': 'connect_retry', 'default': 30} + ], + [ + {'name': 'neighbors'}, + {'name': 'advertisement_interval', 'default': 30} + ], + [ + {'name': 'neighbors'}, + {'name': 'auth_pwd'}, + {'name': 'encrypted', 'default': False} + ], + [ + {'name': 'neighbors'}, + {'name': 'ebgp_multihop'}, + {'name': 'enabled', 'default': False} + ], + [ + {'name': 'neighbors'}, + {'name': 'passive', 'default': False} + ], +] + class Bgp_neighbors(ConfigBase): """ @@ -180,7 +272,9 @@ class Bgp_neighbors(ConfigBase): commands = have new_have = have else: - new_have = self.remove_default_entries(have) + new_have = deepcopy(have) + for default_entry in default_entries: + remove_matching_defaults(new_have, default_entry) d_diff = get_diff(want, new_have, TEST_KEYS, is_skeleton=True) delete_diff = get_diff(want, d_diff, TEST_KEYS, is_skeleton=True) commands = delete_diff @@ -192,141 +286,6 @@ class Bgp_neighbors(ConfigBase): commands = [] return commands, requests - def remove_default_entries(self, data): - new_data = [] - if not data: - return new_data - for conf in data: - new_conf = {} - as_val = conf['bgp_as'] - vrf_name = conf['vrf_name'] - new_conf['bgp_as'] = as_val - new_conf['vrf_name'] = vrf_name - peergroup = conf.get('peer_group', None) - new_peergroups = [] - if peergroup is not None: - for pg in peergroup: - new_pg = {} - pg_val = pg.get('name', None) - new_pg['name'] = pg_val - remote_as = pg.get('remote_as', None) - new_remote = {} - if remote_as: - peer_as = remote_as.get('peer_as', None) - peer_type = remote_as.get('peer_type', None) - if peer_as is not None: - new_remote['peer_as'] = peer_as - if peer_type is not None: - new_remote['peer_type'] = peer_type - if new_remote: - new_pg['remote_as'] = new_remote - timers = pg.get('timers', None) - new_timers = {} - if timers: - keepalive = timers.get('keepalive', None) - holdtime = timers.get('holdtime', None) - connect_retry = timers.get('connect_retry', None) - if keepalive is not None and keepalive != 60: - new_timers['keepalive'] = keepalive - if holdtime is not None and holdtime != 180: - new_timers['holdtime'] = holdtime - if connect_retry is not None and connect_retry != 30: - new_timers['connect_retry'] = connect_retry - if new_timers: - new_pg['timers'] = new_timers - advertisement_interval = pg.get('advertisement_interval', None) - if advertisement_interval is not None and advertisement_interval != 30: - new_pg['advertisement_interval'] = advertisement_interval - bfd = pg.get('bfd', None) - if bfd is not None: - new_pg['bfd'] = bfd - capability = pg.get('capability', None) - if capability is not None: - new_pg['capability'] = capability - afi = [] - address_family = pg.get('address_family', None) - if address_family: - if address_family.get('afis', None): - for each in address_family['afis']: - if each: - tmp = {} - if each.get('afi', None) is not None: - tmp['afi'] = each['afi'] - if each.get('safi', None) is not None: - tmp['safi'] = each['safi'] - if each.get('activate', None) is not None and each['activate'] is not False: - tmp['activate'] = each['activate'] - if each.get('allowas_in', None) is not None: - tmp['allowas_in'] = each['allowas_in'] - if each.get('ip_afi', None) is not None: - tmp['ip_afi'] = each['ip_afi'] - if each.get('prefix_limit', None) is not None: - tmp['prefix_limit'] = each['prefix_limit'] - if each.get('prefix_list_in', None) is not None: - tmp['prefix_list_in'] = each['prefix_list_in'] - if each.get('prefix_list_out', None) is not None: - tmp['prefix_list_out'] = each['prefix_list_out'] - afi.append(tmp) - if afi and len(afi) > 0: - afis = {} - afis.update({'afis': afi}) - new_pg['address_family'] = afis - if new_pg: - new_peergroups.append(new_pg) - if new_peergroups: - new_conf['peer_group'] = new_peergroups - neighbors = conf.get('neighbors', None) - new_neighbors = [] - if neighbors is not None: - for neighbor in neighbors: - new_neighbor = {} - neighbor_val = neighbor.get('neighbor', None) - new_neighbor['neighbor'] = neighbor_val - remote_as = neighbor.get('remote_as', None) - new_remote = {} - if remote_as: - peer_as = remote_as.get('peer_as', None) - peer_type = remote_as.get('peer_type', None) - if peer_as is not None: - new_remote['peer_as'] = peer_as - if peer_type is not None: - new_remote['peer_type'] = peer_type - if new_remote: - new_neighbor['remote_as'] = new_remote - peer_group = neighbor.get('peer_group', None) - if peer_group: - new_neighbor['peer_group'] = peer_group - timers = neighbor.get('timers', None) - new_timers = {} - if timers: - keepalive = timers.get('keepalive', None) - holdtime = timers.get('holdtime', None) - connect_retry = timers.get('connect_retry', None) - if keepalive is not None and keepalive != 60: - new_timers['keepalive'] = keepalive - if holdtime is not None and holdtime != 180: - new_timers['holdtime'] = holdtime - if connect_retry is not None and connect_retry != 30: - new_timers['connect_retry'] = connect_retry - if new_timers: - new_neighbor['timers'] = new_timers - advertisement_interval = neighbor.get('advertisement_interval', None) - if advertisement_interval is not None and advertisement_interval != 30: - new_neighbor['advertisement_interval'] = advertisement_interval - bfd = neighbor.get('bfd', None) - if bfd is not None: - new_neighbor['bfd'] = bfd - capability = neighbor.get('capability', None) - if capability is not None: - new_neighbor['capability'] = capability - if new_neighbor: - new_neighbors.append(new_neighbor) - if new_neighbors: - new_conf['neighbors'] = new_neighbors - if new_conf: - new_data.append(new_conf) - return new_data - def build_bgp_peer_groups_payload(self, cmd, have, bgp_as, vrf_name): requests = [] bgp_peer_group_list = [] @@ -444,7 +403,7 @@ class Bgp_neighbors(ConfigBase): if each.get('prefix_limit', None) is not None: pfx_lmt_cfg = get_prefix_limit_payload(each['prefix_limit']) if pfx_lmt_cfg and afi_safi == 'L2VPN_EVPN': - samp.update({'l2vpn-evpn': {'prefix-limit': {'config': pfx_lmt_cfg}}}) + self._module.fail_json('Prefix limit configuration not supported for l2vpn evpn') else: if each.get('ip_afi', None) is not None: afi_safi_cfg = get_ip_afi_cfg_payload(each['ip_afi']) @@ -696,13 +655,35 @@ class Bgp_neighbors(ConfigBase): advertisement_interval = each.get('advertisement_interval', None) bfd = each.get('bfd', None) capability = each.get('capability', None) + auth_pwd = each.get('auth_pwd', None) + pg_description = each.get('pg_description', None) + disable_connected_check = each.get('disable_connected_check', None) + dont_negotiate_capability = each.get('dont_negotiate_capability', None) + ebgp_multihop = each.get('ebgp_multihop', None) + enforce_first_as = each.get('enforce_first_as', None) + enforce_multihop = each.get('enforce_multihop', None) + local_address = each.get('local_address', None) + local_as = each.get('local_as', None) + override_capability = each.get('override_capability', None) + passive = each.get('passive', None) + shutdown_msg = each.get('shutdown_msg', None) + solo = each.get('solo', None) + strict_capability_match = each.get('strict_capability_match', None) + ttl_security = each.get('ttl_security', None) address_family = each.get('address_family', None) - if name and not remote_as and not timers and not advertisement_interval and not bfd and not capability and not address_family: + if (name and not remote_as and not timers and not advertisement_interval and not bfd and not capability and not auth_pwd and not + pg_description and disable_connected_check is None and dont_negotiate_capability is None and not ebgp_multihop and + enforce_first_as is None and enforce_multihop is None and not local_address and not local_as and override_capability + is None and passive is None and not shutdown_msg and solo is None and strict_capability_match is None and not ttl_security and + not address_family): want_pg_match = None if want_peer_group: want_pg_match = next((cfg for cfg in want_peer_group if cfg['name'] == name), None) if want_pg_match: - keys = ['remote_as', 'timers', 'advertisement_interval', 'bfd', 'capability', 'address_family'] + keys = ['remote_as', 'timers', 'advertisement_interval', 'bfd', 'capability', 'auth_pwd', 'pg_description', + 'disable_connected_check', 'dont_negotiate_capability', 'ebgp_multihop', 'enforce_first_as', 'enforce_multihop', + 'local_address', 'local_as', 'override_capability', 'passive', 'shutdown_msg', 'solo', 'strict_capability_match', + 'ttl_security', 'address_family'] if not any(want_pg_match.get(key, None) for key in keys): requests.append(self.get_delete_vrf_specific_peergroup_request(vrf_name, name)) else: @@ -808,7 +789,7 @@ class Bgp_neighbors(ConfigBase): delete_path = delete_static_path + '/ebgp-multihop/config/enabled' requests.append({'path': delete_path, 'method': DELETE}) if cmd['ebgp_multihop'].get('multihop_ttl', None) is not None: - delete_path = delete_static_path + '/ebgp-multihop/config/multihop_ttl' + delete_path = delete_static_path + '/ebgp-multihop/config/multihop-ttl' requests.append({'path': delete_path, 'method': DELETE}) if cmd.get('address_family', None) is not None: if cmd['address_family'].get('afis', None) is None: @@ -857,9 +838,6 @@ class Bgp_neighbors(ConfigBase): requests.extend(self.delete_ip_afi_requests(ip_afi, afi_safi_name, 'ipv6-unicast', delete_static_path)) if prefix_limit: requests.extend(self.delete_prefix_limit_requests(prefix_limit, afi_safi_name, 'ipv6-unicast', delete_static_path)) - elif afi_safi == 'L2VPN_EVPN': - if prefix_limit: - requests.extend(self.delete_prefix_limit_requests(prefix_limit, afi_safi_name, 'l2vpn-evpn', delete_static_path)) return requests @@ -909,12 +887,36 @@ class Bgp_neighbors(ConfigBase): advertisement_interval = each.get('advertisement_interval', None) bfd = each.get('bfd', None) capability = each.get('capability', None) - if neighbor and not remote_as and not peer_group and not timers and not advertisement_interval and not bfd and not capability: + auth_pwd = each.get('auth_pwd', None) + nbr_description = each.get('nbr_description', None) + disable_connected_check = each.get('disable_connected_check', None) + dont_negotiate_capability = each.get('dont_negotiate_capability', None) + ebgp_multihop = each.get('ebgp_multihop', None) + enforce_first_as = each.get('enforce_first_as', None) + enforce_multihop = each.get('enforce_multihop', None) + local_address = each.get('local_address', None) + local_as = each.get('local_as', None) + override_capability = each.get('override_capability', None) + passive = each.get('passive', None) + port = each.get('port', None) + shutdown_msg = each.get('shutdown_msg', None) + solo = each.get('solo', None) + strict_capability_match = each.get('strict_capability_match', None) + ttl_security = each.get('ttl_security', None) + v6only = each.get('v6only', None) + if (neighbor and not remote_as and not peer_group and not timers and not advertisement_interval and not bfd and not capability and not + auth_pwd and not nbr_description and disable_connected_check is None and dont_negotiate_capability is None and not + ebgp_multihop and enforce_first_as is None and enforce_multihop is None and not local_address and not local_as and + override_capability is None and passive is None and not port and not shutdown_msg and solo is None and strict_capability_match + is None and not ttl_security and v6only is None): want_nei_match = None if want_neighbors: want_nei_match = next(cfg for cfg in want_neighbors if cfg['neighbor'] == neighbor) if want_nei_match: - keys = ['remote_as', 'peer_group', 'timers', 'advertisement_interval', 'bfd', 'capability'] + keys = ['remote_as', 'peer_group', 'timers', 'advertisement_interval', 'bfd', 'capability', 'auth_pwd', 'nbr_description', + 'disable_connected_check', 'dont_negotiate_capability', 'ebgp_multihop', 'enforce_first_as', 'enforce_multihop', + 'local_address', 'local_as', 'override_capability', 'passive', 'port', 'shutdown_msg', 'solo', + 'strict_capability_match', 'ttl_security', 'v6only'] if not any(want_nei_match.get(key, None) for key in keys): requests.append(self.delete_neighbor_whole_request(vrf_name, neighbor)) else: @@ -1034,7 +1036,7 @@ class Bgp_neighbors(ConfigBase): delete_path = delete_static_path + '/ebgp-multihop/config/enabled' requests.append({'path': delete_path, 'method': DELETE}) if cmd['ebgp_multihop'].get('multihop_ttl', None) is not None: - delete_path = delete_static_path + '/ebgp-multihop/config/multihop_ttl' + delete_path = delete_static_path + '/ebgp-multihop/config/multihop-ttl' requests.append({'path': delete_path, 'method': DELETE}) return requests diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors_af/bgp_neighbors_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors_af/bgp_neighbors_af.py index 15f46f966..196a6eea9 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors_af/bgp_neighbors_af.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors_af/bgp_neighbors_af.py @@ -13,11 +13,6 @@ created from __future__ import absolute_import, division, print_function __metaclass__ = type -try: - from urllib import quote -except ImportError: - from urllib.parse import quote - from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -288,7 +283,7 @@ class Bgp_neighbors_af(ConfigBase): if conf_prefix_limit: pfx_lmt_cfg = get_prefix_limit_payload(conf_prefix_limit) if pfx_lmt_cfg and afi_safi_val == 'L2VPN_EVPN': - afi_safi['l2vpn-evpn'] = {'prefix-limit': {'config': pfx_lmt_cfg}} + self._module.fail_json('Prefix limit configuration not supported for l2vpn evpn') else: if conf_ip_afi: ip_afi_cfg = get_ip_afi_cfg_payload(conf_ip_afi) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/copp/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/copp/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/copp/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/copp/copp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/copp/copp.py new file mode 100644 index 000000000..cec802e67 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/copp/copp.py @@ -0,0 +1,393 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_copp class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + get_diff, + get_replaced_config, + send_requests, + remove_empties, + update_states, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) + + +COPP_GROUPS_PATH = '/data/openconfig-copp-ext:copp/copp-groups' +PATCH = 'patch' +DELETE = 'delete' +TEST_KEYS = [ + {'copp_groups': {'copp_name': ''}} +] +reserved_copp_names = [ + 'copp-system-lacp', + 'copp-system-udld', + 'copp-system-stp', + 'copp-system-bfd', + 'copp-system-ptp', + 'copp-system-lldp', + 'copp-system-vrrp', + 'copp-system-iccp', + 'copp-system-ospf', + 'copp-system-bgp', + 'copp-system-pim', + 'copp-system-igmp', + 'copp-system-suppress', + 'copp-system-arp', + 'copp-system-dhcp', + 'copp-system-icmp', + 'copp-system-ip2me', + 'copp-system-subnet', + 'copp-system-nat', + 'copp-system-mtu', + 'copp-system-sflow', + 'copp-system-default', + 'copp-system-ttl', + 'default' +] + + +class Copp(ConfigBase): + """ + The sonic_copp class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'copp', + ] + + def __init__(self, module): + super(Copp, self).__init__(module) + + def get_copp_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + copp_facts = facts['ansible_network_resources'].get('copp') + if not copp_facts: + return [] + return copp_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = [] + commands = [] + + existing_copp_facts = self.get_copp_facts() + commands, requests = self.set_config(existing_copp_facts) + if commands and len(requests) > 0: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + result['changed'] = True + result['commands'] = commands + + changed_copp_facts = self.get_copp_facts() + + result['before'] = existing_copp_facts + if result['changed']: + result['after'] = changed_copp_facts + + result['warnings'] = warnings + return result + + def set_config(self, existing_copp_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + have = existing_copp_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + state = self._module.params['state'] + + diff = get_diff(want, have, TEST_KEYS) + + if state == 'overridden': + commands, requests = self._state_overridden(want, have) + elif state == 'deleted': + commands, requests = self._state_deleted(want, have) + elif state == 'merged': + commands, requests = self._state_merged(diff) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have, diff) + return commands, requests + + def _state_replaced(self, want, have, diff): + """ The command generator when state is replaced + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + replaced_config = get_replaced_config(want, have, TEST_KEYS) + + if replaced_config: + is_delete_all = True + requests = self.get_delete_copp_requests(replaced_config, None, is_delete_all) + send_requests(self._module, requests) + + commands = want + else: + commands = diff + + requests = [] + + if commands: + requests = self.get_modify_copp_groups_request(commands) + + if len(requests) > 0: + commands = update_states(commands, "replaced") + else: + commands = [] + else: + commands = [] + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + self.sort_lists_in_config(want) + self.sort_lists_in_config(have) + + have = self.filter_copp_groups(have) + if have and have != want: + is_delete_all = True + requests = self.get_delete_copp_requests(have, None, is_delete_all) + send_requests(self._module, requests) + have = [] + + commands = [] + requests = [] + + if not have and want: + commands = want + requests = self.get_modify_copp_groups_request(commands) + + if len(requests) > 0: + commands = update_states(commands, "overridden") + else: + commands = [] + + return commands, requests + + def _state_merged(self, diff): + """ The command generator when state is merged + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = diff + requests = self.get_modify_copp_groups_request(commands) + + if commands and len(requests) > 0: + commands = update_states(commands, "merged") + else: + commands = [] + + return commands, requests + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + is_delete_all = False + # if want is none, then delete ALL + want = remove_empties(want) + if not want: + commands = have + is_delete_all = True + else: + commands = want + commands = self.filter_copp_groups(commands) + requests = self.get_delete_copp_requests(commands, have, is_delete_all) + if commands and len(requests) > 0: + commands = update_states(commands, "deleted") + else: + commands = [] + + return commands, requests + + def get_modify_copp_groups_request(self, commands): + request = None + + copp_groups = commands.get('copp_groups', None) + if copp_groups: + group_list = [] + for group in copp_groups: + config_dict = {} + group_dict = {} + copp_name = group.get('copp_name', None) + trap_priority = group.get('trap_priority', None) + trap_action = group.get('trap_action', None) + queue = group.get('queue', None) + cir = group.get('cir', None) + cbs = group.get('cbs', None) + + if copp_name: + config_dict['name'] = copp_name + group_dict['name'] = copp_name + if trap_priority: + config_dict['trap-priority'] = trap_priority + if trap_action: + config_dict['trap-action'] = trap_action + if queue: + config_dict['queue'] = queue + if cir: + config_dict['cir'] = cir + if cbs: + config_dict['cbs'] = cbs + if config_dict: + group_dict['config'] = config_dict + group_list.append(group_dict) + + if group_list: + copp_groups_dict = {'copp-group': group_list} + payload = {'openconfig-copp-ext:copp-groups': copp_groups_dict} + request = {'path': COPP_GROUPS_PATH, 'method': PATCH, 'data': payload} + + return request + + def get_delete_copp_requests(self, commands, have, is_delete_all): + requests = [] + + if is_delete_all: + copp_groups = commands.get('copp_groups', None) + if copp_groups: + for group in copp_groups: + copp_name = group.get('copp_name', None) + requests.append(self.get_delete_single_copp_group_request(copp_name)) + else: + copp_groups = commands.get('copp_groups', None) + if copp_groups: + for group in copp_groups: + copp_name = group.get('copp_name', None) + trap_priority = group.get('trap_priority', None) + trap_action = group.get('trap_action', None) + queue = group.get('queue', None) + cir = group.get('cir', None) + cbs = group.get('cbs', None) + + if have: + cfg_copp_groups = have.get('copp_groups', None) + if cfg_copp_groups: + for cfg_group in cfg_copp_groups: + cfg_copp_name = cfg_group.get('copp_name', None) + cfg_trap_priority = cfg_group.get('trap_priority', None) + cfg_trap_action = cfg_group.get('trap_action', None) + cfg_queue = cfg_group.get('queue', None) + cfg_cir = cfg_group.get('cir', None) + cfg_cbs = cfg_group.get('cbs', None) + + if copp_name == cfg_copp_name: + if trap_priority and trap_priority == cfg_trap_priority: + requests.append(self.get_delete_copp_groups_attr_request(copp_name, 'trap-priority')) + if trap_action and trap_action == cfg_trap_action: + err_msg = "Deletion of trap-action attribute is not supported." + self._module.fail_json(msg=err_msg, code=405) + requests.append(self.get_delete_copp_groups_attr_request(copp_name, 'trap-action')) + if queue and queue == cfg_queue: + requests.append(self.get_delete_copp_groups_attr_request(copp_name, 'queue')) + if cir and cir == cfg_cir: + requests.append(self.get_delete_copp_groups_attr_request(copp_name, 'cir')) + if cbs and cbs == cfg_cbs: + requests.append(self.get_delete_copp_groups_attr_request(copp_name, 'cbs')) + if not trap_priority and not trap_action and not queue and not cir and not cbs: + requests.append(self.get_delete_single_copp_group_request(copp_name)) + + return requests + + def get_delete_copp_groups_attr_request(self, copp_name, attr): + url = '%s/copp-group=%s/config/%s' % (COPP_GROUPS_PATH, copp_name, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_single_copp_group_request(self, copp_name): + url = '%s/copp-group=%s' % (COPP_GROUPS_PATH, copp_name) + request = {'path': url, 'method': DELETE} + + return request + + def filter_copp_groups(self, commands): + cfg_dict = {} + + if commands: + copp_groups = commands.get('copp_groups', None) + if copp_groups: + copp_groups_list = [] + for group in copp_groups: + copp_name = group.get('copp_name', None) + if copp_name not in reserved_copp_names: + copp_groups_list.append(group) + if copp_groups_list: + cfg_dict['copp_groups'] = copp_groups_list + + return cfg_dict + + def get_copp_groups_key(self, copp_groups_key): + return copp_groups_key.get('copp_name') + + def sort_lists_in_config(self, config): + if 'copp_groups' in config and config['copp_groups'] is not None: + config['copp_groups'].sort(key=self.get_copp_groups_key) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/dhcp_relay/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/dhcp_relay/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/dhcp_relay/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/dhcp_relay/dhcp_relay.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/dhcp_relay/dhcp_relay.py new file mode 100644 index 000000000..64d50fb5b --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/dhcp_relay/dhcp_relay.py @@ -0,0 +1,695 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_dhcp_relay class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + get_diff, + update_states, + normalize_interface_name, + get_normalize_interface_name, + remove_empties_from_list +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible.module_utils.connection import ConnectionError + +PATCH = 'patch' +DELETE = 'delete' + +DEFAULT_CIRCUIT_ID = '%p' +DEFAULT_MAX_HOP_COUNT = 10 +DEFAULT_POLICY_ACTION = 'discard' + +BOOL_TO_SELECT_VALUE = { + True: 'ENABLE', + False: 'DISABLE' +} + + +class Dhcp_relay(ConfigBase): + """ + The sonic_dhcp_relay class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'dhcp_relay', + ] + + dhcp_relay_intf_path = 'data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface={intf_name}' + dhcp_relay_intf_config_path = { + 'circuit_id': dhcp_relay_intf_path + '/agent-information-option/config/circuit-id', + 'link_select': dhcp_relay_intf_path + '/agent-information-option/config/openconfig-relay-agent-ext:link-select', + 'max_hop_count': dhcp_relay_intf_path + '/config/openconfig-relay-agent-ext:max-hop-count', + 'policy_action': dhcp_relay_intf_path + '/config/openconfig-relay-agent-ext:policy-action', + 'server_address': dhcp_relay_intf_path + '/config/helper-address={server_address}', + 'server_addresses_all': dhcp_relay_intf_path + '/config/helper-address', + 'source_interface': dhcp_relay_intf_path + '/config/openconfig-relay-agent-ext:src-intf', + 'vrf_name': dhcp_relay_intf_path + '/config/openconfig-relay-agent-ext:vrf', + 'vrf_select': dhcp_relay_intf_path + '/agent-information-option/config/openconfig-relay-agent-ext:vrf-select' + } + + dhcpv6_relay_intf_path = 'data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface={intf_name}' + dhcpv6_relay_intf_config_path = { + 'max_hop_count': dhcpv6_relay_intf_path + '/config/openconfig-relay-agent-ext:max-hop-count', + 'server_address': dhcpv6_relay_intf_path + '/config/helper-address={server_address}', + 'server_addresses_all': dhcpv6_relay_intf_path + '/config/helper-address', + 'source_interface': dhcpv6_relay_intf_path + '/config/openconfig-relay-agent-ext:src-intf', + 'vrf_name': dhcpv6_relay_intf_path + '/config/openconfig-relay-agent-ext:vrf', + 'vrf_select': dhcpv6_relay_intf_path + '/options/config/openconfig-relay-agent-ext:vrf-select' + } + + def __init__(self, module): + super(Dhcp_relay, self).__init__(module) + + def get_dhcp_relay_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + dhcp_relay_facts = facts['ansible_network_resources'].get('dhcp_relay') + if not dhcp_relay_facts: + return [] + return dhcp_relay_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = [] + + existing_dhcp_relay_facts = self.get_dhcp_relay_facts() + commands, requests = self.set_config(existing_dhcp_relay_facts) + if commands: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + result['changed'] = True + + changed_dhcp_relay_facts = self.get_dhcp_relay_facts() + + result['before'] = existing_dhcp_relay_facts + if result['changed']: + result['after'] = changed_dhcp_relay_facts + + result['commands'] = commands + result['warnings'] = warnings + return result + + def set_config(self, existing_dhcp_relay_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + state = self._module.params['state'] + want = self._module.params['config'] + if want: + # In state deleted, specific empty parameters are supported + if state != 'deleted': + want = remove_empties_from_list(want) + + normalize_interface_name(want, self._module) + for config in want: + if config.get('ipv4') and config['ipv4'].get('source_interface'): + config['ipv4']['source_interface'] = get_normalize_interface_name(config['ipv4']['source_interface'], self._module) + if config.get('ipv6') and config['ipv6'].get('source_interface'): + config['ipv6']['source_interface'] = get_normalize_interface_name(config['ipv6']['source_interface'], self._module) + else: + want = [] + + have = existing_dhcp_relay_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + state = self._module.params['state'] + if state == 'deleted': + commands, requests = self._state_deleted(want, have) + elif state == 'merged': + commands, requests = self._state_merged(want, have) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have) + elif state == 'overridden': + commands, requests = self._state_overridden(want, have) + return commands, requests + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = get_diff(want, have) + requests = self.get_modify_dhcp_dhcpv6_relay_requests(commands) + if commands and len(requests) > 0: + commands = update_states(commands, 'merged') + else: + commands = [] + + return commands, requests + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + requests = [] + if not want: + commands = have + requests.extend(self.get_delete_dhcp_dhcpv6_relay_completely_requests(commands)) + else: + commands = want + requests.extend(self.get_delete_dhcp_dhcpv6_relay_requests(commands, have)) + + if len(requests) == 0: + commands = [] + + if commands: + commands = update_states(commands, "deleted") + + return commands, requests + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + + del_commands, del_requests = self.get_delete_commands_requests_for_replaced_overridden(want, have, 'replaced') + if del_commands: + new_have = get_diff(have, del_commands) + commands = update_states(del_commands, 'deleted') + requests = del_requests + else: + new_have = have + + add_commands = get_diff(want, new_have) + if add_commands: + commands.extend(update_states(add_commands, 'replaced')) + requests.extend(self.get_modify_dhcp_dhcpv6_relay_requests(add_commands)) + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + + del_commands, del_requests = self.get_delete_commands_requests_for_replaced_overridden(want, have, 'overridden') + if del_commands: + new_have = get_diff(have, del_commands) + commands = update_states(del_commands, 'deleted') + requests = del_requests + else: + new_have = have + + add_commands = get_diff(want, new_have) + if add_commands: + commands.extend(update_states(add_commands, 'overridden')) + requests.extend(self.get_modify_dhcp_dhcpv6_relay_requests(add_commands)) + + return commands, requests + + def get_modify_dhcp_dhcpv6_relay_requests(self, commands): + """Get requests to modify DHCP and DHCPv6 relay configurations + for all interfaces specified by the commands + """ + requests = [] + + for command in commands: + if command.get('ipv4'): + requests.extend(self.get_modify_specific_dhcp_relay_param_requests(command)) + if command.get('ipv6'): + requests.extend(self.get_modify_specific_dhcpv6_relay_param_requests(command)) + + return requests + + def get_modify_specific_dhcp_relay_param_requests(self, command): + """Get requests to modify specific DHCP relay configurations + based on the command specified for the interface + """ + requests = [] + + name = command['name'] + ipv4 = command.get('ipv4') + if not ipv4: + return requests + + # Specifying appropriate order for merge to succeed + server_addresses = self.get_server_addresses(ipv4.get('server_addresses')) + if server_addresses: + payload = {'openconfig-relay-agent:helper-address': list(server_addresses)} + url = self.dhcp_relay_intf_config_path['server_addresses_all'].format(intf_name=name) + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if ipv4.get('vrf_name'): + payload = {'openconfig-relay-agent-ext:vrf': ipv4['vrf_name']} + url = self.dhcp_relay_intf_config_path['vrf_name'].format(intf_name=name) + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if ipv4.get('source_interface'): + payload = {'openconfig-relay-agent-ext:src-intf': ipv4['source_interface']} + url = self.dhcp_relay_intf_config_path['source_interface'].format(intf_name=name) + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if ipv4.get('link_select') is not None: + link_select = BOOL_TO_SELECT_VALUE[ipv4['link_select']] + payload = {'openconfig-relay-agent-ext:link-select': link_select} + url = self.dhcp_relay_intf_config_path['link_select'].format(intf_name=name) + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if ipv4.get('max_hop_count'): + payload = {'openconfig-relay-agent-ext:max-hop-count': ipv4['max_hop_count']} + url = self.dhcp_relay_intf_config_path['max_hop_count'].format(intf_name=name) + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if ipv4.get('vrf_select') is not None: + vrf_select = BOOL_TO_SELECT_VALUE[ipv4['vrf_select']] + payload = {'openconfig-relay-agent-ext:vrf-select': vrf_select} + url = self.dhcp_relay_intf_config_path['vrf_select'].format(intf_name=name) + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if ipv4.get('policy_action'): + payload = {'openconfig-relay-agent-ext:policy-action': ipv4['policy_action'].upper()} + url = self.dhcp_relay_intf_config_path['policy_action'].format(intf_name=name) + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if ipv4.get('circuit_id'): + payload = {'openconfig-relay-agent:circuit-id': ipv4['circuit_id']} + url = self.dhcp_relay_intf_config_path['circuit_id'].format(intf_name=name) + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + return requests + + def get_modify_specific_dhcpv6_relay_param_requests(self, command): + """Get requests to modify specific DHCPv6 relay configurations + based on the command specified for the interface + """ + requests = [] + + name = command['name'] + ipv6 = command.get('ipv6') + if not ipv6: + return requests + + # Specifying appropriate order for merge to succeed + server_addresses = self.get_server_addresses(ipv6.get('server_addresses')) + if server_addresses: + payload = {'openconfig-relay-agent:helper-address': list(server_addresses)} + url = self.dhcpv6_relay_intf_config_path['server_addresses_all'].format(intf_name=name) + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if ipv6.get('vrf_name'): + payload = {'openconfig-relay-agent-ext:vrf': ipv6['vrf_name']} + url = self.dhcpv6_relay_intf_config_path['vrf_name'].format(intf_name=name) + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if ipv6.get('source_interface'): + payload = {'openconfig-relay-agent-ext:src-intf': ipv6['source_interface']} + url = self.dhcpv6_relay_intf_config_path['source_interface'].format(intf_name=name) + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if ipv6.get('max_hop_count'): + payload = {'openconfig-relay-agent-ext:max-hop-count': ipv6['max_hop_count']} + url = self.dhcpv6_relay_intf_config_path['max_hop_count'].format(intf_name=name) + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if ipv6.get('vrf_select') is not None: + vrf_select = BOOL_TO_SELECT_VALUE[ipv6['vrf_select']] + payload = {'openconfig-relay-agent-ext:vrf-select': vrf_select} + url = self.dhcpv6_relay_intf_config_path['vrf_select'].format(intf_name=name) + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + return requests + + def get_delete_dhcp_dhcpv6_relay_completely_requests(self, have): + """Get requests to delete all existing DHCP and DHCPv6 relay + configurations in the chassis + """ + requests = [] + for cfg in have: + if cfg.get('ipv4'): + requests.append(self.get_delete_all_dhcp_relay_intf_request(cfg['name'])) + if cfg.get('ipv6'): + requests.append(self.get_delete_all_dhcpv6_relay_intf_request(cfg['name'])) + + return requests + + def get_delete_dhcp_dhcpv6_relay_requests(self, commands, have): + """Get requests to delete DHCP and DHCPv6 relay configurations + based on the commands specified + """ + requests = [] + + for command in commands: + intf_name = command['name'] + have_obj = next((cfg for cfg in have if cfg['name'] == intf_name), None) + if not have_obj: + continue + + have_ipv4 = have_obj.get('ipv4') + have_ipv6 = have_obj.get('ipv6') + + ipv4 = command.get('ipv4') + ipv6 = command.get('ipv6') + if not ipv4 and not ipv6: + if have_ipv4: + requests.append(self.get_delete_all_dhcp_relay_intf_request(intf_name)) + if have_ipv6: + requests.append(self.get_delete_all_dhcpv6_relay_intf_request(intf_name)) + else: + if ipv4 and have_ipv4: + requests.extend(self.get_delete_specific_dhcp_relay_param_requests(command, have_obj)) + if ipv6 and have_ipv6: + requests.extend(self.get_delete_specific_dhcpv6_relay_param_requests(command, have_obj)) + + return requests + + def get_delete_specific_dhcp_relay_param_requests(self, command, config, is_state_deleted=True): + """Get requests to delete specific DHCP relay configurations + based on the command specified for the interface + """ + requests = [] + + name = command['name'] + ipv4 = command.get('ipv4') + have_ipv4 = config.get('ipv4') + if not ipv4 or not have_ipv4: + return requests + + server_addresses = self.get_server_addresses(ipv4.get('server_addresses')) + have_server_addresses = self.get_server_addresses(have_ipv4.get('server_addresses')) + + # Delete all DHCP relay config for an interface, if only + # a single server address with no value is specified. + # + # This "special" YAML sequence is supported to provide + # "delete all AF parameters" functionality despite the Ansible + # infrastructure limitations that prevent use of a simpler + # syntax for deleting an entire AF parameter dictionary. + if (ipv4.get('server_addresses') and len(ipv4.get('server_addresses')) + and not server_addresses): + requests.append(self.get_delete_all_dhcp_relay_intf_request(name)) + return requests + + del_server_addresses = have_server_addresses.intersection(server_addresses) + if del_server_addresses: + # Deleting all DHCP server addresses configured on an + # interface automatically removes all DHCP relay config in + # that interface. Therefore, seperate requests to delete + # other DHCP relay configs are not required. + if is_state_deleted and len(del_server_addresses) == len(have_server_addresses): + requests.append(self.get_delete_all_dhcp_relay_intf_request(name)) + return requests + + for addr in del_server_addresses: + url = self.dhcp_relay_intf_config_path['server_address'].format(intf_name=name, server_address=addr) + requests.append({'path': url, 'method': DELETE}) + + # Specifying appropriate order for deletion to succeed + if ipv4.get('link_select') is not None and have_ipv4.get('link_select'): + url = self.dhcp_relay_intf_config_path['link_select'].format(intf_name=name) + requests.append({'path': url, 'method': DELETE}) + + if (ipv4.get('source_interface') and have_ipv4.get('source_interface') + and ipv4['source_interface'] == have_ipv4['source_interface']): + url = self.dhcp_relay_intf_config_path['source_interface'].format(intf_name=name) + requests.append({'path': url, 'method': DELETE}) + + if (ipv4.get('max_hop_count') and have_ipv4.get('max_hop_count') + and ipv4['max_hop_count'] == have_ipv4['max_hop_count'] + and have_ipv4['max_hop_count'] != DEFAULT_MAX_HOP_COUNT): + url = self.dhcp_relay_intf_config_path['max_hop_count'].format(intf_name=name) + requests.append({'path': url, 'method': DELETE}) + + if ipv4.get('vrf_select') is not None and have_ipv4.get('vrf_select'): + url = self.dhcp_relay_intf_config_path['vrf_select'].format(intf_name=name) + requests.append({'path': url, 'method': DELETE}) + + if (ipv4.get('policy_action') and have_ipv4.get('policy_action') + and ipv4['policy_action'] == have_ipv4['policy_action'] + and have_ipv4['policy_action'] != DEFAULT_POLICY_ACTION): + url = self.dhcp_relay_intf_config_path['policy_action'].format(intf_name=name) + requests.append({'path': url, 'method': DELETE}) + + if (ipv4.get('circuit_id') and have_ipv4.get('circuit_id') + and ipv4['circuit_id'] == have_ipv4['circuit_id'] + and have_ipv4['circuit_id'] != DEFAULT_CIRCUIT_ID): + url = self.dhcp_relay_intf_config_path['circuit_id'].format(intf_name=name) + requests.append({'path': url, 'method': DELETE}) + + return requests + + def get_delete_specific_dhcpv6_relay_param_requests(self, command, have, is_state_deleted=True): + """Get requests to delete specific DHCPv6 relay configurations + based on the command specified for the interface + """ + requests = [] + + name = command['name'] + ipv6 = command.get('ipv6') + have_ipv6 = have.get('ipv6') + if not ipv6 or not have_ipv6: + return requests + + server_addresses = self.get_server_addresses(ipv6.get('server_addresses')) + have_server_addresses = self.get_server_addresses(have_ipv6.get('server_addresses')) + + # Delete all DHCPv6 relay config for an interface, if only + # a single server address with no value is specified. + # + # This "special" YAML sequence is supported to provide + # "delete all AF parameters" functionality despite the Ansible + # infrastructure limitations that prevent use of a simpler + # syntax for deleting an entire AF parameter dictionary. + if (ipv6.get('server_addresses') and len(ipv6.get('server_addresses')) + and not server_addresses): + requests.append(self.get_delete_all_dhcpv6_relay_intf_request(name)) + return requests + + del_server_addresses = have_server_addresses.intersection(server_addresses) + if del_server_addresses: + # Deleting all DHCPv6 server addresses configured on an + # interface automatically removes all DHCPv6 relay config + # in that interface. Therefore, seperate requests to delete + # other DHCPv6 relay configs are not required. + if is_state_deleted and len(del_server_addresses) == len(have_server_addresses): + requests.append(self.get_delete_all_dhcpv6_relay_intf_request(name)) + return requests + + for addr in del_server_addresses: + url = self.dhcpv6_relay_intf_config_path['server_address'].format(intf_name=name, server_address=addr) + requests.append({'path': url, 'method': DELETE}) + + # Specifying appropriate order for deletion to succeed + if (ipv6.get('source_interface') and have_ipv6.get('source_interface') + and ipv6['source_interface'] == have_ipv6['source_interface']): + url = self.dhcpv6_relay_intf_config_path['source_interface'].format(intf_name=name) + requests.append({'path': url, 'method': DELETE}) + + if (ipv6.get('max_hop_count') and have_ipv6.get('max_hop_count') + and ipv6['max_hop_count'] == have_ipv6['max_hop_count'] + and have_ipv6['max_hop_count'] != DEFAULT_MAX_HOP_COUNT): + url = self.dhcpv6_relay_intf_config_path['max_hop_count'].format(intf_name=name) + requests.append({'path': url, 'method': DELETE}) + + if ipv6.get('vrf_select') is not None and have_ipv6.get('vrf_select'): + url = self.dhcpv6_relay_intf_config_path['vrf_select'].format(intf_name=name) + requests.append({'path': url, 'method': DELETE}) + + return requests + + def get_delete_all_dhcp_relay_intf_request(self, intf_name): + """Get request to delete all DHCP relay configurations in the + specified interface + """ + return {'path': self.dhcp_relay_intf_config_path['server_addresses_all'].format(intf_name=intf_name), 'method': DELETE} + + def get_delete_all_dhcpv6_relay_intf_request(self, intf_name): + """Get request to delete all DHCPv6 relay configurations in the + specified interface + """ + return {'path': self.dhcpv6_relay_intf_config_path['server_addresses_all'].format(intf_name=intf_name), 'method': DELETE} + + def get_delete_commands_requests_for_replaced_overridden(self, want, have, state): + """Returns the commands and requests necessary to remove applicable + current configurations when state is replaced or overridden + """ + default_value = { + 'circuit_id': DEFAULT_CIRCUIT_ID, + 'max_hop_count': DEFAULT_MAX_HOP_COUNT, + 'policy_action': DEFAULT_POLICY_ACTION + } + commands = [] + requests = [] + if not have: + return commands, requests + + for conf in have: + intf_name = conf['name'] + ipv4_conf = conf.get('ipv4') + ipv6_conf = conf.get('ipv6') + + match_obj = next((cmd for cmd in want if cmd['name'] == intf_name), None) + if not match_obj: + # Delete all DHCP and DHCPv6 relay config for interfaces, + # that are not specified in overridden. + if state == 'overridden': + commands.append(conf) + if ipv4_conf: + requests.append(self.get_delete_all_dhcp_relay_intf_request(intf_name)) + if ipv6_conf: + requests.append(self.get_delete_all_dhcpv6_relay_intf_request(intf_name)) + continue + + command = {'name': intf_name} + if ipv4_conf: + match_ipv4 = match_obj.get('ipv4') + # Delete all DHCP relay config for an interface if not specified + if not match_ipv4: + command['ipv4'] = ipv4_conf + requests.append(self.get_delete_all_dhcp_relay_intf_request(intf_name)) + else: + have_server_addresses = self.get_server_addresses(ipv4_conf.get('server_addresses')) + server_addresses = self.get_server_addresses(match_ipv4.get('server_addresses')) + + # Delete all DHCP relay config for an interface, if + # all existing server addresses are to be replaced + # or if the VRF is to be removed. + if (not have_server_addresses.intersection(server_addresses) + or (ipv4_conf.get('vrf_name') and match_ipv4.get('vrf_name') is None)): + command['ipv4'] = ipv4_conf + requests.append(self.get_delete_all_dhcp_relay_intf_request(intf_name)) + else: + ipv4_command = {} + del_server_addresses = have_server_addresses.difference(server_addresses) + if del_server_addresses: + ipv4_command['server_addresses'] = [] + for address in del_server_addresses: + ipv4_command['server_addresses'].append({'address': address}) + + for option in ('source_interface', 'link_select', 'vrf_select'): + if ipv4_conf.get(option) and match_ipv4.get(option) is None: + ipv4_command[option] = ipv4_conf[option] + + for option in ('circuit_id', 'max_hop_count', 'policy_action'): + if (ipv4_conf.get(option) and match_ipv4.get(option) is None + and ipv4_conf[option] != default_value[option]): + ipv4_command[option] = ipv4_conf[option] + + if ipv4_command: + command['ipv4'] = ipv4_command + requests.extend(self.get_delete_specific_dhcp_relay_param_requests(command, command, False)) + + if ipv6_conf: + match_ipv6 = match_obj.get('ipv6') + # Delete all DHCPv6 relay config for an interface if not specified + if not match_ipv6: + command['ipv6'] = ipv6_conf + requests.append(self.get_delete_all_dhcpv6_relay_intf_request(intf_name)) + else: + have_server_addresses = self.get_server_addresses(ipv6_conf.get('server_addresses')) + server_addresses = self.get_server_addresses(match_ipv6.get('server_addresses')) + + # Delete all DHCPv6 relay config for an interface, if + # all existing server addresses are to be replaced + # or if the VRF is to be removed. + if (not have_server_addresses.intersection(server_addresses) + or (ipv6_conf.get('vrf_name') and match_ipv6.get('vrf_name') is None)): + command['ipv6'] = ipv6_conf + requests.append(self.get_delete_all_dhcpv6_relay_intf_request(intf_name)) + else: + ipv6_command = {} + del_server_addresses = have_server_addresses.difference(server_addresses) + if del_server_addresses: + ipv6_command['server_addresses'] = [] + for address in del_server_addresses: + ipv6_command['server_addresses'].append({'address': address}) + + for option in ('source_interface', 'vrf_select'): + if ipv6_conf.get(option) and match_ipv6.get(option) is None: + ipv6_command[option] = ipv6_conf[option] + + if (ipv6_conf.get('max_hop_count') and match_ipv6.get('max_hop_count') is None + and ipv6_conf['max_hop_count'] != default_value['max_hop_count']): + ipv6_command['max_hop_count'] = ipv6_conf['max_hop_count'] + + if ipv6_command: + command['ipv6'] = ipv6_command + requests.extend(self.get_delete_specific_dhcpv6_relay_param_requests(command, command, False)) + + if command.get('ipv4') or command.get('ipv6'): + commands.append(command) + + return commands, requests + + @staticmethod + def get_server_addresses(server_addresses_dict): + """Get a set of server addresses available in the given + server_addresses dict + """ + server_addresses = set() + if not server_addresses_dict: + return server_addresses + + for addr in server_addresses_dict: + if addr.get('address'): + server_addresses.add(addr['address']) + + return server_addresses diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/dhcp_snooping/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/dhcp_snooping/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/dhcp_snooping/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/dhcp_snooping/dhcp_snooping.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/dhcp_snooping/dhcp_snooping.py new file mode 100644 index 000000000..d3c3233b1 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/dhcp_snooping/dhcp_snooping.py @@ -0,0 +1,649 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_dhcp_snooping class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + remove_empties, + validate_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + get_diff, + update_states +) + + +class Dhcp_snooping(ConfigBase): + """ + The sonic_dhcp_snooping class + """ + test_keys = [ + {'afis': {'afi': ''}}, + {"source_bindings": {"mac_addr": ""}}, + {"trusted": {"intf_name": ""}} + ] + + ipv4_key = 'ipv4' + ipv6_key = 'ipv6' + + delete_method_value = 'delete' + patch_method_value = 'patch' + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'dhcp_snooping', + ] + + dhcp_snooping_uri = 'data/openconfig-dhcp-snooping:dhcp-snooping' + config_uri = dhcp_snooping_uri + '/config' + enable_uri = config_uri + '/dhcpv{v}-admin-enable' + verify_mac_uri = config_uri + '/dhcpv{v}-verify-mac-address' + binding_uri = dhcp_snooping_uri + '-static-binding/entry' + trusted_uri = 'data/openconfig-interfaces:interfaces/interface={name}/dhcpv{v}-snooping-trust/config/dhcpv{v}-snooping-trust' + vlans_uri = 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST={vlan_name}/dhcpv{v}_snooping_enable' + + def __init__(self, module): + super(Dhcp_snooping, self).__init__(module) + + def get_dhcp_snooping_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, + self.gather_network_resources) + dhcp_snooping_facts = facts['ansible_network_resources'].get('dhcp_snooping') + if not dhcp_snooping_facts: + return [] + return dhcp_snooping_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = list() + + existing_dhcp_snooping_facts = self.get_dhcp_snooping_facts() + commands, requests = self.set_config(existing_dhcp_snooping_facts) + if commands: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + result['changed'] = True + result['commands'] = commands + + changed_dhcp_snooping_facts = self.get_dhcp_snooping_facts() + + result['before'] = existing_dhcp_snooping_facts + if result['changed']: + result['after'] = changed_dhcp_snooping_facts + + result['warnings'] = warnings + return result + + def set_config(self, existing_dhcp_snooping_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + have = existing_dhcp_snooping_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + afis = {} + want = self.remove_none(want) + + # just in case weird arguments passed + if want is None: + want = {} + if have is None: + have = {} + + if want.get('afis') is not None: + for want_afi in want.get('afis'): + if want_afi.get('afi') == self.ipv4_key: + afis['want_ipv4'] = want_afi + elif want_afi.get('afi') == self.ipv6_key: + afis['want_ipv6'] = want_afi + + if have.get('afis') is not None: + for have_afi in have.get('afis'): + if have_afi.get('afi') == self.ipv4_key: + afis['have_ipv4'] = have_afi + elif have_afi.get('afi') == self.ipv6_key: + afis['have_ipv6'] = have_afi + + state = self._module.params['state'] + if state == 'merged': + commands, requests = self._state_merged(want, have, afis) + elif state == 'deleted': + commands, requests = self._state_deleted(want, have, afis) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have, afis) + elif state == 'overridden': + commands, requests = self._state_overridden(want, have, afis) + + return commands, requests + + def _state_merged(self, want, have, afis): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + want = remove_empties(want) + self.validate_config({"config": want}) + + commands = get_diff(want, have, test_keys=self.test_keys) + self.prep_replaced_to_merge(commands, afis) + requests = self.get_modify_requests(commands) + + if commands and len(requests) > 0: + commands = update_states(commands, "merged") + else: + commands = [] + + return commands, requests + + def _state_deleted(self, want, have, afis): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + requests = [] + if not have or not have.get('afis'): + # nothing that could be deleted + commands = [] + elif not want or not want.get('afis'): + # want is empty, meaning want to delete all config + # afis parameter only stores the on device config at this point + commands, requests = self.get_delete_all_have_requests(afis) + else: + # some mix of settings specified in both + commands, requests = self.get_delete_specific_requests(afis) + + if commands and len(requests) > 0: + commands = update_states(commands, "deleted") + else: + commands = [] + return commands, requests + + def _state_overridden(self, want, have, afis): + """ The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + if not want: + return commands, requests + + # Determine if there is any configuration specified in the playbook + # that is not contained in the current configuration. + diff_requested = get_diff(want, have, self.test_keys) + diff_requested_keyed = {} + for afi in diff_requested.get("afis", []): + diff_requested_keyed[afi["afi"]] = afi + + # Determine if there is anything already configured that is not + # specified in the playbook. + diff_unwanted = get_diff(have, want, self.test_keys) + + # Idempotency check: If the configuration already matches the + # requested configuration with no extra attributes, no + # commands should be executed on the device. + if not diff_requested and not diff_unwanted: + return commands, requests + + used_commands_per_afi = [] + commands = [] + + for diff_unwanted_afi in diff_unwanted.get("afis", []): + # enabled and verify_mac can't be deleted from config, only set to default. + # so in the case they appear in both the "need to delete" and "need to change", keeping in both results in double requests + if "enabled" in diff_unwanted_afi and "enabled" in diff_requested_keyed.get(diff_unwanted_afi["afi"], {}): + del diff_unwanted_afi["enabled"] + if "verify_mac" in diff_unwanted_afi and "verify_mac" in diff_requested_keyed.get(diff_unwanted_afi["afi"], {}): + del diff_unwanted_afi["verify_mac"] + afi_commands, afi_requests = self.get_delete_specific_afi_fields_requests(diff_unwanted_afi, afis["have_" + diff_unwanted_afi["afi"]]) + if afi_commands: + afi_commands["afi"] = diff_unwanted_afi["afi"] + used_commands_per_afi.append(afi_commands) + requests.extend(afi_requests) + if len(used_commands_per_afi): + commands = {"afis": used_commands_per_afi} + if commands and len(requests) > 0: + commands = update_states(commands, "deleted") + + # apply the things to add or change + # need to add back in the source bindings since the diff could pick up only the different values in a source binding + self.prep_replaced_to_merge(diff_requested, afis) + overridden_requests = self.get_modify_requests(diff_requested) + requests.extend(overridden_requests) + if diff_requested and len(overridden_requests) > 0: + diff_requested = update_states(diff_requested, "overridden") + commands.extend(diff_requested) + return commands, requests + + def _state_replaced(self, want, have, afis): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + + # do needed deletes + commands, requests = self.get_delete_replaced_groupings(afis) + if commands and len(requests) > 0: + commands = update_states(commands, "deleted") + # getting what needs to be added/changed after deletes + # need to add back in the source bindings since the diff could pick up only the different values in a source binding + diff = get_diff(want, have, self.test_keys) + self.prep_replaced_to_merge(diff, afis) + merged_commands = diff + + replaced_requests = self.get_modify_requests(merged_commands) + requests.extend(replaced_requests) + if merged_commands and len(replaced_requests) > 0: + merged_commands = update_states(merged_commands, "replaced") + commands.extend(merged_commands) + return commands, requests + + def validate_config(self, config): + '''validate passed in config is argspec compliant. Also does checks on values in ranges that ansible might not do''' + validated_config = validate_config(self._module.argument_spec, config) + return validated_config + + def remove_none(self, config): + '''goes through nested dictionary items and removes any keys that have None as value. + enables using empty list/dict to specify clear everything for that section and differentiate this + 'clear everything' case from when no value was given + remove_empties in ansible utils will remove empty lists and dicts as well as None''' + if isinstance(config, dict): + for k, v in list(config.items()): + if v is None: + del config[k] + else: + self.remove_none(v) + elif isinstance(config, list): + for item in list(config): + if item is None: + config.remove(item) + self.remove_none(item) + return config + + def get_modify_requests(self, to_modify_config): + '''builds and returns requests to add in given config + + :param to_modify: dictionary specifying what to modify in argspec format. expected to be at root level of config''' + requests = [] + + if to_modify_config.get('afis') is not None: + for afi_config in to_modify_config.get('afis'): + requests.extend(self.get_single_afi_modify_requests(afi_config)) + + return requests + + def get_single_afi_modify_requests(self, to_modify_afi): + """build requests to modify a single afi family. Uses passed in config to find which family and what to change + + :param to_modify_afi: dictionary specifying the config to add/change in argspec format. expected to be for a single afi + :param v: version number of afi to modify + """ + requests = [] + v = self.afi_to_vnum(to_modify_afi) + + if to_modify_afi.get('enabled') is not None: + payload = {'openconfig-dhcp-snooping:dhcpv{v}-admin-enable'.format(v=v): to_modify_afi['enabled']} + uri = self.enable_uri.format(v=v) + requests.append({'path': uri, 'method': self.patch_method_value, 'data': payload}) + + if to_modify_afi.get('verify_mac') is not None: + payload = {'openconfig-dhcp-snooping:dhcpv{v}-verify-mac-address'.format(v=v): to_modify_afi['verify_mac']} + uri = self.verify_mac_uri.format(v=v) + requests.append({'path': uri, 'method': self.patch_method_value, 'data': payload}) + + if to_modify_afi.get('trusted'): + for intf in to_modify_afi.get('trusted'): + intf_name = intf.get("intf_name") + if intf_name: + payload = {'openconfig-interfaces:dhcpv{v}-snooping-trust'.format(v=v): 'ENABLE'} + uri = self.trusted_uri.format(name=intf_name, v=v) + requests.append({'path': uri, 'method': self.patch_method_value, 'data': payload}) + + if to_modify_afi.get('vlans'): + for vlan_id in to_modify_afi.get('vlans'): + payload = {'sonic-vlan:dhcpv{v}_snooping_enable'.format(v=v): 'enable'} + uri = self.vlans_uri.format(vlan_name='Vlan' + vlan_id, v=v) + requests.append({'path': uri, 'method': self.patch_method_value, 'data': payload}) + + if to_modify_afi.get('source_bindings'): + entries = [] + for entry in to_modify_afi.get('source_bindings'): + if entry.get('mac_addr'): + entries.append({ + 'mac': entry.get('mac_addr'), + 'iptype': 'ipv' + str(v), + 'config': { + 'mac': entry.get('mac_addr'), + 'iptype': 'ipv' + str(v), + 'vlan': "Vlan" + str(entry.get('vlan_id')), + 'interface': entry.get('intf_name'), + 'ip': entry.get('ip_addr'), + } + }) + + payload = {'openconfig-dhcp-snooping:entry': entries} + uri = self.binding_uri + requests.append({'path': uri, 'method': self.patch_method_value, 'data': payload}) + + return requests + + def get_delete_all_have_requests(self, afis): + '''creates and builds list of requests to delete all current dhcp snooping config for ipv4 and ipv6''' + modified_afi_commands = [] + requests = [] + ipv4_commands, ipv4_requests = self.get_delete_specific_afi_fields_requests(afis.get('have_ipv4'), afis.get('have_ipv4')) + requests.extend(ipv4_requests) + if ipv4_commands: + ipv4_commands["afi"] = afis.get('have_ipv4')["afi"] + modified_afi_commands.append(ipv4_commands) + ipv6_commands, ipv6_requests = self.get_delete_specific_afi_fields_requests(afis.get('have_ipv6'), afis.get('have_ipv6')) + requests.extend(ipv6_requests) + if ipv6_commands: + ipv6_commands["afi"] = afis.get('have_ipv6')["afi"] + modified_afi_commands.append(ipv6_commands) + + sent_commands = [] + if modified_afi_commands: + sent_commands = {"afis": modified_afi_commands} + + return sent_commands, requests + + def get_delete_specific_requests(self, afis): + '''creates and returns list of requests to delete afi settings. + Checks if clearing settings for a ip family or just matching fields in config''' + modified_afi_commands = [] + requests = [] + + want_ipv4 = afis.get('want_ipv4') + want_ipv6 = afis.get('want_ipv6') + have_ipv4 = afis.get('have_ipv4') + have_ipv6 = afis.get('have_ipv6') + + if want_ipv4: + if want_ipv4.keys() == set(["afi"]): + # just afi key supplied, interpreting this as delete all config for that afi + ipv4_commands, ipv4_requests = self.get_delete_specific_afi_fields_requests(have_ipv4, have_ipv4) + else: + ipv4_commands, ipv4_requests = self.get_delete_specific_afi_fields_requests(want_ipv4, have_ipv4) + requests.extend(ipv4_requests) + if ipv4_commands: + ipv4_commands["afi"] = want_ipv4["afi"] + modified_afi_commands.append(ipv4_commands) + if want_ipv6: + if want_ipv6.keys() == set(["afi"]): + ipv6_commands, ipv6_requests = self.get_delete_specific_afi_fields_requests(have_ipv6, have_ipv6) + else: + ipv6_commands, ipv6_requests = self.get_delete_specific_afi_fields_requests(want_ipv6, have_ipv6) + requests.extend(ipv6_requests) + if ipv6_commands: + ipv6_commands["afi"] = want_ipv6["afi"] + modified_afi_commands.append(ipv6_commands) + + sent_commands = [] + if modified_afi_commands: + sent_commands = {"afis": modified_afi_commands} + + return sent_commands, requests + + def get_delete_specific_afi_fields_requests(self, want_afi, have_afi): + '''creates and builds list of requests for deleting some fields of dhcp snooping config for + one ip family. Each field checked and deleted independently from each other depending on if + it is specified in playbook and matches with current config''' + sent_commands = {} + requests = [] + + if want_afi.get('enabled') is True and have_afi.get('enabled') is True: + # only need to send a request if want from playbook is set to non default value and the setting currently configured is non default + sent_commands.update({"enabled": want_afi.get("enabled")}) + requests.extend(self.get_delete_enabled_request(want_afi)) + if want_afi.get('verify_mac') is False and have_afi.get('verify_mac') is False: + sent_commands.update({"verify_mac": want_afi.get("verify_mac")}) + requests.extend(self.get_delete_verify_mac_request(want_afi)) + if want_afi.get('vlans') is not None and have_afi.get('vlans') is not None and have_afi.get("vlans") != []: + # gathering list of vlans to be deleted. this section also handles cases where empty list of vlans is passed in + # which means delete all vlans + to_delete_vlans = have_afi["vlans"] + if len(want_afi["vlans"]) > 0: + to_delete_vlans = list(set(have_afi.get("vlans", [])).intersection(set(want_afi.get("vlans", [])))) + to_delete = {"afi": want_afi["afi"], "vlans": to_delete_vlans} + if len(to_delete["vlans"]): + sent_commands.update({"vlans": deepcopy(to_delete_vlans)}) + requests.extend(self.get_delete_vlans_requests(to_delete)) + if want_afi.get('trusted') is not None and have_afi.get('trusted') is not None and have_afi.get('trusted') != []: + # gathering list of interfaces to be deleted. this section also handles cases where empty list of interfaces is passed in which + # means delete all trusted interfaces + to_delete_trusted = have_afi["trusted"] + if len(want_afi["trusted"]) > 0: + to_delete_trusted = want_afi["trusted"] + # removing interfaces that don't exist on device + for intf in list(to_delete_trusted): + if intf not in have_afi["trusted"]: + to_delete_trusted.remove(intf) + to_delete = {"afi": want_afi["afi"], "trusted": to_delete_trusted} + if len(to_delete["trusted"]): + sent_commands.update({"trusted": deepcopy(to_delete_trusted)}) + requests.extend(self.get_delete_trusted_requests(to_delete)) + if want_afi.get('source_bindings') is not None and have_afi.get('source_bindings') is not None and have_afi.get('source_bindings') != []: + # gathering list of source bindings to be deleted. this section also handles cases where empty list of bindings is passed in which + # means delete all trusted bindings + to_delete_bindings = have_afi["source_bindings"] + if len(want_afi["source_bindings"]) > 0: + to_delete_bindings = want_afi["source_bindings"] + # removing bindings that don't exist on device + existing_keys = [binding["mac_addr"] for binding in have_afi["source_bindings"]] + for binding in list(to_delete_bindings): + if binding["mac_addr"] not in existing_keys: + # need to check by the key since can have two different versions of same binding + to_delete_bindings.remove(binding) + to_delete = {"afi": want_afi["afi"], "source_bindings": to_delete_bindings} + if len(to_delete["source_bindings"]): + sent_commands.update({"source_bindings": deepcopy(to_delete_bindings)}) + requests.extend(self.get_delete_specific_source_bindings_requests(to_delete)) + + return sent_commands, requests + + def get_delete_enabled_request(self, afi): + '''makes and returns request to "delete" aka reset to default the enabled setting for one afi family. returns as a list''' + payload = {'openconfig-dhcp-snooping:dhcpv{v}-admin-enable'.format(v=self.afi_to_vnum(afi)): False} + return [{'path': self.enable_uri.format(v=self.afi_to_vnum(afi)), 'method': self.patch_method_value, 'data': payload}] + + def get_delete_verify_mac_request(self, afi): + '''makes and returns request to "delete" aka reset to default the config for one afi family's verify mac setting''' + payload = {'openconfig-dhcp-snooping:dhcpv{v}-verify-mac-address'.format(v=self.afi_to_vnum(afi)): True} + return [{'path': self.verify_mac_uri.format(v=self.afi_to_vnum(afi)), 'method': self.patch_method_value, 'data': payload}] + + def get_delete_vlans_requests(self, afi): + '''makes and returns request to delete the given vlans for the given afi faimily. + input expected as a dictionary of form {"afi": <ip_version>, "vlans": <list_of_vlans>}''' + requests = [] + if afi.get('vlans'): + for vlan_id in afi.get('vlans'): + requests.append({ + 'path': self.vlans_uri.format(vlan_name='Vlan' + vlan_id, v=self.afi_to_vnum(afi)), + 'method': self.delete_method_value + }) + return requests + + def get_delete_trusted_requests(self, afi): + '''makes and returns request to delete the given trusted interfaces for the given afi faimily. + input expected as a dictionary of form {"afi": <ip_version>, "trusted": [{"intf_name": <name>}...]}''' + requests = [] + if afi.get('trusted'): + for intf in afi.get('trusted'): + intf_name = intf.get('intf_name') + if intf_name: + requests.append({ + 'path': self.trusted_uri.format(name=intf_name, v=self.afi_to_vnum(afi)), + 'method': self.delete_method_value + }) + return requests + + def get_delete_all_source_bindings_request(self): + '''creates request to delete the source bindings list, which clears all bindings from both families''' + return [{'path': self.binding_uri, 'method': self.delete_method_value}] + + def get_delete_specific_source_bindings_requests(self, afi): + '''creates and builds a list of requests to delete the source bindings listed in the given afi family + input expected as a dictionary of form to_delete = {"afi": <ip_version>, "source_bindings": <list of source_bindings>}''' + requests = [] + for entry in afi.get('source_bindings'): + if entry.get('mac_addr'): + requests.append({ + 'path': self.binding_uri + '={mac},{ipv}'.format(mac=entry.get('mac_addr'), ipv=afi.get('afi')), + 'method': self.delete_method_value + }) + return requests + + def get_delete_individual_source_bindings_requests(self, afi, entry): + '''create a request to delete the given source binding entry and address family specified + by afi''' + return [{'path': self.binding_uri + '={mac},{ipv}'.format(mac=entry.get('mac_addr'), ipv=afi.get('afi')), 'method': self.delete_method_value}] + + def get_delete_replaced_groupings(self, afis): + '''builds list of requests to handle replaced state for both address families''' + modified_afi_commands = [] + requests = [] + + want_ipv4 = afis.get('want_ipv4') + have_ipv4 = afis.get('have_ipv4') + want_ipv6 = afis.get('want_ipv6') + have_ipv6 = afis.get('have_ipv6') + + if want_ipv4 and have_ipv4: + ipv4_commands, ipv4_requests = self.get_delete_replaced_groupings_afi(want_ipv4, have_ipv4) + requests.extend(ipv4_requests) + if ipv4_commands: + ipv4_commands["afi"] = want_ipv4["afi"] + modified_afi_commands.append(ipv4_commands) + if want_ipv6 and have_ipv6: + ipv6_commands, ipv6_requests = self.get_delete_replaced_groupings_afi(want_ipv6, have_ipv6) + requests.extend(ipv6_requests) + if ipv6_commands: + ipv6_commands["afi"] = want_ipv6["afi"] + modified_afi_commands.append(ipv6_commands) + + sent_commands = [] + if modified_afi_commands: + sent_commands = {"afis": modified_afi_commands} + + return sent_commands, requests + + def get_delete_replaced_groupings_afi(self, want_afi, have_afi): + '''creates and builds a list of requests to handle all parts that need to be deleted + while handling the replaced state for an address family''' + sent_commands = {} + requests = [] + diff_requested = get_diff(have_afi, want_afi, self.test_keys) + + if diff_requested.get("vlans") and "vlans" in want_afi: + # delete any vlans that are different + to_delete = {"afi": have_afi["afi"], "vlans": diff_requested["vlans"]} + sent_commands["vlans"] = deepcopy(diff_requested["vlans"]) + requests.extend(self.get_delete_vlans_requests(to_delete)) + if diff_requested.get('trusted') and 'trusted' in want_afi: + # delete anything that has a difference, covers things that are + # in have but not want and things in both but modified + to_delete = {"afi": have_afi["afi"], "trusted": diff_requested["trusted"]} + sent_commands["trusted"] = deepcopy(diff_requested["trusted"]) + requests.extend(self.get_delete_trusted_requests(to_delete)) + if diff_requested.get('source_bindings') and 'source_bindings' in want_afi: + # assuming source bindings considered a replaceable subsection ie the list afterwards + # should look exactly like what was passed into want + if want_afi["source_bindings"] == []: + # replaced told want to replace existing with blank list, only thing to do is delete existing bindings for family + sent_commands["source_bindings"] = deepcopy(have_afi["source_bindings"]) + requests.extend(self.get_delete_specific_source_bindings_requests(have_afi)) + else: + sent_commands["source_bindings"] = deepcopy(diff_requested["source_bindings"]) + for entry in diff_requested["source_bindings"]: + requests.extend(self.get_delete_individual_source_bindings_requests(have_afi, entry)) + return sent_commands, requests + + def prep_replaced_to_merge(self, diff, afis): + '''preps results from a get diff for use in merging. needed for source bindings to have all data needed. get diff only returns the fields that + are different in each source binding when all data for it is needed instead. Fills in each source binding in diff with what is found for it in afis''' + if not diff or not diff.get("afis"): + return {} + for diff_afi in diff["afis"]: + if "source_bindings" in diff_afi: + for binding in diff_afi["source_bindings"]: + binding.update(self.match_binding(binding["mac_addr"], afis["want_" + diff_afi["afi"]]["source_bindings"])) + + @staticmethod + def match_binding(mac_addr, bindings): + for binding in bindings: + if binding["mac_addr"] == mac_addr: + return binding + return {} + + @staticmethod + def afi_to_vnum(afi): + if afi.get('afi') == 'ipv6': + return '6' + else: + return '4' diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/interfaces/interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/interfaces/interfaces.py index acf985ebf..33607817b 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/interfaces/interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/interfaces/interfaces.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# © Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -18,6 +18,15 @@ try: except ImportError: from urllib.parse import quote +""" +The use of natsort causes sanity error due to it is not available in python version currently used. +When natsort becomes available, the code here and below using it will be applied. +from natsort import ( + natsorted, + ns +) +""" +from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -33,14 +42,22 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.interfaces_util import ( build_interfaces_create_request, + retrieve_default_intf_speed, + retrieve_port_group_interfaces ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( get_diff, update_states, - normalize_interface_name + normalize_interface_name, + remove_empties_from_list +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + get_new_config, + get_formatted_config_diff ) from ansible.module_utils._text import to_native from ansible.module_utils.connection import ConnectionError +import re import traceback LIB_IMP_ERR = None @@ -53,8 +70,54 @@ except Exception as e: ERR_MSG = to_native(e) LIB_IMP_ERR = traceback.format_exc() +GET = 'get' PATCH = 'patch' DELETE = 'delete' +url = 'data/openconfig-interfaces:interfaces/interface=%s' +eth_conf_url = "/openconfig-if-ethernet:ethernet/config" + +port_num_regex = re.compile(r'[\d]{1,4}$') +non_eth_attribute = ('description', 'mtu', 'enabled') +eth_attribute = ('description', 'mtu', 'enabled', 'auto_negotiate', 'speed', 'fec', 'advertised_speed') + +attributes_default_value = { + "description": '', + "mtu": 9100, + "enabled": False, + "auto_negotiate": False, + "fec": 'FEC_DISABLED', + "advertised_speed": [] +} +default_intf_speeds = {} +port_group_interfaces = None + + +def __derive_interface_config_delete_op(key_set, command, exist_conf): + new_conf = exist_conf + intf_name = command['name'] + + for attr in eth_attribute: + if attr in command: + if attr == "speed": + new_conf[attr] = default_intf_speeds[intf_name] + elif attr == "advertised_speed": + if new_conf[attr] is not None: + new_conf[attr] = list(set(new_conf[attr]).difference(command[attr])) + if new_conf[attr] == []: + new_conf[attr] = None + elif attr == "auto_negotiate": + new_conf[attr] = False + if new_conf.get('advertised_speed') is not None: + new_conf['advertised_speed'] = None + else: + new_conf[attr] = attributes_default_value[attr] + + return True, new_conf + + +TEST_KEYS_formatted_diff = [ + {'config': {'name': '', '__delete_op': __derive_interface_config_delete_op}}, +] class Interfaces(ConfigBase): @@ -71,9 +134,6 @@ class Interfaces(ConfigBase): 'interfaces', ] - params = ('description', 'mtu', 'enabled') - delete_flag = False - def __init__(self, module): super(Interfaces, self).__init__(module) @@ -100,7 +160,7 @@ class Interfaces(ConfigBase): warnings = list() existing_interfaces_facts = self.get_interfaces_facts() - commands, requests = self.set_config(existing_interfaces_facts) + commands, requests = self.set_config(existing_interfaces_facts, warnings) if commands and len(requests) > 0: if not self._module.check_mode: try: @@ -116,10 +176,27 @@ class Interfaces(ConfigBase): if result['changed']: result['after'] = changed_interfaces_facts + new_config = changed_interfaces_facts + old_config = existing_interfaces_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_interfaces_facts, + TEST_KEYS_formatted_diff) + # See the above comment about natsort module + # new_config = natsorted(new_config, key=lambda x: x['name']) + # For time-being, use simple "sort" + new_config.sort(key=lambda x: x['name']) + result['after(generated)'] = new_config + old_config.sort(key=lambda x: x['name']) + + if self._module._diff: + result['diff'] = get_formatted_config_diff(old_config, + new_config, + self._module._verbosity) result['warnings'] = warnings return result - def set_config(self, existing_interfaces_facts): + def set_config(self, existing_interfaces_facts, warnings): """ Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) @@ -128,12 +205,51 @@ class Interfaces(ConfigBase): to the desired configuration """ want = self._module.params['config'] - normalize_interface_name(want, self._module) have = existing_interfaces_facts + self.filter_out_mgmt_interface(want, have) - resp = self.set_state(want, have) + new_want, new_have = self.validate_config(want, have, warnings) + resp = self.set_state(new_want, new_have) return to_list(resp) + def validate_config(self, want, have, warnings): + new_want = deepcopy(want) + new_have = deepcopy(have) + normalize_interface_name(new_want, self._module) + for cmd in new_have: + # If auto_neg is true, ignore speed + if cmd.get('auto_negotiate') is True: + if cmd.get('speed'): + cmd.pop('speed') + elif cmd.get('advertised_speed'): + cmd.pop('advertised_speed') + + if new_want: + for cmd in new_want: + intf = next((cfg for cfg in new_have if cfg['name'] == cmd['name']), None) + state = self._module.params['state'] + if cmd.get('advertised_speed'): + cmd['advertised_speed'].sort() + + if state != "deleted": + if intf: + want_autoneg = cmd.get('auto_negotiate') + have_autoneg = intf.get('auto_negotiate') + want_speed = cmd.get('speed') + want_ads = cmd.get('advertised_speed') + + if want_speed is not None: + if want_autoneg or (want_ads and have_autoneg): + warnings.append("Speed cannot be configured when autoneg is enabled") + cmd.pop('speed') + + if want_ads is not None: + if want_autoneg is False or (not want_autoneg and not have_autoneg): + warnings.append("Advertised speed cannot be configured when autoneg is disabled") + cmd.pop('advertised_speed') + + return new_want, new_have + def set_state(self, want, have): """ Select the appropriate function based on the state provided @@ -149,18 +265,17 @@ class Interfaces(ConfigBase): # removing the dict in case diff found if state == 'overridden': - have = [each_intf for each_intf in have if each_intf['name'].startswith('Ethernet')] - commands, requests = self._state_overridden(want, have, diff) + commands, requests = self._state_overridden(want, have) elif state == 'deleted': - commands, requests = self._state_deleted(want, have, diff) + commands, requests = self._state_deleted(want, have) elif state == 'merged': commands, requests = self._state_merged(want, have, diff) elif state == 'replaced': - commands, requests = self._state_replaced(want, have, diff) + commands, requests = self._state_replaced(want, have) return commands, requests - def _state_replaced(self, want, have, diff): + def _state_replaced(self, want, have): """ The command generator when state is replaced :param want: the desired configuration as a dictionary @@ -170,17 +285,13 @@ class Interfaces(ConfigBase): :returns: the commands necessary to migrate the current configuration to the desired configuration """ - commands = self.filter_comands_to_change(diff, have) - requests = self.get_delete_interface_requests(commands, have) - requests.extend(self.get_modify_interface_requests(commands, have)) - if commands and len(requests) > 0: - commands = update_states(commands, "replaced") - else: - commands = [] + commands = [] + requests = [] + commands, requests = self.get_replaced_overridden_config(want, have, "replaced") return commands, requests - def _state_overridden(self, want, have, diff): + def _state_overridden(self, want, have): """ The command generator when state is overridden :param want: the desired configuration as a dictionary @@ -190,18 +301,9 @@ class Interfaces(ConfigBase): to the desired configuration """ commands = [] - commands_del = self.filter_comands_to_change(want, have) - requests = self.get_delete_interface_requests(commands_del, have) - del_req_count = len(requests) - if commands_del and del_req_count > 0: - commands_del = update_states(commands_del, "deleted") - commands.extend(commands_del) - - commands_over = diff - requests.extend(self.get_modify_interface_requests(commands_over, have)) - if commands_over and len(requests) > del_req_count: - commands_over = update_states(commands_over, "overridden") - commands.extend(commands_over) + requests = [] + + commands, requests = self.get_replaced_overridden_config(want, have, "overridden") return commands, requests @@ -214,8 +316,8 @@ class Interfaces(ConfigBase): :returns: the commands necessary to merge the provided into the current configuration """ - commands = diff - requests = self.get_modify_interface_requests(commands, have) + commands = self.filter_commands_to_change(diff, have) + requests = self.get_interface_requests(commands, have) if commands and len(requests) > 0: commands = update_states(commands, "merged") else: @@ -223,7 +325,7 @@ class Interfaces(ConfigBase): return commands, requests - def _state_deleted(self, want, have, diff): + def _state_deleted(self, want, have): """ The command generator when state is deleted :param want: the objects from which the configuration should be removed @@ -234,34 +336,23 @@ class Interfaces(ConfigBase): of the provided objects """ # if want is none, then delete all the interfaces + + want = remove_empties_from_list(want) + delete_all = False if not want: commands = have + delete_all = True else: commands = want - requests = self.get_delete_interface_requests(commands, have) - - if commands and len(requests) > 0: - commands = update_states(commands, "deleted") - else: - commands = [] - - return commands, requests - - def filter_comands_to_delete(self, configs, have): + commands_del, requests = self.handle_delete_interface_config(commands, have, delete_all) commands = [] + if commands_del: + commands.extend(update_states(commands_del, "deleted")) - for conf in configs: - if self.is_this_delete_required(conf, have): - temp_conf = dict() - temp_conf['name'] = conf['name'] - temp_conf['description'] = '' - temp_conf['mtu'] = 9100 - temp_conf['enabled'] = True - commands.append(temp_conf) - return commands + return commands, requests - def filter_comands_to_change(self, configs, have): + def filter_commands_to_change(self, configs, have): commands = [] if configs: for conf in configs: @@ -269,17 +360,18 @@ class Interfaces(ConfigBase): commands.append(conf) return commands - def get_modify_interface_requests(self, configs, have): - self.delete_flag = False - commands = self.filter_comands_to_change(configs, have) - - return self.get_interface_requests(commands, have) - - def get_delete_interface_requests(self, configs, have): - self.delete_flag = True - commands = self.filter_comands_to_delete(configs, have) + def is_this_change_required(self, conf, have): + intf = next((e_intf for e_intf in have if conf['name'] == e_intf['name']), None) + if intf: + # Check all parameter if any one is different from existing + for param in eth_attribute: + if conf.get(param) is not None and conf.get(param) != intf.get(param): + return True + else: + # if given interface is not present + return True - return self.get_interface_requests(commands, have) + return False def get_interface_requests(self, configs, have): requests = [] @@ -288,67 +380,285 @@ class Interfaces(ConfigBase): # Create URL and payload for conf in configs: - name = conf["name"] - if self.delete_flag and name.startswith('Loopback'): - method = DELETE - url = 'data/openconfig-interfaces:interfaces/interface=%s' % quote(name, safe='') - request = {"path": url, "method": method} + name = conf['name'] + have_conf = next((cfg for cfg in have if cfg['name'] == name), None) + + # Create Loopback incase if not available in have + if name.startswith('Loopback'): + if not have_conf: + loopback_create_request = build_interfaces_create_request(name) + requests.append(loopback_create_request) else: - # Create Loopback in case not availble in have - if name.startswith('Loopback'): - have_conf = next((cfg for cfg in have if cfg['name'] == name), None) - if not have_conf: - loopback_create_request = build_interfaces_create_request(name) - requests.append(loopback_create_request) - method = PATCH - url = 'data/openconfig-interfaces:interfaces/interface=%s/config' % quote(name, safe='') - payload = self.build_create_payload(conf) - request = {"path": url, "method": method, "data": payload} - requests.append(request) - + attribute = eth_attribute if name.startswith('Eth') else non_eth_attribute + + for attr in attribute: + if attr in conf: + c_attr = conf.get(attr) + h_attr = have_conf.get(attr) + attr_request = self.build_create_request(c_attr, h_attr, name, attr) + if attr_request: + requests.append(attr_request) return requests - def is_this_delete_required(self, conf, have): - if conf['name'] == "eth0": - return False - intf = next((e_intf for e_intf in have if conf['name'] == e_intf['name']), None) - if intf: - if (intf['name'].startswith('Loopback') or not ((intf.get('description') is None or intf.get('description') == '') and - (intf.get('enabled') is None or intf.get('enabled') is True) and (intf.get('mtu') is None or intf.get('mtu') == 9100))): - return True - return False + def build_create_request(self, c_attr, h_attr, intf_name, attr): + attributes_payload = { + "speed": 'port-speed', + "auto_negotiate": 'auto-negotiate', + "fec": 'openconfig-if-ethernet-ext2:port-fec', + "advertised_speed": 'openconfig-if-ethernet-ext2:advertised-speed' + } + + config_url = (url + eth_conf_url) % quote(intf_name, safe='') + payload = {'openconfig-if-ethernet:config': {}} + payload_attr = attributes_payload.get(attr, attr) + method = PATCH + + if attr in ('description', 'mtu', 'enabled'): + config_url = (url + '/config') % quote(intf_name, safe='') + payload = {'openconfig-interfaces:config': {}} + payload['openconfig-interfaces:config'][payload_attr] = c_attr + return {"path": config_url, "method": method, "data": payload} + + elif attr in ('fec'): + payload['openconfig-if-ethernet:config'][payload_attr] = 'openconfig-platform-types:' + c_attr + return {"path": config_url, "method": method, "data": payload} + else: + payload['openconfig-if-ethernet:config'][payload_attr] = c_attr + if attr == 'speed': + if self.is_port_in_port_group(intf_name): + self._module.fail_json(msg='Unable to configure speed in port group member. Please use port group module to change the speed') + payload['openconfig-if-ethernet:config'][payload_attr] = 'openconfig-if-ethernet:' + c_attr + if attr == 'advertised_speed': + c_ads = c_attr if c_attr else [] + h_ads = h_attr if h_attr else [] + new_ads = list(set(h_ads).union(c_ads)) + if new_ads: + payload['openconfig-if-ethernet:config'][payload_attr] = ','.join(new_ads) + + return {"path": config_url, "method": method, "data": payload} + + return [] + + def handle_delete_interface_config(self, commands, have, delete_all=False): + if not commands: + return [], [] + + commands_del, requests = [], [] + # Create URL and payload + for conf in commands: + name = conf['name'] + have_conf = next((cfg for cfg in have if cfg['name'] == name), None) + if have_conf: + lp_key_set = set(conf.keys()) + if name.startswith('Loopback'): + if delete_all or len(lp_key_set) == 1: + method = DELETE + lpbk_url = url % quote(name, safe='') + request = {"path": lpbk_url, "method": DELETE} + requests.append(request) + + commands_del.append({'name': name}) + continue + + cmd = deepcopy(have_conf) if len(lp_key_set) == 1 else deepcopy(conf) + + del_cmd = {'name': name} + attribute = eth_attribute if name.startswith('Eth') else non_eth_attribute + + for attr in attribute: + if attr in conf: + c_attr = conf.get(attr) + h_attr = have_conf.get(attr) + default_val = self.get_default_value(attr, h_attr, name) + if c_attr is not None and h_attr is not None and h_attr != default_val: + if attr == 'advertised_speed': + c_ads = c_attr if c_attr else [] + h_ads = h_attr if h_attr else [] + new_ads = list(set(h_attr).intersection(c_attr)) + if new_ads: + del_cmd.update({attr: new_ads}) + requests.append(self.build_delete_request(c_ads, h_ads, name, attr)) + else: + del_cmd.update({attr: h_attr}) + requests.append(self.build_delete_request(c_attr, h_attr, name, attr)) + if requests: + commands_del.append(del_cmd) + + return commands_del, requests + + def get_replaced_overridden_config(self, want, have, cur_state): + commands, requests = [], [] + + commands_add, commands_del = [], [] + requests_add, requests_del = [], [] + + delete_all = False + for conf in want: + name = conf['name'] + intf = next((e_intf for e_intf in have if name == e_intf['name']), None) + if name.startswith('Loopback'): + if not intf: + commands_add.append({'name': name}) + continue + + temp_conf = {} + add_conf, del_conf = {}, {} + + temp_conf['name'] = name + attribute = eth_attribute if name.startswith('Eth') else non_eth_attribute + + if not intf: + commands_add.append(conf) + else: + is_change = False + non_ads_attr_specified = False + if cur_state == "replaced": + for attr in conf: + if attr != 'name' and attr != 'advertised_speed' and conf.get(attr) is not None: + non_ads_attr_specified = True + break + else: + non_ads_attr_specified = True + + for attr in attribute: + c_attr = conf.get(attr) + h_attr = intf.get(attr) + default_val = self.get_default_value(attr, h_attr, name) + if attr != 'advertised_speed': + if c_attr is None and h_attr is not None and h_attr != default_val and non_ads_attr_specified: + del_conf[attr] = h_attr + requests_del.append(self.build_delete_request(c_attr, h_attr, name, attr)) + if c_attr is not None and c_attr != h_attr: + add_conf[attr] = c_attr + requests_add.append(self.build_create_request(c_attr, h_attr, name, attr)) + else: + c_ads = c_attr if c_attr else [] + h_ads = h_attr if h_attr else [] + new_ads = list(set(c_ads).difference(h_ads)) + delete_ads = list(set(h_ads).difference(c_ads)) + if new_ads: + add_conf[attr] = new_ads + requests_add.append(self.build_create_request(new_ads, h_attr, name, attr)) + if delete_ads: + del_conf[attr] = delete_ads + requests_del.append(self.build_delete_request(delete_ads, h_attr, name, attr)) + + if add_conf: + add_conf['name'] = name + commands_add.append(add_conf) + + if del_conf: + del_conf['name'] = name + commands_del.append(del_conf) + + if cur_state == "overridden": + for have_conf in have: + in_want = next((conf for conf in want if conf['name'] == have_conf['name']), None) + if not in_want: + del_conf = {} + for attr in attribute: + h_attr = have_conf.get(attr) + if h_attr is not None and h_attr != self.get_default_value(attr, h_attr, have_conf['name']): + del_conf[attr] = h_attr + requests_del.append(self.build_delete_request([], h_attr, have_conf['name'], attr)) + if del_conf: + del_conf['name'] = have_conf['name'] + commands_del.append(del_conf) + + if len(requests_del) > 0: + commands.extend(update_states(commands_del, "deleted")) + requests.extend(requests_del) + + if len(requests_add) > 0: + commands.extend(update_states(commands_add, cur_state)) + requests.extend(requests_add) - def is_this_change_required(self, conf, have): - if conf['name'] == "eth0": - return False - ret_flag = False - intf = next((e_intf for e_intf in have if conf['name'] == e_intf['name']), None) - if intf: - # Check all parameter if any one is differen from existing - for param in self.params: - if conf.get(param) is not None and conf.get(param) != intf.get(param): - ret_flag = True - break - # if given interface is not present + return commands, requests + + def build_delete_request(self, c_attr, h_attr, intf_name, attr): + method = DELETE + attributes_payload = { + "speed": 'port-speed', + "auto_negotiate": 'auto-negotiate', + "fec": 'openconfig-if-ethernet-ext2:port-fec', + "advertised_speed": 'openconfig-if-ethernet-ext2:advertised-speed' + } + + config_url = (url + eth_conf_url) % quote(intf_name, safe='') + payload = {'openconfig-if-ethernet:config': {}} + payload_attr = attributes_payload.get(attr, attr) + + if attr in ('description', 'mtu', 'enabled'): + attr_url = "/config/" + payload_attr + config_url = (url + attr_url) % quote(intf_name, safe='') + return {"path": config_url, "method": method} + + elif attr in ('fec'): + payload_attr = attributes_payload[attr] + payload['openconfig-if-ethernet:config'][payload_attr] = 'FEC_DISABLED' + return {"path": config_url, "method": PATCH, "data": payload} else: - ret_flag = True + payload_attr = attributes_payload[attr] + if attr == 'auto_negotiate': + # For auto-negotiate, we assign value to False since deleting the attribute will become None if deleted + # In case, if auto-negotiate is disabled, both speed and advertised_speed will have default value. + payload['openconfig-if-ethernet:config'][payload_attr] = False + return {"path": config_url, "method": PATCH, "data": payload} + + if attr == 'speed': + attr_url = eth_conf_url + "/" + attributes_payload[attr] + del_config_url = (url + attr_url) % quote(intf_name, safe='') + return {"path": del_config_url, "method": method} + + if attr == 'advertised_speed': + new_ads = list(set(h_attr).difference(c_attr)) + if new_ads: + payload['openconfig-if-ethernet:config'][payload_attr] = ','.join(new_ads) + return {"path": config_url, "method": PATCH, "data": payload} + else: + attr_url = eth_conf_url + "/" + attributes_payload[attr] + del_config_url = (url + attr_url) % quote(intf_name, safe='') + return {"path": del_config_url, "method": method} + return {} + + # Utils + def get_default_value(self, attr, h_attr, intf_name): + if attr == 'speed': + default_val = self._retrieve_default_intf_speed(intf_name) + if default_val == 'SPEED_DEFAULT': + # Incase if the port belongs to port-group, we can not able to delete the speed + default_val = h_attr + return default_val + else: + return attributes_default_value[attr] + + def filter_out_mgmt_interface(self, want, have): + if want: + mgmt_intf = next((intf for intf in want if intf['name'] == 'Management0'), None) + if mgmt_intf: + self._module.fail_json(msg='Management interface should not be configured.') + + for intf in have: + if intf['name'] == 'Management0': + have.remove(intf) + break + + def is_port_in_port_group(self, intf_name): + global port_group_interfaces + if port_group_interfaces is None: + port_group_interfaces = retrieve_port_group_interfaces(self._module) + port_num = re.search(port_num_regex, intf_name) + port_num = int(port_num.group(0)) + if port_num in port_group_interfaces: + return True - return ret_flag + return False - def build_create_payload(self, conf): - temp_conf = dict() - temp_conf['name'] = conf['name'] + def _retrieve_default_intf_speed(self, intf_name): + # To avoid multiple get requests + if self.is_port_in_port_group(intf_name): + return "SPEED_DEFAULT" - if not temp_conf['name'].startswith('Loopback'): - if conf.get('enabled') is not None: - if conf.get('enabled'): - temp_conf['enabled'] = True - else: - temp_conf['enabled'] = False - if conf.get('description') is not None: - temp_conf['description'] = conf['description'] - if conf.get('mtu') is not None: - temp_conf['mtu'] = conf['mtu'] - - payload = {'openconfig-interfaces:config': temp_conf} - return payload + if default_intf_speeds.get(intf_name) is None: + default_intf_speeds[intf_name] = retrieve_default_intf_speed(self._module, intf_name) + return default_intf_speeds[intf_name] diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/ip_neighbor/ip_neighbor.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/ip_neighbor/ip_neighbor.py new file mode 100644 index 000000000..ab3a4dde6 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/ip_neighbor/ip_neighbor.py @@ -0,0 +1,420 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_ip_neighbor class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + remove_empties +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import ( + Facts +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + get_diff, + update_states, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + get_new_config, + get_formatted_config_diff +) +from ansible.module_utils.connection import ConnectionError + +GET = 'get' +PATCH = 'patch' +PUT = 'put' +DELETE = 'delete' +GLB_URL = 'data/openconfig-neighbor:neighbor-globals/neighbor-global' +URL = 'data/openconfig-neighbor:neighbor-globals/neighbor-global=Values' +CONFIG_URL = 'data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config' + +IP_NEIGH_CONFIG_DEFAULT = { + 'ipv4_arp_timeout': 180, + 'ipv4_drop_neighbor_aging_time': 300, + 'ipv6_drop_neighbor_aging_time': 300, + 'ipv6_nd_cache_expiry': 180, + 'num_local_neigh': 0 +} + +IP_NEIGH_CONFIG_REQ_DEFAULT = { + 'name': 'Values', + 'ipv4-arp-timeout': 180, + 'ipv4-drop-neighbor-aging-time': 300, + 'ipv6-drop-neighbor-aging-time': 300, + 'ipv6-nd-cache-expiry': 180, + 'num-local-neigh': 0 +} + + +def __derive_ip_neighbor_config_delete_op(key_set, command, exist_conf): + new_conf = exist_conf + + if 'ipv4_arp_timeout' in command: + new_conf['ipv4_arp_timeout'] = IP_NEIGH_CONFIG_DEFAULT['ipv4_arp_timeout'] + + if 'ipv4_drop_neighbor_aging_time' in command: + new_conf['ipv4_drop_neighbor_aging_time'] = \ + IP_NEIGH_CONFIG_DEFAULT['ipv4_drop_neighbor_aging_time'] + + if 'ipv6_drop_neighbor_aging_time' in command: + new_conf['ipv6_drop_neighbor_aging_time'] = \ + IP_NEIGH_CONFIG_DEFAULT['ipv6_drop_neighbor_aging_time'] + + if 'ipv6_nd_cache_expiry' in command: + new_conf['ipv6_nd_cache_expiry'] = IP_NEIGH_CONFIG_DEFAULT['ipv6_nd_cache_expiry'] + + if 'num_local_neigh' in command: + new_conf['num_local_neigh'] = IP_NEIGH_CONFIG_DEFAULT['num_local_neigh'] + + return True, new_conf + + +TEST_KEYS_formatted_diff = [ + {'__default_ops': {'__delete_op': __derive_ip_neighbor_config_delete_op}}, +] + + +class Ip_neighbor(ConfigBase): + """ + The sonic_ip_neighbor class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'ip_neighbor', + ] + + def __init__(self, module): + super(Ip_neighbor, self).__init__(module) + + def get_ip_neighbor_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + ip_neighbor_facts = facts['ansible_network_resources'].get('ip_neighbor') + if not ip_neighbor_facts: + requests = self.build_create_all_requests() + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + ip_neighbor_facts = facts['ansible_network_resources'].get('ip_neighbor') + + if not ip_neighbor_facts: + err_msg = "IP neighbor module: get facts failed." + self._module.fail_json(msg=err_msg, code=500) + + return ip_neighbor_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = list() + commands = list() + requests = list() + + existing_ip_neighbor_facts = self.get_ip_neighbor_facts() + + commands, requests = self.set_config(existing_ip_neighbor_facts) + + if commands and len(requests) > 0: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + result['changed'] = True + result['commands'] = commands + + changed_ip_neighbor_facts = self.get_ip_neighbor_facts() + + result['before'] = existing_ip_neighbor_facts + if result['changed']: + result['after'] = changed_ip_neighbor_facts + + new_config = changed_ip_neighbor_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_ip_neighbor_facts, + TEST_KEYS_formatted_diff) + result['after(generated)'] = new_config + + if self._module._diff: + result['diff'] = get_formatted_config_diff(existing_ip_neighbor_facts, + new_config, + self._module._verbosity) + result['warnings'] = warnings + return result + + def set_config(self, existing_ip_neighbor_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + have = existing_ip_neighbor_facts + + resp = self.set_state(want, have) + + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + state = self._module.params['state'] + want = remove_empties(want) + + if state == 'merged': + commands, requests = self._state_merged(want, have) + elif state == 'deleted': + commands, requests = self._state_deleted(want, have) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have) + elif state == 'overridden': + commands, requests = self._state_overridden(want, have) + + return commands, requests + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = get_diff(want, have) + requests = [] + + if commands: + requests = self.build_merge_requests(commands) + + if len(requests) > 0: + commands = update_states(commands, "merged") + else: + commands = [] + + return commands, requests + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + delete_all = False + if not want: + tmp_commands = have + delete_all = True + else: + tmp_commands = want + tmp_commands = self.preprocess_delete_commands(tmp_commands, have) + + commands = get_diff(tmp_commands, IP_NEIGH_CONFIG_DEFAULT) + + requests = [] + if commands: + requests = self.build_delete_requests(commands, delete_all) + + if len(requests) > 0: + commands = update_states(commands, "deleted") + else: + commands = [] + + return commands, requests + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + new_want = self.augment_want_with_default(want) + commands = get_diff(new_want, have) + + requests = [] + if commands: + requests = self.build_merge_requests(commands) + + if len(requests) > 0: + commands = update_states(commands, "replaced") + else: + commands = [] + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + new_want = self.augment_want_with_default(want) + commands = get_diff(new_want, have) + + requests = [] + if commands: + requests = self.build_merge_requests(commands) + + if len(requests) > 0: + commands = update_states(commands, "overridden") + else: + commands = [] + + return commands, requests + + def preprocess_delete_commands(self, commands, have): + new_commands = dict() + + if 'ipv4_arp_timeout' in commands: + new_commands['ipv4_arp_timeout'] = have['ipv4_arp_timeout'] + + if 'ipv4_drop_neighbor_aging_time' in commands: + new_commands['ipv4_drop_neighbor_aging_time'] = have['ipv4_drop_neighbor_aging_time'] + + if 'ipv6_drop_neighbor_aging_time' in commands: + new_commands['ipv6_drop_neighbor_aging_time'] = have['ipv6_drop_neighbor_aging_time'] + + if 'ipv6_nd_cache_expiry' in commands: + new_commands['ipv6_nd_cache_expiry'] = have['ipv6_nd_cache_expiry'] + + if 'num_local_neigh' in commands: + new_commands['num_local_neigh'] = have['num_local_neigh'] + + return new_commands + + def augment_want_with_default(self, want): + new_want = IP_NEIGH_CONFIG_DEFAULT + + if 'ipv4_arp_timeout' in want: + new_want['ipv4_arp_timeout'] = want['ipv4_arp_timeout'] + + if 'ipv4_drop_neighbor_aging_time' in want: + new_want['ipv4_drop_neighbor_aging_time'] = want['ipv4_drop_neighbor_aging_time'] + + if 'ipv6_drop_neighbor_aging_time' in want: + new_want['ipv6_drop_neighbor_aging_time'] = want['ipv6_drop_neighbor_aging_time'] + + if 'ipv6_nd_cache_expiry' in want: + new_want['ipv6_nd_cache_expiry'] = want['ipv6_nd_cache_expiry'] + + if 'num_local_neigh' in want: + new_want['num_local_neigh'] = want['num_local_neigh'] + + return new_want + + def build_create_all_requests(self): + requests = [] + payload = { + "openconfig-neighbor:neighbor-global": + [{"name": "Values", + "config": IP_NEIGH_CONFIG_REQ_DEFAULT}] + } + method = PUT + + request = {"path": GLB_URL, "method": method, "data": payload} + requests.append(request) + return requests + + def build_merge_requests(self, conf): + requests = [] + ip_neigh_config = dict() + + if 'ipv4_arp_timeout' in conf: + ip_neigh_config['ipv4-arp-timeout'] = conf['ipv4_arp_timeout'] + + if 'ipv4_drop_neighbor_aging_time' in conf: + ip_neigh_config['ipv4-drop-neighbor-aging-time'] = conf['ipv4_drop_neighbor_aging_time'] + + if 'ipv6_drop_neighbor_aging_time' in conf: + ip_neigh_config['ipv6-drop-neighbor-aging-time'] = conf['ipv6_drop_neighbor_aging_time'] + + if 'ipv6_nd_cache_expiry' in conf: + ip_neigh_config['ipv6-nd-cache-expiry'] = conf['ipv6_nd_cache_expiry'] + + if 'num_local_neigh' in conf: + ip_neigh_config['num-local-neigh'] = conf['num_local_neigh'] + + if ip_neigh_config: + payload = {'config': ip_neigh_config} + method = PATCH + requests = {"path": CONFIG_URL, "method": method, "data": payload} + + return requests + + def build_delete_requests(self, conf, delete_all): + requests = [] + method = DELETE + + if delete_all: + request = {"path": URL, "method": method} + requests.append(request) + return requests + + if 'ipv4_arp_timeout' in conf: + req_url = CONFIG_URL + '/ipv4-arp-timeout' + request = {"path": req_url, "method": method} + requests.append(request) + + if 'ipv4_drop_neighbor_aging_time' in conf: + req_url = CONFIG_URL + '/ipv4-drop-neighbor-aging-time' + request = {"path": req_url, "method": method} + requests.append(request) + + if 'ipv6_drop_neighbor_aging_time' in conf: + req_url = CONFIG_URL + '/ipv6-drop-neighbor-aging-time' + request = {"path": req_url, "method": method} + requests.append(request) + + if 'ipv6_nd_cache_expiry' in conf: + req_url = CONFIG_URL + '/ipv6-nd-cache-expiry' + request = {"path": req_url, "method": method} + requests.append(request) + + if 'num_local_neigh' in conf: + req_url = CONFIG_URL + '/num-local-neigh' + request = {"path": req_url, "method": method} + requests.append(request) + + return requests diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_acls/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_acls/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_acls/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_acls/l2_acls.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_acls/l2_acls.py new file mode 100644 index 000000000..392e69039 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_acls/l2_acls.py @@ -0,0 +1,602 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_l2_acls class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ast import literal_eval + +from ansible.module_utils._text import to_text +from ansible.module_utils.common.validation import check_required_arguments +from ansible.module_utils.connection import ConnectionError +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + remove_empties, + validate_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + update_states +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff +) + +DELETE = 'delete' +PATCH = 'patch' +POST = 'post' + +TEST_KEYS_formatted_diff = [ + {'config': {'name': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, + {'rules': {'sequence_num': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, +] + +L2_ACL_TYPE = 'ACL_L2' +ETHERTYPE_FORMAT = '0x{:04x}' + +ethertype_value_to_protocol_map = { + '0x0800': 'ipv4', + '0x0806': 'arp', + '0x86dd': 'ipv6' +} +pcp_value_to_traffic_map = { + 0: 'be', + 1: 'bk', + 2: 'ee', + 3: 'ca', + 4: 'vi', + 5: 'vo', + 6: 'ic', + 7: 'nc' +} + +# Spec value to payload value mappings +action_value_to_payload_map = { + 'permit': 'ACCEPT', + 'discard': 'DISCARD', + 'do-not-nat': 'DO_NOT_NAT', + 'deny': 'DROP', + 'transit': 'TRANSIT' +} +ethertype_protocol_to_payload_map = { + 'arp': 'ETHERTYPE_ARP', + 'ipv4': 'ETHERTYPE_IPV4', + 'ipv6': 'ETHERTYPE_IPV6' +} +ethertype_value_to_payload_map = { + '0x8847': 'ETHERTYPE_MPLS', + '0x88cc': 'ETHERTYPE_LLDP', + '0x8915': 'ETHERTYPE_ROCE' +} +pcp_traffic_to_value_map = {v: k for k, v in pcp_value_to_traffic_map.items()} + + +class L2_acls(ConfigBase): + """ + The sonic_l2_acls class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'l2_acls', + ] + + acl_path = 'data/openconfig-acl:acl/acl-sets/acl-set' + l2_acl_path = 'data/openconfig-acl:acl/acl-sets/acl-set={acl_name},ACL_L2' + l2_acl_rule_path = 'data/openconfig-acl:acl/acl-sets/acl-set={acl_name},ACL_L2/acl-entries' + l2_acl_remark_path = 'data/openconfig-acl:acl/acl-sets/acl-set={acl_name},ACL_L2/config/description' + + def __init__(self, module): + super(L2_acls, self).__init__(module) + + def get_l2_acls_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + l2_acls_facts = facts['ansible_network_resources'].get('l2_acls') + if not l2_acls_facts: + return [] + return l2_acls_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = [] + + existing_l2_acls_facts = self.get_l2_acls_facts() + commands, requests = self.set_config(existing_l2_acls_facts) + if commands: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._handle_failure_response(exc) + + result['changed'] = True + + changed_l2_acls_facts = self.get_l2_acls_facts() + + result['before'] = existing_l2_acls_facts + if result['changed']: + result['after'] = changed_l2_acls_facts + + result['commands'] = commands + + new_config = changed_l2_acls_facts + old_config = existing_l2_acls_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_l2_acls_facts, + TEST_KEYS_formatted_diff) + result['after(generated)'] = new_config + if self._module._diff: + self.sort_config(new_config) + self.sort_config(old_config) + result['diff'] = get_formatted_config_diff(old_config, + new_config, + self._module._verbosity) + result['warnings'] = warnings + return result + + def set_config(self, existing_l2_acls_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + if want: + want = self.validate_and_normalize_config(want) + else: + want = [] + + have = existing_l2_acls_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + state = self._module.params['state'] + if state in ('merged', 'overridden', 'replaced'): + commands, requests = self._state_merged_overridden_replaced(want, have, state) + elif state == 'deleted': + commands, requests = self._state_deleted(want, have) + + return commands, requests + + def _handle_failure_response(self, connection_error): + log = None + try: + response = literal_eval(connection_error.args[0]) + error_app_tag = response['ietf-restconf:errors']['error'][0].get('error-app-tag') + except Exception: + pass + else: + if error_app_tag == 'too-many-elements': + log = 'Exceeds maximum number of ACL / ACL Rules' + elif error_app_tag == 'update-not-allowed': + log = 'Creating ACLs with same name and different type not allowed' + + if log: + response.update({u'log': log}) + self._module.fail_json(msg=to_text(response), code=connection_error.code) + else: + self._module.fail_json(msg=str(connection_error), code=connection_error.code) + + def _state_merged_overridden_replaced(self, want, have, state): + """ The command generator when state is merged/overridden/replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + add_commands = [] + del_commands = [] + commands = [] + + add_requests = [] + del_requests = [] + requests = [] + + have_dict = self._convert_config_list_to_dict(have) + want_dict = self._convert_config_list_to_dict(want) + have_acl_names = set(have_dict.keys()) + want_acl_names = set(want_dict.keys()) + + if state == 'overridden': + # Delete non-modified ACLs + for acl_name in have_acl_names.difference(want_acl_names): + del_commands.append({'name': acl_name}) + del_requests.append(self.get_delete_l2_acl_request(acl_name)) + + # Modify existing ACLs + for acl_name in want_acl_names.intersection(have_acl_names): + acl_add_command = {'name': acl_name} + acl_del_command = {'name': acl_name} + rule_add_commands = [] + rule_del_commands = [] + + have_acl = have_dict[acl_name] + want_acl = want_dict[acl_name] + if not want_acl['remark']: + if have_acl['remark'] and state in ('replaced', 'overridden'): + acl_del_command['remark'] = have_acl['remark'] + del_requests.append(self.get_delete_l2_acl_remark_request(acl_name)) + else: + if want_acl['remark'] != have_acl['remark']: + acl_add_command['remark'] = want_acl['remark'] + add_requests.append(self.get_create_l2_acl_remark_request(acl_name, want_acl['remark'])) + + have_seq_nums = set(have_acl['rules'].keys()) + want_seq_nums = set(want_acl['rules'].keys()) + + if state in ('replaced', 'overridden'): + # Delete non-modified rules + for seq_num in have_seq_nums.difference(want_seq_nums): + rule_del_commands.append({'sequence_num': seq_num}) + del_requests.append(self.get_delete_l2_acl_rule_request(acl_name, seq_num)) + + for seq_num in want_seq_nums.intersection(have_seq_nums): + # Replace existing rules + if have_acl['rules'][seq_num] != want_acl['rules'][seq_num]: + if state == 'merged': + self._module.fail_json( + msg="Cannot update existing sequence {0} of L2 ACL {1} with state merged." + " Please use state replaced or overridden.".format(seq_num, acl_name) + ) + + rule_del_commands.append({'sequence_num': seq_num}) + del_requests.append(self.get_delete_l2_acl_rule_request(acl_name, seq_num)) + + rule_add_commands.append(want_acl['rules'][seq_num]) + add_requests.append(self.get_create_l2_acl_rule_request(acl_name, seq_num, want_acl['rules'][seq_num])) + + # Add new rules + for seq_num in want_seq_nums.difference(have_seq_nums): + rule_add_commands.append(want_acl['rules'][seq_num]) + add_requests.append(self.get_create_l2_acl_rule_request(acl_name, seq_num, want_acl['rules'][seq_num])) + + if rule_del_commands: + acl_del_command['rules'] = rule_del_commands + if rule_add_commands: + acl_add_command['rules'] = rule_add_commands + + if acl_del_command.get('rules') or acl_del_command.get('remark'): + del_commands.append(acl_del_command) + if acl_add_command.get('rules') or acl_add_command.get('remark'): + add_commands.append(acl_add_command) + + # Add new ACLs + for acl_name in want_acl_names.difference(have_acl_names): + acl_add_command = {'name': acl_name} + add_requests.append(self.get_create_l2_acl_request(acl_name)) + + want_acl = want_dict[acl_name] + if want_acl['remark']: + acl_add_command['remark'] = want_acl['remark'] + add_requests.append(self.get_create_l2_acl_remark_request(acl_name, want_acl['remark'])) + + # Add new rules + want_seq_nums = set(want_acl['rules'].keys()) + if want_seq_nums: + acl_add_command['rules'] = [] + for seq_num in want_seq_nums: + acl_add_command['rules'].append(want_acl['rules'][seq_num]) + add_requests.append(self.get_create_l2_acl_rule_request(acl_name, seq_num, want_acl['rules'][seq_num])) + + add_commands.append(acl_add_command) + + if del_commands: + commands = update_states(del_commands, 'deleted') + requests = del_requests + + if add_commands: + commands.extend(update_states(add_commands, state)) + requests.extend(add_requests) + + return commands, requests + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + requests = [] + + if not want: + for acl in have: + commands.append({'name': acl['name']}) + requests.append(self.get_delete_l2_acl_request(acl['name'])) + else: + have_dict = self._convert_config_list_to_dict(have) + want_dict = self._convert_config_list_to_dict(want) + have_acl_names = set(have_dict.keys()) + want_acl_names = set(want_dict.keys()) + + # Delete existing ACLs + for acl_name in want_acl_names.intersection(have_acl_names): + have_acl = have_dict[acl_name] + want_acl = want_dict[acl_name] + + # Delete entire ACL if only the name is specified + if not want_acl['remark'] and not want_acl['rules']: + commands.append({'name': acl_name}) + requests.append(self.get_delete_l2_acl_request(acl_name)) + continue + + acl_del_command = {'name': acl_name} + rule_del_commands = [] + have_seq_nums = set(have_acl['rules'].keys()) + want_seq_nums = set(want_acl['rules'].keys()) + + if want_acl['remark'] and want_acl['remark'] == have_acl['remark']: + acl_del_command['remark'] = want_acl['remark'] + requests.append(self.get_delete_l2_acl_remark_request(acl_name)) + + # Delete existing rules + # When state is deleted, options other than sequence_num are not considered + for seq_num in want_seq_nums.intersection(have_seq_nums): + rule_del_commands.append({'sequence_num': seq_num}) + requests.append(self.get_delete_l2_acl_rule_request(acl_name, seq_num)) + + if rule_del_commands: + acl_del_command['rules'] = rule_del_commands + + if acl_del_command.get('rules') or acl_del_command.get('remark'): + commands.append(acl_del_command) + + commands = update_states(commands, "deleted") + return commands, requests + + def get_create_l2_acl_request(self, acl_name): + """Get request to create L2 ACL with specified name""" + url = self.acl_path + payload = { + 'acl-set': [{ + 'name': acl_name, + 'type': L2_ACL_TYPE, + 'config': { + 'name': acl_name, + 'type': L2_ACL_TYPE + } + }] + } + + return {'path': url, 'method': PATCH, 'data': payload} + + def get_create_l2_acl_remark_request(self, acl_name, remark): + """Get request to add given remark to the specified L2 ACL""" + url = self.l2_acl_remark_path.format(acl_name=acl_name) + payload = {'description': remark} + return {'path': url, 'method': PATCH, 'data': payload} + + def get_create_l2_acl_rule_request(self, acl_name, seq_num, rule): + """Get request to create a rule with given sequence number + and configuration in the specified L2 ACL + """ + url = self.l2_acl_rule_path.format(acl_name=acl_name) + payload = { + 'openconfig-acl:acl-entry': [{ + 'sequence-id': seq_num, + 'config': { + 'sequence-id': seq_num + }, + 'l2': { + 'config': {} + }, + 'actions': { + 'config': { + 'forwarding-action': action_value_to_payload_map[rule['action']] + } + } + }] + } + rule_l2_config = payload['openconfig-acl:acl-entry'][0]['l2']['config'] + + if rule['source'].get('host'): + rule_l2_config['source-mac'] = rule['source']['host'] + elif rule['source'].get('address'): + rule_l2_config['source-mac'] = rule['source']['address'] + rule_l2_config['source-mac-mask'] = rule['source']['address_mask'] + + if rule['destination'].get('host'): + rule_l2_config['destination-mac'] = rule['destination']['host'] + elif rule['destination'].get('address'): + rule_l2_config['destination-mac'] = rule['destination']['address'] + rule_l2_config['destination-mac-mask'] = rule['destination']['address_mask'] + + if rule.get('ethertype'): + if rule['ethertype'].get('value'): + rule_l2_config['ethertype'] = ethertype_value_to_payload_map.get(rule['ethertype']['value'], int(rule['ethertype']['value'], 16)) + else: + rule_l2_config['ethertype'] = ethertype_protocol_to_payload_map[next(iter(rule['ethertype']))] + + if rule.get('vlan_id') is not None: + rule_l2_config['vlanid'] = rule['vlan_id'] + + if rule.get('vlan_tag_format') and rule['vlan_tag_format'].get('multi_tagged'): + rule_l2_config['vlan-tag-format'] = 'openconfig-acl-ext:MULTI_TAGGED' + + if rule.get('dei') is not None: + rule_l2_config['dei'] = rule['dei'] + + if rule.get('pcp'): + if rule['pcp'].get('traffic_type'): + rule_l2_config['pcp'] = pcp_traffic_to_value_map[rule['pcp']['traffic_type']] + else: + rule_l2_config['pcp'] = rule['pcp']['value'] + rule_l2_config['pcp-mask'] = rule['pcp']['mask'] + + if rule.get('remark'): + payload['openconfig-acl:acl-entry'][0]['config']['description'] = rule['remark'] + + return {'path': url, 'method': POST, 'data': payload} + + def get_delete_l2_acl_request(self, acl_name): + """Get request to delete L2 ACL with specified name""" + url = self.l2_acl_path.format(acl_name=acl_name) + return {'path': url, 'method': DELETE} + + def get_delete_l2_acl_remark_request(self, acl_name): + """Get request to delete remark of the specified L2 ACL""" + url = self.l2_acl_remark_path.format(acl_name=acl_name) + return {'path': url, 'method': DELETE} + + def get_delete_l2_acl_rule_request(self, acl_name, seq_num): + """Get request to delete the rule with given sequence number + in the specified L2 ACL + """ + url = self.l2_acl_rule_path.format(acl_name=acl_name) + url += '/acl-entry={0}'.format(seq_num) + return {'path': url, 'method': DELETE} + + def validate_and_normalize_config(self, config_list): + """Validate and normalize the given config""" + # Remove empties and validate the config with argument spec + updated_config_list = [remove_empties(config) for config in config_list] + validate_config(self._module.argument_spec, {'config': updated_config_list}) + + state = self._module.params['state'] + # When state is deleted, options other than sequence_num are not considered + if state == 'deleted': + return updated_config_list + + for acl in updated_config_list: + if not acl.get('rules'): + continue + + for rule in acl['rules']: + self._check_required(['action', 'source', 'destination'], rule, ['config', 'rules']) + for endpoint in ('source', 'destination'): + if rule[endpoint].get('any') is False: + self._invalid_rule('True is the only valid value for {0} -> any'.format(endpoint), acl['name'], rule['sequence_num']) + elif rule[endpoint].get('host'): + rule[endpoint]['host'] = rule[endpoint]['host'].lower() + elif rule[endpoint].get('address'): + rule[endpoint]['address'] = rule[endpoint]['address'].lower() + rule[endpoint]['address_mask'] = rule[endpoint]['address_mask'].lower() + + self._normalize_ethertype(rule) + self._normalize_pcp(rule) + self._normalize_vlan_tag_format(rule) + + return updated_config_list + + def _invalid_rule(self, err_msg, acl_name, seq_num): + self._module.fail_json(msg='L2 ACL {0}, sequence number {1}: {2}'.format(acl_name, seq_num, err_msg)) + + def _check_required(self, required_parameters, parameters, options_context=None): + if required_parameters: + spec = {} + for parameter in required_parameters: + spec[parameter] = {'required': True} + + try: + check_required_arguments(spec, parameters, options_context) + except TypeError as exc: + self._module.fail_json(msg=str(exc)) + + @staticmethod + def _normalize_ethertype(rule): + ethertype = rule.get('ethertype') + if ethertype: + if ethertype.get('value'): + value = ethertype.pop('value') + if value.startswith('0x'): + value = ETHERTYPE_FORMAT.format(int(value, 16)) + else: + # If the hexadecimal number is not enclosed within + # quotes, it will be passed as a string after being + # converted to decimal. + value = ETHERTYPE_FORMAT.format(int(value, 10)) + + if value in ethertype_value_to_protocol_map: + ethertype[ethertype_value_to_protocol_map[value]] = True + else: + ethertype['value'] = value + else: + # Remove ethertype option if its value is False + if not next(iter(ethertype.values())): + del rule['ethertype'] + + @staticmethod + def _normalize_pcp(rule): + pcp = rule.get('pcp') + if pcp and pcp.get('value') is not None and pcp.get('mask') is None: + pcp['traffic_type'] = pcp_value_to_traffic_map[pcp['value']] + del pcp['value'] + + @staticmethod + def _normalize_vlan_tag_format(rule): + vlan_tag_format = rule.get('vlan_tag_format') + # Remove vlan_tag_format option if the value is False + if vlan_tag_format and not vlan_tag_format.get('multi_tagged'): + del rule['vlan_tag_format'] + + @staticmethod + def _convert_config_list_to_dict(config_list): + config_dict = {} + for config in config_list: + acl_name = config['name'] + config_dict[acl_name] = {} + config_dict[acl_name]['remark'] = config.get('remark') + config_dict[acl_name]['rules'] = {} + if config.get('rules'): + for rule in config['rules']: + config_dict[acl_name]['rules'][rule['sequence_num']] = rule + + return config_dict + + def sort_config(self, configs): + # natsort provides better result. + # The use of natsort causes sanity error due to it is not available in + # python version currently used. + # new_config = natsorted(new_config, key=lambda x: x['name']) + # For time-being, use simple "sort" + configs.sort(key=lambda x: x['name']) + + for conf in configs: + if conf.get('rules', []): + conf['rules'].sort(key=lambda x: x['sequence_num']) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_interfaces/l2_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_interfaces/l2_interfaces.py index fccba7707..c47f06940 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_interfaces/l2_interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_interfaces/l2_interfaces.py @@ -13,17 +13,19 @@ created from __future__ import absolute_import, division, print_function __metaclass__ = type -import json +import traceback from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( get_diff, + get_ranges_in_list, update_states, normalize_interface_name ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, to_list ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import ( @@ -33,9 +35,14 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s to_request, edit_config ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG, + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff +) from ansible.module_utils._text import to_native from ansible.module_utils.connection import ConnectionError -import traceback LIB_IMP_ERR = None ERR_MSG = None @@ -47,6 +54,7 @@ except Exception as e: ERR_MSG = to_native(e) LIB_IMP_ERR = traceback.format_exc() +DELETE = 'delete' PATCH = 'patch' intf_key = 'openconfig-if-ethernet:ethernet' port_chnl_key = 'openconfig-if-aggregate:aggregation' @@ -54,6 +62,10 @@ port_chnl_key = 'openconfig-if-aggregate:aggregation' TEST_KEYS = [ {'allowed_vlans': {'vlan': ''}}, ] +TEST_KEYS_formatted_diff = [ + {'config': {'name': '', '__delete_op': __DELETE_CONFIG}}, + {'allowed_vlans': {'vlan': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, +] class L2_interfaces(ConfigBase): @@ -112,6 +124,19 @@ class L2_interfaces(ConfigBase): if result['changed']: result['after'] = changed_l2_interfaces_facts + new_config = changed_l2_interfaces_facts + old_config = existing_l2_interfaces_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_l2_interfaces_facts, + TEST_KEYS_formatted_diff) + result['after(generated)'] = new_config + if self._module._diff: + self.sort_config(new_config) + self.sort_config(old_config) + result['diff'] = get_formatted_config_diff(old_config, + new_config, + self._module._verbosity) result['warnings'] = warnings return result @@ -123,7 +148,15 @@ class L2_interfaces(ConfigBase): :returns: the commands necessary to migrate the current configuration to the desired configuration """ + state = self._module.params['state'] want = self._module.params['config'] + if want: + # In state deleted, specific empty parameters are supported + if state != 'deleted': + want = [remove_empties(conf) for conf in want] + else: + want = [] + normalize_interface_name(want, self._module) have = existing_l2_interfaces_facts @@ -147,45 +180,40 @@ class L2_interfaces(ConfigBase): """ state = self._module.params['state'] - diff = get_diff(want, have, TEST_KEYS) - if state == 'overridden': - commands, requests = self._state_overridden(want, have, diff) + commands, requests = self._state_overridden(want, have) elif state == 'deleted': - commands, requests = self._state_deleted(want, have, diff) + commands, requests = self._state_deleted(want, have) elif state == 'merged': - commands, requests = self._state_merged(want, have, diff) + commands, requests = self._state_merged(want, have) elif state == 'replaced': - commands, requests = self._state_replaced(want, have, diff) + commands, requests = self._state_replaced(want, have) return commands, requests - def _state_replaced(self, want, have, diff): + def _state_replaced(self, want, have): """ The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ - + commands = [] requests = [] - commands = diff - if commands: - requests_del = self.get_delete_all_switchport_requests(commands) - if requests_del: - requests.extend(requests_del) - - requests_rep = self.get_create_l2_interface_request(commands) - if len(requests_del) or len(requests_rep): - requests.extend(requests_rep) - commands = update_states(commands, "replaced") - else: - commands = [] + del_commands, del_requests = self.get_delete_commands_requests_for_replaced_overridden(want, have, 'replaced') + if del_commands: + commands = update_states(del_commands, 'deleted') + requests = del_requests + + add_commands, add_requests = self.get_merge_commands_requests(want, have) + if add_commands: + commands.extend(update_states(add_commands, 'replaced')) + requests.extend(add_requests) return commands, requests - def _state_overridden(self, want, have, diff): + def _state_overridden(self, want, have): """ The command generator when state is overridden :rtype: A list @@ -195,23 +223,19 @@ class L2_interfaces(ConfigBase): commands = [] requests = [] - commands_del = get_diff(have, want, TEST_KEYS) - requests_del = self.get_delete_all_switchport_requests(commands_del) - if len(requests_del): - requests.extend(requests_del) - commands_del = update_states(commands_del, "deleted") - commands.extend(commands_del) + del_commands, del_requests = self.get_delete_commands_requests_for_replaced_overridden(want, have, 'overridden') + if del_commands: + commands = update_states(del_commands, 'deleted') + requests = del_requests - commands_over = diff - requests_over = self.get_create_l2_interface_request(commands_over) - if requests_over: - requests.extend(requests_over) - commands_over = update_states(commands_over, "overridden") - commands.extend(commands_over) + add_commands, add_requests = self.get_merge_commands_requests(want, have) + if add_commands: + commands.extend(update_states(add_commands, 'overridden')) + requests.extend(add_requests) return commands, requests - def _state_merged(self, want, have, diff): + def _state_merged(self, want, have): """ The command generator when state is merged :rtype: A list @@ -220,77 +244,235 @@ class L2_interfaces(ConfigBase): Requests necessary to merge to the current configuration at position-1 """ - commands = diff - requests = self.get_create_l2_interface_request(commands) - if commands and len(requests): - commands = update_states(commands, "merged") + commands, requests = self.get_merge_commands_requests(want, have) + if commands: + commands = update_states(commands, 'merged') + return commands, requests - def _state_deleted(self, want, have, diff): + def _state_deleted(self, want, have): """ The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ + commands, requests = self.get_delete_commands_requests_for_deleted(want, have) + if commands: + commands = update_states(commands, 'deleted') - # if want is none, then delete all the vlan links - if not want or len(have) == 0: - commands = have - requests = self.get_delete_all_switchport_requests(commands) + return commands, requests + + def get_merge_commands_requests(self, want, have): + """Returns the commands and requests necessary to merge the provided + configurations into the current configuration + """ + commands = [] + requests = [] + if not want: + return commands, requests + + if have: + diff = get_diff(want, have, TEST_KEYS) else: - commands = want - requests = self.get_delete_specifig_switchport_requests(want, have) - if len(requests) == 0: - commands = [] + diff = want - if commands: - commands = update_states(commands, "deleted") + for cmd in diff: + name = cmd['name'] + if name == 'eth0': + continue + + if cmd.get('trunk') and cmd['trunk'].get('allowed_vlans'): + match = next((cnf for cnf in have if cnf['name'] == name), None) + if match: + cmd['trunk']['allowed_vlans'] = self.get_trunk_allowed_vlans_diff(cmd, match) + if not cmd['trunk']['allowed_vlans']: + cmd.pop('trunk') + + if cmd.get('access') or cmd.get('trunk'): + commands.append(cmd) + requests = self.get_create_l2_interface_requests(commands) return commands, requests - def get_trunk_delete_switchport_request(self, config, match_config): - method = "DELETE" - name = config['name'] + def get_delete_commands_requests_for_deleted(self, want, have): + """Returns the commands and requests necessary to remove the current + configuration of the provided objects when state is deleted + """ + commands = [] requests = [] - match_trunk = match_config.get('trunk') - if match_trunk: - conf_allowed_vlans = config['trunk'].get('allowed_vlans', []) - if conf_allowed_vlans: - for each_allowed_vlan in conf_allowed_vlans: - if each_allowed_vlan in match_trunk.get('allowed_vlans'): - vlan_id = each_allowed_vlan['vlan'] - key = intf_key - if name.startswith('PortChannel'): - key = port_chnl_key - url = "data/openconfig-interfaces:interfaces/interface={0}/{1}/".format(name, key) - url += "openconfig-vlan:switched-vlan/config/trunk-vlans={0}".format(vlan_id) - request = {"path": url, "method": method} - requests.append(request) - return requests + if not have: + return commands, requests + + if not want: + # Delete all L2 interface config + commands = [remove_empties(conf) for conf in have] + requests = self.get_delete_all_switchport_requests(commands) + return commands, requests + + for conf in want: + name = conf['name'] + matched = next((cnf for cnf in have if cnf['name'] == name), None) + if matched: + # If both access and trunk are not mentioned, delete all config + # in that interface + if not conf.get('access') and not conf.get('trunk'): + command = {'name': name} + if matched.get('access'): + command['access'] = matched['access'] + if matched.get('trunk'): + command['trunk'] = matched['trunk'] + + commands.append(command) + requests.extend(self.get_delete_all_switchport_requests([command])) + else: + command = {} + if conf.get('access'): + access_match = matched.get('access') + if conf['access'].get('vlan'): + if access_match and access_match.get('vlan') == conf['access']['vlan']: + command['access'] = {'vlan': conf['access']['vlan']} + requests.append(self.get_access_delete_switchport_request(name)) + else: + # If access -> vlan is mentioned without value, + # delete existing access vlan config + if access_match and access_match.get('vlan'): + command['access'] = {'vlan': access_match['vlan']} + requests.append(self.get_access_delete_switchport_request(name)) + + if conf.get('trunk'): + if conf['trunk'].get('allowed_vlans'): + trunk_vlans_to_delete = self.get_trunk_allowed_vlans_common(conf, matched) + if trunk_vlans_to_delete: + command['trunk'] = {'allowed_vlans': trunk_vlans_to_delete} + requests.append(self.get_trunk_allowed_vlans_delete_switchport_request(name, command['trunk']['allowed_vlans'])) + else: + # If trunk -> allowed_vlans is mentioned without + # value, delete existing trunk allowed vlans config + trunk_match = matched.get('trunk') + if trunk_match and trunk_match.get('allowed_vlans'): + command['trunk'] = {'allowed_vlans': trunk_match['allowed_vlans'].copy()} + requests.append(self.get_trunk_allowed_vlans_delete_switchport_request(name, command['trunk']['allowed_vlans'])) + + if command: + command['name'] = name + commands.append(command) + + return commands, requests + + def get_delete_commands_requests_for_replaced_overridden(self, want, have, state): + """Returns the commands and requests necessary to remove applicable + current configurations when state is replaced or overridden + """ + commands = [] + requests = [] + if not have: + return commands, requests + + have_interfaces = self.get_interface_names(have) + want_interfaces = self.get_interface_names(want) + interfaces_to_replace = have_interfaces.intersection(want_interfaces) + if state == 'overridden': + interfaces_to_delete = have_interfaces.difference(want_interfaces) + else: + interfaces_to_delete = [] + + if want: + del_diff = get_diff(have, want, TEST_KEYS) + else: + del_diff = have + + for conf in del_diff: + name = conf['name'] + + # Delete all config in interfaces not specified in overridden + if name in interfaces_to_delete: + command = {'name': name} + if conf.get('access'): + command['access'] = conf['access'] + if conf.get('trunk'): + command['trunk'] = conf['trunk'] + + commands.append(command) + requests.extend(self.get_delete_all_switchport_requests([command])) + + # Delete config in interfaces that are replaced/overridden + elif name in interfaces_to_replace: + command = {} + + if conf.get('access') and conf['access'].get('vlan'): + command['access'] = {'vlan': conf['access']['vlan']} + requests.append(self.get_access_delete_switchport_request(name)) + + if conf.get('trunk') and conf['trunk'].get('allowed_vlans'): + matched = next((cnf for cnf in want if cnf['name'] == name), None) + if matched: + trunk_vlans_to_delete = self.get_trunk_allowed_vlans_diff(conf, matched) + if trunk_vlans_to_delete: + command['trunk'] = {'allowed_vlans': trunk_vlans_to_delete} + requests.append(self.get_trunk_allowed_vlans_delete_switchport_request(name, command['trunk']['allowed_vlans'])) + + if command: + command['name'] = name + commands.append(command) + + return commands, requests + + def get_trunk_allowed_vlans_delete_switchport_request(self, intf_name, allowed_vlans): + """Returns the request as a dict to delete the trunk vlan ranges + specified in allowed_vlans for the given interface + """ + method = DELETE + vlan_id_list = "" + for each_allowed_vlan in allowed_vlans: + vlan_id = each_allowed_vlan['vlan'] + + if '-' in vlan_id: + vlan_id_fmt = vlan_id.replace('-', '..') + else: + vlan_id_fmt = vlan_id + + if vlan_id_list: + vlan_id_list += ",{0}".format(vlan_id_fmt) + else: + vlan_id_list = vlan_id_fmt + + key = intf_key + if intf_name.startswith('PortChannel'): + key = port_chnl_key + + url = "data/openconfig-interfaces:interfaces/interface={0}/{1}/".format(intf_name, key) + url += "openconfig-vlan:switched-vlan/config/" + url += "trunk-vlans=" + vlan_id_list.replace(',', '%2C') + + request = {"path": url, "method": method} + return request + + def get_access_delete_switchport_request(self, intf_name): + """Returns the request as a dict to delete the access vlan + configuration for the given interface + """ + method = DELETE + key = intf_key + if intf_name.startswith('PortChannel'): + key = port_chnl_key + url = "data/openconfig-interfaces:interfaces/interface={}/{}/openconfig-vlan:switched-vlan/config/access-vlan" + request = {"path": url.format(intf_name, key), "method": method} - def get_access_delete_switchport_request(self, config, match_config): - method = "DELETE" - request = None - name = config['name'] - match_access = match_config.get('access') - if match_access and match_access.get('vlan') == config['access'].get('vlan'): - key = intf_key - if name.startswith('PortChannel'): - key = port_chnl_key - url = "data/openconfig-interfaces:interfaces/interface={}/{}/openconfig-vlan:switched-vlan/config/access-vlan" - request = {"path": url.format(name, key), "method": method} return request def get_delete_all_switchport_requests(self, configs): + """Returns a list of requests to delete all switchport + configuration for all interfaces specified in the config list + """ requests = [] if not configs: return requests # Create URL and payload url = "data/openconfig-interfaces:interfaces/interface={}/{}/openconfig-vlan:switched-vlan/config" - method = "DELETE" + method = DELETE for intf in configs: - name = intf.get("name") + name = intf['name'] key = intf_key if name.startswith('PortChannel'): key = port_chnl_key @@ -301,78 +483,19 @@ class L2_interfaces(ConfigBase): return requests - def get_delete_specifig_switchport_requests(self, configs, have): + def get_create_l2_interface_requests(self, configs): + """Returns a list of requests to add the switchport + configurations specified in the config list + """ requests = [] if not configs: return requests - for conf in configs: - name = conf['name'] - - matched = next((cnf for cnf in have if cnf['name'] == name), None) - if matched: - keys = conf.keys() - - # if both access and trunk not mention in delete - if not ('access' in keys) and not ('trunk' in keys): - requests.extend(self.get_delete_all_switchport_requests([conf])) - else: - # if access or trnuk is mentioned with value - if conf.get('access') or conf.get('trunk'): - # if access is mentioned with value - if conf.get('access'): - vlan = conf.get('access').get('vlan') - if vlan: - request = self.get_access_delete_switchport_request(conf, matched) - if request: - requests.append(request) - else: - if matched.get('access') and matched.get('access').get('vlan'): - conf['access']['vlan'] = matched.get('access').get('vlan') - request = self.get_access_delete_switchport_request(conf, matched) - if request: - requests.append(request) - - # if trunk is mentioned with value - if conf.get('trunk'): - allowed_vlans = conf['trunk'].get('allowed_vlans') - if allowed_vlans: - requests.extend(self.get_trunk_delete_switchport_request(conf, matched)) - # allowed vlans mentinoed without value - else: - if matched.get('trunk') and matched.get('trunk').get('allowed_vlans'): - conf['trunk']['allowed_vlans'] = matched.get('trunk') and matched.get('trunk').get('allowed_vlans').copy() - requests.extend(self.get_trunk_delete_switchport_request(conf, matched)) - # check for access or trunk is mentioned without value - else: - # access mentioned wothout value - if ('access' in keys) and conf.get('access', None) is None: - # get the existing values and delete it - if matched.get('access'): - conf['access'] = matched.get('access').copy() - request = self.get_access_delete_switchport_request(conf, matched) - if request: - requests.append(request) - # trunk mentioned wothout value - if ('trunk' in keys) and conf.get('trunk', None) is None: - # get the existing values and delete it - if matched.get('trunk'): - conf['trunk'] = matched.get('trunk').copy() - requests.extend(self.get_trunk_delete_switchport_request(conf, matched)) - - return requests - - def get_create_l2_interface_request(self, configs): - requests = [] - if not configs: - return requests # Create URL and payload url = "data/openconfig-interfaces:interfaces/interface={}/{}/openconfig-vlan:switched-vlan/config" - method = "PATCH" + method = PATCH for conf in configs: - name = conf.get('name') - if name == "eth0": - continue + name = conf['name'] key = intf_key if name.startswith('PortChannel'): key = port_chnl_key @@ -382,33 +505,124 @@ class L2_interfaces(ConfigBase): "data": payload } requests.append(request) + return requests def build_create_payload(self, conf): - payload_url = '{"openconfig-vlan:config":{ ' - access_payload = '' - trunk_payload = '' - if conf.get('access'): - access_vlan_id = conf['access']['vlan'] - access_payload = '"access-vlan": {0}'.format(access_vlan_id) - if conf.get('trunk'): - trunk_payload = '"trunk-vlans": [' - cnt = 0 + """Returns the payload to add the switchport configurations + specified in the interface config + """ + payload = {'openconfig-vlan:config': {}} + trunk_payload = [] + + if conf.get('access') and conf['access'].get('vlan'): + payload['openconfig-vlan:config']['access-vlan'] = int(conf['access']['vlan']) + + if conf.get('trunk') and conf['trunk'].get('allowed_vlans'): for each_allowed_vlan in conf['trunk']['allowed_vlans']: - if cnt > 0: - trunk_payload += ',' - trunk_payload += str(each_allowed_vlan['vlan']) - cnt = cnt + 1 - trunk_payload += ']' - - if access_payload != '': - payload_url += access_payload - if trunk_payload != '': - if access_payload != '': - payload_url += ',' - payload_url += trunk_payload - - payload_url += '}}' - - ret_payload = json.loads(payload_url) - return ret_payload + vlan_val = each_allowed_vlan['vlan'] + if '-' in vlan_val: + trunk_payload.append('{0}'.format(vlan_val.replace('-', '..'))) + else: + trunk_payload.append(int(vlan_val)) + + if trunk_payload: + payload['openconfig-vlan:config']['trunk-vlans'] = trunk_payload + + return payload + + def get_trunk_allowed_vlans_common(self, config, match): + """Returns the allowed vlan ranges that are common in the + interface configurations specified by 'config' and 'match' in + allowed_vlans spec format + """ + trunk_vlans = [] + match_trunk_vlans = [] + if config.get('trunk') and config['trunk'].get('allowed_vlans'): + trunk_vlans = config['trunk']['allowed_vlans'] + + if not trunk_vlans: + return [] + + if match.get('trunk') and match['trunk'].get('allowed_vlans'): + match_trunk_vlans = match['trunk']['allowed_vlans'] + + if not match_trunk_vlans: + return [] + + trunk_vlans = self.get_vlan_id_list(trunk_vlans) + match_trunk_vlans = self.get_vlan_id_list(match_trunk_vlans) + return self.get_allowed_vlan_range_list(list(set(trunk_vlans).intersection(set(match_trunk_vlans)))) + + def get_trunk_allowed_vlans_diff(self, config, match): + """Returns the allowed vlan ranges present only in 'config' + and and not in 'match' in allowed_vlans spec format + """ + trunk_vlans = [] + match_trunk_vlans = [] + if config.get('trunk') and config['trunk'].get('allowed_vlans'): + trunk_vlans = config['trunk']['allowed_vlans'] + + if not trunk_vlans: + return [] + + if match.get('trunk') and match['trunk'].get('allowed_vlans'): + match_trunk_vlans = match['trunk']['allowed_vlans'] + + if not match_trunk_vlans: + return trunk_vlans + + trunk_vlans = self.get_vlan_id_list(trunk_vlans) + match_trunk_vlans = self.get_vlan_id_list(match_trunk_vlans) + return self.get_allowed_vlan_range_list(list(set(trunk_vlans) - set(match_trunk_vlans))) + + @staticmethod + def get_vlan_id_list(allowed_vlan_range_list): + """Returns a list of all VLAN IDs specified in allowed_vlans list""" + vlan_id_list = [] + if allowed_vlan_range_list: + for vlan_range in allowed_vlan_range_list: + vlan_val = vlan_range['vlan'] + if '-' in vlan_val: + start, end = vlan_val.split('-') + vlan_id_list.extend(range(int(start), int(end) + 1)) + else: + # Single VLAN ID + vlan_id_list.append(int(vlan_val)) + + return vlan_id_list + + @staticmethod + def get_allowed_vlan_range_list(vlan_id_list): + """Returns the allowed_vlans list for given list of VLAN IDs""" + allowed_vlan_range_list = [] + + if vlan_id_list: + vlan_id_list.sort() + for vlan_range in get_ranges_in_list(vlan_id_list): + allowed_vlan_range_list.append({'vlan': '-'.join(map(str, (vlan_range[0], vlan_range[-1])[:len(vlan_range)]))}) + + return allowed_vlan_range_list + + @staticmethod + def get_interface_names(configs): + """Returns a set of interface names available in the given + configs list + """ + interface_names = set() + for conf in configs: + interface_names.add(conf['name']) + + return interface_names + + def sort_config(self, configs): + # natsort provides better result. + # The use of natsort causes sanity error due to it is not available in + # python version currently used. + # new_config = natsorted(new_config, key=lambda x: x['name']) + # For time-being, use simple "sort" + configs.sort(key=lambda x: x['name']) + + for conf in configs: + if conf.get('trunk', {}) and conf['trunk'].get('allowed_vlans', []): + conf['trunk']['allowed_vlans'].sort(key=lambda x: x['vlan']) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_acls/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_acls/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_acls/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_acls/l3_acls.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_acls/l3_acls.py new file mode 100644 index 000000000..26fbb7fdb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_acls/l3_acls.py @@ -0,0 +1,763 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_l3_acls class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ast import literal_eval + +from ansible.module_utils._text import to_text +from ansible.module_utils.common.validation import check_required_arguments +from ansible.module_utils.connection import ConnectionError +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + remove_empties, + validate_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + update_states +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff +) + +DELETE = 'delete' +PATCH = 'patch' +POST = 'post' + +TEST_KEYS_formatted_diff = [ + {'config': {'address_family': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, + {'acls': {'name': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, + {'rules': {'sequence_num': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, +] + +L4_PORT_START = 0 +L4_PORT_END = 65535 + +protocol_number_to_name_map = { + 1: 'icmp', + 6: 'tcp', + 17: 'udp', + 58: 'icmpv6' +} +dscp_value_to_name_map = { + 0: 'default', + 8: 'cs1', + 16: 'cs2', + 24: 'cs3', + 32: 'cs4', + 40: 'cs5', + 48: 'cs6', + 56: 'cs7', + 10: 'af11', + 12: 'af12', + 14: 'af13', + 18: 'af21', + 20: 'af22', + 22: 'af23', + 26: 'af31', + 28: 'af32', + 30: 'af33', + 34: 'af41', + 36: 'af42', + 38: 'af43', + 46: 'ef', + 44: 'voice_admit' +} + +# Spec value to payload value mappings +acl_type_to_payload_map = { + 'ipv4': 'ACL_IPV4', + 'ipv6': 'ACL_IPV6' +} +acl_type_to_host_mask_map = { + 'ipv4': '/32', + 'ipv6': '/128' +} +action_value_to_payload_map = { + 'permit': 'ACCEPT', + 'discard': 'DISCARD', + 'do-not-nat': 'DO_NOT_NAT', + 'deny': 'DROP', + 'transit': 'TRANSIT' +} +protocol_name_to_payload_map = { + 'icmp': 'IP_ICMP', + 'icmpv6': 58, + 'tcp': 'IP_TCP', + 'udp': 'IP_UDP' +} +protocol_number_to_payload_map = { + 2: 'IP_IGMP', + 46: 'IP_RSVP', + 47: 'IP_GRE', + 51: 'IP_AUTH', + 103: 'IP_PIM', + 115: 'IP_L2TP' +} +dscp_name_to_value_map = {v: k for k, v in dscp_value_to_name_map.items()} + + +class L3_acls(ConfigBase): + """ + The sonic_l3_acls class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'l3_acls', + ] + + acl_path = 'data/openconfig-acl:acl/acl-sets/acl-set' + l3_acl_path = 'data/openconfig-acl:acl/acl-sets/acl-set={acl_name},{acl_type}' + l3_acl_rule_path = 'data/openconfig-acl:acl/acl-sets/acl-set={acl_name},{acl_type}/acl-entries' + l3_acl_remark_path = 'data/openconfig-acl:acl/acl-sets/acl-set={acl_name},{acl_type}/config/description' + + def __init__(self, module): + super(L3_acls, self).__init__(module) + + def get_l3_acls_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + l3_acls_facts = facts['ansible_network_resources'].get('l3_acls') + if not l3_acls_facts: + return [] + return l3_acls_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = [] + + existing_l3_acls_facts = self.get_l3_acls_facts() + commands, requests = self.set_config(existing_l3_acls_facts) + if commands: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._handle_failure_response(exc) + + result['changed'] = True + + changed_l3_acls_facts = self.get_l3_acls_facts() + + result['before'] = existing_l3_acls_facts + if result['changed']: + result['after'] = changed_l3_acls_facts + + result['commands'] = commands + + new_config = changed_l3_acls_facts + old_config = existing_l3_acls_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_l3_acls_facts, + TEST_KEYS_formatted_diff) + self.post_process_generated_config(new_config) + result['after(generated)'] = new_config + if self._module._diff: + self.sort_config(new_config) + self.sort_config(old_config) + result['diff'] = get_formatted_config_diff(old_config, + new_config, + self._module._verbosity) + result['warnings'] = warnings + return result + + def set_config(self, existing_l3_acls_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + if want: + want = self.validate_and_normalize_config(want) + else: + want = [] + + have = existing_l3_acls_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + state = self._module.params['state'] + if state in ('merged', 'overridden', 'replaced'): + commands, requests = self._state_merged_overridden_replaced(want, have, state) + elif state == 'deleted': + commands, requests = self._state_deleted(want, have) + + return commands, requests + + def _handle_failure_response(self, connection_error): + log = None + try: + response = literal_eval(connection_error.args[0]) + error_app_tag = response['ietf-restconf:errors']['error'][0].get('error-app-tag') + except Exception: + pass + else: + if error_app_tag == 'too-many-elements': + log = 'Exceeds maximum number of ACL / ACL Rules' + elif error_app_tag == 'update-not-allowed': + log = 'Creating ACLs with same name and different type not allowed' + + if log: + response.update({u'log': log}) + self._module.fail_json(msg=to_text(response), code=connection_error.code) + else: + self._module.fail_json(msg=str(connection_error), code=connection_error.code) + + def _state_merged_overridden_replaced(self, want, have, state): + """ The command generator when state is merged/overridden/replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + add_commands = [] + del_commands = [] + commands = [] + + add_requests = [] + del_requests = [] + requests = [] + + have_dict = self._convert_config_list_to_dict(have) + want_dict = self._convert_config_list_to_dict(want) + + for acl_type in ('ipv4', 'ipv6'): + acl_type_add_commands = [] + acl_type_del_commands = [] + + have_acl_names = set(have_dict.get(acl_type, {}).keys()) + want_acl_names = set(want_dict.get(acl_type, {}).keys()) + + if state == 'overridden': + # Delete non-modified ACLs + for acl_name in have_acl_names.difference(want_acl_names): + acl_type_del_commands.append({'name': acl_name}) + del_requests.append(self.get_delete_l3_acl_request(acl_type, acl_name)) + + # Modify existing ACLs + for acl_name in want_acl_names.intersection(have_acl_names): + acl_add_command = {'name': acl_name} + acl_del_command = {'name': acl_name} + rule_add_commands = [] + rule_del_commands = [] + + have_acl = have_dict[acl_type][acl_name] + want_acl = want_dict[acl_type][acl_name] + if not want_acl['remark']: + if have_acl['remark'] and state in ('replaced', 'overridden'): + acl_del_command['remark'] = have_acl['remark'] + del_requests.append(self.get_delete_l3_acl_remark_request(acl_type, acl_name)) + else: + if want_acl['remark'] != have_acl['remark']: + acl_add_command['remark'] = want_acl['remark'] + add_requests.append(self.get_create_l3_acl_remark_request(acl_type, acl_name, want_acl['remark'])) + + have_seq_nums = set(have_acl['rules'].keys()) + want_seq_nums = set(want_acl['rules'].keys()) + + if state in ('replaced', 'overridden'): + # Delete non-modified rules + for seq_num in have_seq_nums.difference(want_seq_nums): + rule_del_commands.append({'sequence_num': seq_num}) + del_requests.append(self.get_delete_l3_acl_rule_request(acl_type, acl_name, seq_num)) + + for seq_num in want_seq_nums.intersection(have_seq_nums): + # Replace existing rules + if have_acl['rules'][seq_num] != want_acl['rules'][seq_num]: + if state == 'merged': + self._module.fail_json( + msg="Cannot update existing sequence {0} of {1} ACL {2} with state merged." + " Please use state replaced or overridden.".format(seq_num, acl_type, acl_name) + ) + + rule_del_commands.append({'sequence_num': seq_num}) + del_requests.append(self.get_delete_l3_acl_rule_request(acl_type, acl_name, seq_num)) + + rule_add_commands.append(want_acl['rules'][seq_num]) + add_requests.append(self.get_create_l3_acl_rule_request(acl_type, acl_name, seq_num, want_acl['rules'][seq_num])) + + # Add new rules + for seq_num in want_seq_nums.difference(have_seq_nums): + rule_add_commands.append(want_acl['rules'][seq_num]) + add_requests.append(self.get_create_l3_acl_rule_request(acl_type, acl_name, seq_num, want_acl['rules'][seq_num])) + + if rule_del_commands: + acl_del_command['rules'] = rule_del_commands + if rule_add_commands: + acl_add_command['rules'] = rule_add_commands + + if acl_del_command.get('rules') or acl_del_command.get('remark'): + acl_type_del_commands.append(acl_del_command) + if acl_add_command.get('rules') or acl_add_command.get('remark'): + acl_type_add_commands.append(acl_add_command) + + # Add new ACLs + for acl_name in want_acl_names.difference(have_acl_names): + acl_add_command = {'name': acl_name} + add_requests.append(self.get_create_l3_acl_request(acl_type, acl_name)) + + want_acl = want_dict[acl_type][acl_name] + if want_acl['remark']: + acl_add_command['remark'] = want_acl['remark'] + add_requests.append(self.get_create_l3_acl_remark_request(acl_type, acl_name, want_acl['remark'])) + + # Add new rules + want_seq_nums = set(want_acl['rules'].keys()) + if want_seq_nums: + acl_add_command['rules'] = [] + for seq_num in want_seq_nums: + acl_add_command['rules'].append(want_acl['rules'][seq_num]) + add_requests.append(self.get_create_l3_acl_rule_request(acl_type, acl_name, seq_num, want_acl['rules'][seq_num])) + + acl_type_add_commands.append(acl_add_command) + + if acl_type_del_commands: + del_commands.append({'address_family': acl_type, 'acls': acl_type_del_commands}) + + if acl_type_add_commands: + add_commands.append({'address_family': acl_type, 'acls': acl_type_add_commands}) + + if del_commands: + commands = update_states(del_commands, 'deleted') + requests = del_requests + + if add_commands: + commands.extend(update_states(add_commands, state)) + requests.extend(add_requests) + + return commands, requests + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + requests = [] + + if not want: + for config in have: + if not config.get('acls'): + continue + + acl_type_commands = [] + acl_type = config['address_family'] + for acl in config['acls']: + acl_type_commands.append({'name': acl['name']}) + requests.append(self.get_delete_l3_acl_request(acl_type, acl['name'])) + + if acl_type_commands: + commands.append({'address_family': acl_type, 'acls': acl_type_commands}) + else: + have_dict = self._convert_config_list_to_dict(have) + want_dict = self._convert_config_list_to_dict(want) + + for acl_type in ('ipv4', 'ipv6'): + acl_type_commands = [] + have_acl_names = set(have_dict.get(acl_type, {}).keys()) + want_acl_names = set(want_dict.get(acl_type, {}).keys()) + + # If only the type is specified, delete all ACLs of that type + if acl_type in want_dict and not want_acl_names: + for acl_name in have_acl_names: + acl_type_commands.append({'name': acl_name}) + requests.append(self.get_delete_l3_acl_request(acl_type, acl_name)) + + # Delete existing ACLs + for acl_name in want_acl_names.intersection(have_acl_names): + have_acl = have_dict[acl_type][acl_name] + want_acl = want_dict[acl_type][acl_name] + + # Delete entire ACL if only the name is specified + if not want_acl['remark'] and not want_acl['rules']: + acl_type_commands.append({'name': acl_name}) + requests.append(self.get_delete_l3_acl_request(acl_type, acl_name)) + continue + + acl_del_command = {'name': acl_name} + rule_del_commands = [] + have_seq_nums = set(have_acl['rules'].keys()) + want_seq_nums = set(want_acl['rules'].keys()) + + if want_acl['remark'] and want_acl['remark'] == have_acl['remark']: + acl_del_command['remark'] = want_acl['remark'] + requests.append(self.get_delete_l3_acl_remark_request(acl_type, acl_name)) + + # Delete existing rules + # When state is deleted, options other than sequence_num are not considered + for seq_num in want_seq_nums.intersection(have_seq_nums): + rule_del_commands.append({'sequence_num': seq_num}) + requests.append(self.get_delete_l3_acl_rule_request(acl_type, acl_name, seq_num)) + + if rule_del_commands: + acl_del_command['rules'] = rule_del_commands + + if acl_del_command.get('rules') or acl_del_command.get('remark'): + acl_type_commands.append(acl_del_command) + + if acl_type_commands: + commands.append({'address_family': acl_type, 'acls': acl_type_commands}) + + commands = update_states(commands, "deleted") + return commands, requests + + def get_create_l3_acl_request(self, acl_type, acl_name): + """Get request to create L3 ACL with specified type and name""" + url = self.acl_path + payload = { + 'acl-set': [{ + 'name': acl_name, + 'type': acl_type_to_payload_map[acl_type], + 'config': { + 'name': acl_name, + 'type': acl_type_to_payload_map[acl_type] + } + }] + } + + return {'path': url, 'method': PATCH, 'data': payload} + + def get_create_l3_acl_remark_request(self, acl_type, acl_name, remark): + """Get request to add given remark to the specified L3 ACL""" + url = self.l3_acl_remark_path.format(acl_name=acl_name, acl_type=acl_type_to_payload_map[acl_type]) + payload = {'description': remark} + return {'path': url, 'method': PATCH, 'data': payload} + + def get_create_l3_acl_rule_request(self, acl_type, acl_name, seq_num, rule): + """Get request to create a rule with given sequence number + and configuration in the specified L3 ACL + """ + url = self.l3_acl_rule_path.format(acl_name=acl_name, acl_type=acl_type_to_payload_map[acl_type]) + payload = { + 'openconfig-acl:acl-entry': [{ + 'sequence-id': seq_num, + 'config': { + 'sequence-id': seq_num + }, + acl_type: { + 'config': {} + }, + 'transport': { + 'config': {} + }, + 'actions': { + 'config': { + 'forwarding-action': action_value_to_payload_map[rule['action']] + } + } + }] + } + rule_l3_config = payload['openconfig-acl:acl-entry'][0][acl_type]['config'] + rule_l4_config = payload['openconfig-acl:acl-entry'][0]['transport']['config'] + + if rule['protocol'].get('number') is not None: + protocol = rule['protocol']['number'] + rule_l3_config['protocol'] = protocol_number_to_payload_map.get(protocol, protocol) + else: + protocol = rule['protocol']['name'] + if protocol not in ('ip', 'ipv6'): + rule_l3_config['protocol'] = protocol_name_to_payload_map[protocol] + + if rule['source'].get('host'): + rule_l3_config['source-address'] = rule['source']['host'] + acl_type_to_host_mask_map[acl_type] + elif rule['source'].get('prefix'): + rule_l3_config['source-address'] = rule['source']['prefix'] + + src_port_number = self._convert_port_dict_to_payload_format(rule['source'].get('port_number')) + if src_port_number: + rule_l4_config['source-port'] = src_port_number + + if rule['destination'].get('host'): + rule_l3_config['destination-address'] = rule['destination']['host'] + acl_type_to_host_mask_map[acl_type] + elif rule['destination'].get('prefix'): + rule_l3_config['destination-address'] = rule['destination']['prefix'] + + dest_port_number = self._convert_port_dict_to_payload_format(rule['destination'].get('port_number')) + if dest_port_number: + rule_l4_config['destination-port'] = dest_port_number + + if rule.get('protocol_options'): + if protocol in ('icmp', 'icmpv6') and rule['protocol_options'].get(protocol): + if rule['protocol_options'][protocol].get('type') is not None: + rule_l4_config['icmp-type'] = rule['protocol_options'][protocol]['type'] + if rule['protocol_options'][protocol].get('code') is not None: + rule_l4_config['icmp-code'] = rule['protocol_options'][protocol]['code'] + elif rule['protocol_options'].get('tcp'): + if rule['protocol_options']['tcp'].get('established'): + rule_l4_config['tcp-session-established'] = True + else: + tcp_flag_list = [] + for tcp_flag in rule['protocol_options']['tcp'].keys(): + if rule['protocol_options']['tcp'][tcp_flag]: + tcp_flag_list.append('tcp_{0}'.format(tcp_flag).upper()) + + if tcp_flag_list: + rule_l4_config['tcp-flags'] = tcp_flag_list + + if rule.get('vlan_id') is not None: + payload['openconfig-acl:acl-entry'][0]['l2'] = { + 'config': { + 'vlanid': rule['vlan_id'] + } + } + + if rule.get('dscp'): + if rule['dscp'].get('value') is not None: + rule_l3_config['dscp'] = rule['dscp']['value'] + else: + dscp_opt = next(iter(rule['dscp'])) + if rule['dscp'][dscp_opt]: + rule_l3_config['dscp'] = dscp_name_to_value_map[dscp_opt] + + if rule.get('remark'): + payload['openconfig-acl:acl-entry'][0]['config']['description'] = rule['remark'] + + return {'path': url, 'method': POST, 'data': payload} + + def get_delete_l3_acl_request(self, acl_type, acl_name): + """Get request to delete L3 ACL with specified type and name""" + url = self.l3_acl_path.format(acl_name=acl_name, acl_type=acl_type_to_payload_map[acl_type]) + return {'path': url, 'method': DELETE} + + def get_delete_l3_acl_remark_request(self, acl_type, acl_name): + """Get request to delete remark of the specified L3 ACL""" + url = self.l3_acl_remark_path.format(acl_name=acl_name, acl_type=acl_type_to_payload_map[acl_type]) + return {'path': url, 'method': DELETE} + + def get_delete_l3_acl_rule_request(self, acl_type, acl_name, seq_num): + """Get request to delete the rule with given sequence number + in the specified L3 ACL + """ + url = self.l3_acl_rule_path.format(acl_name=acl_name, acl_type=acl_type_to_payload_map[acl_type]) + url += '/acl-entry={0}'.format(seq_num) + return {'path': url, 'method': DELETE} + + def validate_and_normalize_config(self, config_list): + """Validate and normalize the given config""" + # Remove empties and validate the config with argument spec + updated_config_list = [remove_empties(config) for config in config_list] + validate_config(self._module.argument_spec, {'config': updated_config_list}) + + state = self._module.params['state'] + # When state is deleted, options other than sequence_num are not considered + if state == 'deleted': + return updated_config_list + + for config in updated_config_list: + if not config.get('acls'): + continue + + acl_type = config['address_family'] + for acl in config['acls']: + if not acl.get('rules'): + continue + + acl_name = acl['name'] + for rule in acl['rules']: + seq_num = rule['sequence_num'] + + self._check_required(['action', 'source', 'destination', 'protocol'], rule, ['config', 'acls', 'rules']) + self._validate_and_normalize_protocol(acl_type, acl_name, rule) + protocol = rule['protocol']['name'] if rule['protocol'].get('name') else str(rule['protocol']['number']) + + for endpoint in ('source', 'destination'): + if rule[endpoint].get('any') is False: + self._invalid_rule('True is the only valid value for {0} -> any'.format(endpoint), acl_type, acl_name, seq_num) + elif rule[endpoint].get('host'): + rule[endpoint]['host'] = rule[endpoint]['host'].lower() + elif rule[endpoint].get('prefix'): + rule[endpoint]['prefix'] = rule[endpoint]['prefix'].lower() + + if rule[endpoint].get('port_number'): + if protocol not in ('tcp', 'udp'): + self._invalid_rule('{0} -> port_number is valid only for TCP or UDP protocol'.format(endpoint), acl_type, acl_name, seq_num) + + self._validate_and_normalize_port_number(acl_type, acl_name, rule, endpoint) + + if rule.get('protocol_options'): + protocol_options = next(iter(rule['protocol_options'])) + if protocol != protocol_options: + self._invalid_rule('protocol_options -> {0} is not valid for protocol {1}'.format(protocol_options, protocol), + acl_type, acl_name, seq_num) + + self._normalize_protocol_options(rule) + + self._normalize_dscp(rule) + + return updated_config_list + + def _validate_and_normalize_protocol(self, acl_type, acl_name, rule): + protocol = rule.get('protocol') + if protocol: + if protocol.get('number') is not None: + if protocol['number'] in protocol_number_to_name_map: + protocol['name'] = protocol_number_to_name_map[protocol.pop('number')] + + protocol_name = protocol.get('name') + if (acl_type == 'ipv4' and protocol_name in ('ipv6', 'icmpv6')) or (acl_type == 'ipv6' and protocol_name in ('ip', 'icmp')): + self._invalid_rule('invalid protocol {0} for {1} ACL'.format(protocol_name, acl_type), acl_type, acl_name, rule['sequence_num']) + + def _validate_and_normalize_port_number(self, acl_type, acl_name, rule, endpoint): + port_number = rule.get(endpoint, {}).get('port_number') + if port_number: + # Greater than 0 is the same as less than 65535 + if port_number.get('gt') == L4_PORT_START: + port_number['lt'] = L4_PORT_END + del port_number['gt'] + elif rule[endpoint]['port_number'].get('range'): + port_range = rule[endpoint]['port_number']['range'] + if port_range['begin'] >= port_range['end']: + self._invalid_rule('begin must be less than end in {0} -> port_number -> range'.format(endpoint), acl_type, acl_name, rule['sequence_num']) + + # Range of 0 to x is the same as less than x and + # range of x to 65535 is the same as greater than x + if port_range['begin'] == L4_PORT_START: + port_number['lt'] = port_range['end'] + del port_number['range'] + elif port_range['end'] == L4_PORT_END: + port_number['gt'] = port_range['begin'] + del port_number['range'] + + def _invalid_rule(self, err_msg, acl_type, acl_name, seq_num): + self._module.fail_json(msg='{0} ACL {1}, sequence number {2}: {3}'.format(acl_type, acl_name, seq_num, err_msg)) + + def _check_required(self, required_parameters, parameters, options_context=None): + if required_parameters: + spec = {} + for parameter in required_parameters: + spec[parameter] = {'required': True} + + try: + check_required_arguments(spec, parameters, options_context) + except TypeError as exc: + self._module.fail_json(msg=str(exc)) + + @staticmethod + def _normalize_protocol_options(rule): + tcp = rule.get('protocol_options', {}).get('tcp') + if tcp: + # Remove protocol_options option if all tcp options are False + if not any(list(tcp.values())): + del rule['protocol_options'] + else: + tcp_flag_list = list(tcp.keys()) + for tcp_flag in tcp_flag_list: + # Remove tcp option if its value is False + if not tcp[tcp_flag]: + del tcp[tcp_flag] + + @staticmethod + def _normalize_dscp(rule): + dscp = rule.get('dscp') + if dscp: + if dscp.get('value') is not None: + if dscp['value'] in dscp_value_to_name_map: + dscp[dscp_value_to_name_map[dscp.pop('value')]] = True + else: + # Remove dscp option if its value is False + if not next(iter(dscp.values())): + del rule['dscp'] + + @staticmethod + def _convert_config_list_to_dict(config_list): + config_dict = {} + for config in config_list: + acl_type = config['address_family'] + config_dict[acl_type] = {} + if config.get('acls'): + for acl in config['acls']: + acl_name = acl['name'] + config_dict[acl_type][acl_name] = {} + config_dict[acl_type][acl_name]['remark'] = acl.get('remark') + config_dict[acl_type][acl_name]['rules'] = {} + if acl.get('rules'): + for rule in acl['rules']: + config_dict[acl_type][acl_name]['rules'][rule['sequence_num']] = rule + + return config_dict + + @staticmethod + def _convert_port_dict_to_payload_format(port_dict): + payload = None + if port_dict: + if port_dict.get('eq') is not None: + payload = port_dict['eq'] + elif port_dict.get('lt') is not None: + payload = '{0}..{1}'.format(L4_PORT_START, port_dict['lt']) + elif port_dict.get('gt') is not None: + payload = '{0}..{1}'.format(port_dict['gt'], L4_PORT_END) + elif port_dict.get('range'): + payload = '{0}..{1}'.format(port_dict['range']['begin'], port_dict['range']['end']) + + return payload + + def sort_config(self, configs): + # natsort provides better result. + # The use of natsort causes sanity error due to it is not available in + # python version currently used. + # new_config = natsorted(new_config, key=lambda x: x['name']) + # For time-being, use simple "sort" + configs.sort(key=lambda x: x['address_family']) + + for conf in configs: + acls = conf.get('acls', []) + if acls: + acls.sort(key=lambda x: x['name']) + for acl in acls: + if acl.get('rules', []): + acl['rules'].sort(key=lambda x: x['sequence_num']) + + def post_process_generated_config(self, configs): + for conf in configs[:]: + if not conf.get('acls', []): + configs.remove(conf) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_interfaces/l3_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_interfaces/l3_interfaces.py index d1b735251..200d8552b 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_interfaces/l3_interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_interfaces/l3_interfaces.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -30,7 +30,6 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s to_request, edit_config ) -from ansible.module_utils._text import to_native from ansible.module_utils.connection import ConnectionError TEST_KEYS = [ @@ -125,17 +124,17 @@ class L3_interfaces(ConfigBase): state = self._module.params['state'] diff = get_diff(want, have, TEST_KEYS) if state == 'overridden': - commands, requests = self._state_overridden(want, have, diff) + commands, requests = self._state_overridden(want, have) elif state == 'deleted': - commands, requests = self._state_deleted(want, have, diff) + commands, requests = self._state_deleted(want, have) elif state == 'merged': commands, requests = self._state_merged(want, have, diff) elif state == 'replaced': - commands, requests = self._state_replaced(want, have, diff) + commands, requests = self._state_replaced(want, have) ret_commands = commands return ret_commands, requests - def _state_replaced(self, want, have, diff): + def _state_replaced(self, want, have): """ The command generator when state is replaced :rtype: A list @@ -144,19 +143,22 @@ class L3_interfaces(ConfigBase): """ ret_requests = list() commands = list() - l3_interfaces_to_delete = get_diff(have, want, TEST_KEYS) - obj = self.get_object(l3_interfaces_to_delete, want) - diff = get_diff(obj, want, TEST_KEYS) + new_want = self.update_object(want) + new_have = self.remove_default_entries(have) + get_replace_interfaces_list = self.get_interface_object_for_replaced(new_have, want) + + diff = get_diff(get_replace_interfaces_list, new_want, TEST_KEYS) + if diff: - delete_l3_interfaces_requests = self.get_delete_all_requests(want) + delete_l3_interfaces_requests = self.get_delete_all_requests(diff) ret_requests.extend(delete_l3_interfaces_requests) - commands.extend(update_states(want, "deleted")) + commands.extend(update_states(diff, "deleted")) l3_interfaces_to_create_requests = self.get_create_l3_interfaces_requests(want, have, want) ret_requests.extend(l3_interfaces_to_create_requests) - commands.extend(update_states(want, "merged")) + commands.extend(update_states(want, "replaced")) return commands, ret_requests - def _state_overridden(self, want, have, diff): + def _state_overridden(self, want, have): """ The command generator when state is overridden :rtype: A list @@ -165,16 +167,19 @@ class L3_interfaces(ConfigBase): """ ret_requests = list() commands = list() - interfaces_to_delete = get_diff(have, want, TEST_KEYS) - if interfaces_to_delete: - delete_interfaces_requests = self.get_delete_l3_interfaces_requests(want, have) + new_want = self.update_object(want) + new_have = self.remove_default_entries(have) + get_override_interfaces = self.get_interface_object_for_overridden(new_have) + diff = get_diff(get_override_interfaces, new_want, TEST_KEYS) + diff2 = get_diff(new_want, get_override_interfaces, TEST_KEYS) + + if diff or diff2: + delete_interfaces_requests = self.get_delete_all_requests(have) ret_requests.extend(delete_interfaces_requests) - commands.extend(update_states(interfaces_to_delete, "deleted")) - - if diff: - interfaces_to_create_requests = self.get_create_l3_interfaces_requests(diff, have, want) + commands.extend(update_states(diff, "deleted")) + interfaces_to_create_requests = self.get_create_l3_interfaces_requests(want, have, want) ret_requests.extend(interfaces_to_create_requests) - commands.extend(update_states(diff, "merged")) + commands.extend(update_states(want, "overridden")) return commands, ret_requests @@ -195,7 +200,7 @@ class L3_interfaces(ConfigBase): return commands, requests - def _state_deleted(self, want, have, diff): + def _state_deleted(self, want, have): """ The command generator when state is deleted :rtype: A list @@ -215,7 +220,16 @@ class L3_interfaces(ConfigBase): commands = update_states(commands, "deleted") return commands, requests - def get_object(self, have, want): + def remove_default_entries(self, have): + new_have = list() + for obj in have: + if obj['ipv4']['addresses'] is not None or obj['ipv4']['anycast_addresses'] is not None: + new_have.append(obj) + elif obj['ipv6']['addresses'] is not None or obj['ipv6']['enabled']: + new_have.append(obj) + return new_have + + def get_interface_object_for_replaced(self, have, want): objects = list() names = [i.get('name', None) for i in want] for obj in have: @@ -223,6 +237,43 @@ class L3_interfaces(ConfigBase): objects.append(obj.copy()) return objects + def update_object(self, want): + objects = list() + for obj in want: + new_obj = {} + if 'name' in obj: + new_obj['name'] = obj['name'] + if obj['ipv4'] is None: + new_obj['ipv4'] = {'addresses': None, 'anycast_addresses': None} + else: + new_obj['ipv4'] = obj['ipv4'] + + if obj['ipv6'] is None: + new_obj['ipv6'] = {'addresses': None, 'enabled': False} + else: + new_obj['ipv6'] = obj['ipv6'] + + objects.append(new_obj) + return objects + + def get_interface_object_for_overridden(self, have): + objects = list() + for obj in have: + if 'name' in obj and obj['name'] != "Management0": + ipv4_addresses = obj['ipv4']['addresses'] + ipv6_addresses = obj['ipv6']['addresses'] + anycast_addresses = obj['ipv4']['anycast_addresses'] + ipv6_enable = obj['ipv6']['enabled'] + + if ipv4_addresses is not None or ipv6_addresses is not None: + objects.append(obj.copy()) + continue + + if ipv6_enable or anycast_addresses is not None: + objects.append(obj.copy()) + continue + return objects + def get_address(self, ip_str, have_obj): to_return = list() for i in have_obj: @@ -241,6 +292,8 @@ class L3_interfaces(ConfigBase): ipv6_addr_url = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv6/addresses/address={address}' ipv6_enabled_url = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv6/config/enabled' + if not want: + return requests for each_l3 in want: l3 = each_l3.copy() name = l3.pop('name') @@ -274,7 +327,7 @@ class L3_interfaces(ConfigBase): if name and ipv4 is None and ipv6 is None: is_del_ipv4 = True is_del_ipv6 = True - elif ipv4 and ipv4.get('addresses') and not ipv4.get('anycast_addresses'): + elif ipv4 and not ipv4.get('addresses') and not ipv4.get('anycast_addresses'): is_del_ipv4 = True elif ipv6 and not ipv6.get('addresses') and ipv6.get('enabled') is None: is_del_ipv6 = True @@ -299,24 +352,27 @@ class L3_interfaces(ConfigBase): # Store the primary ip at end of the list. So primary ip will be deleted after the secondary ips ipv4_del_reqs = [] - for ip in ipv4_addrs: - match_ip = next((addr for addr in have_ipv4_addrs if addr['address'] == ip['address']), None) - if match_ip: - addr = ip['address'].split('/')[0] - del_url = ipv4_addr_url.format(intf_name=name, sub_intf_name=sub_intf, address=addr) - if match_ip['secondary']: - del_url += '/config/secondary' - ipv4_del_reqs.insert(0, {"path": del_url, "method": DELETE}) - else: - ipv4_del_reqs.append({"path": del_url, "method": DELETE}) - if ipv4_del_reqs: - requests.extend(ipv4_del_reqs) - - for ip in ipv4_anycast_addrs: - if have_ipv4_addrs and ip in have_ipv4_addrs: - ip = ip.replace('/', '%2f') - anycast_delete_request = {"path": ipv4_anycast_url.format(intf_name=name, sub_intf_name=sub_intf, anycast_ip=ip), "method": DELETE} - requests.append(anycast_delete_request) + if ipv4_addrs: + for ip in ipv4_addrs: + if have_ipv4_addrs: + match_ip = next((addr for addr in have_ipv4_addrs if addr['address'] == ip['address']), None) + if match_ip: + addr = ip['address'].split('/')[0] + del_url = ipv4_addr_url.format(intf_name=name, sub_intf_name=sub_intf, address=addr) + if match_ip['secondary']: + del_url += '/config/secondary' + ipv4_del_reqs.insert(0, {"path": del_url, "method": DELETE}) + else: + ipv4_del_reqs.append({"path": del_url, "method": DELETE}) + if ipv4_del_reqs: + requests.extend(ipv4_del_reqs) + + if ipv4_anycast_addrs: + for ip in ipv4_anycast_addrs: + if have_ipv4_anycast_addrs and ip in have_ipv4_anycast_addrs: + ip = ip.replace('/', '%2f') + anycast_delete_request = {"path": ipv4_anycast_url.format(intf_name=name, sub_intf_name=sub_intf, anycast_ip=ip), "method": DELETE} + requests.append(anycast_delete_request) if is_del_ipv6: if have_ipv6_addrs and len(have_ipv6_addrs) != 0: @@ -334,12 +390,12 @@ class L3_interfaces(ConfigBase): ipv6_addrs = l3['ipv6']['addresses'] if 'enabled' in l3['ipv6']: ipv6_enabled = l3['ipv6']['enabled'] - - for ip in ipv6_addrs: - if have_ipv6_addrs and ip['address'] in have_ipv6_addrs: - addr = ip['address'].split('/')[0] - request = {"path": ipv6_addr_url.format(intf_name=name, sub_intf_name=sub_intf, address=addr), "method": DELETE} - requests.append(request) + if ipv6_addrs: + for ip in ipv6_addrs: + if have_ipv6_addrs and ip['address'] in have_ipv6_addrs: + addr = ip['address'].split('/')[0] + request = {"path": ipv6_addr_url.format(intf_name=name, sub_intf_name=sub_intf, address=addr), "method": DELETE} + requests.append(request) if have_ipv6_enabled and ipv6_enabled is not None: request = {"path": ipv6_enabled_url.format(intf_name=name, sub_intf_name=sub_intf), "method": DELETE} @@ -349,8 +405,9 @@ class L3_interfaces(ConfigBase): def get_delete_all_completely_requests(self, configs): delete_requests = list() for l3 in configs: - if l3['ipv4'] or l3['ipv6']: - delete_requests.append(l3) + if l3['name'] != "Management0": + if l3['ipv4'] or l3['ipv6']: + delete_requests.append(l3) return self.get_delete_all_requests(delete_requests) def get_delete_all_requests(self, configs): @@ -364,6 +421,8 @@ class L3_interfaces(ConfigBase): name = l3.get('name') ipv4_addrs = [] ipv4_anycast = [] + if name == "Management0": + continue if l3.get('ipv4'): if l3['ipv4'].get('addresses'): ipv4_addrs = l3['ipv4']['addresses'] diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lag_interfaces/lag_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lag_interfaces/lag_interfaces.py index 541de2c4c..7ccd8ce02 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lag_interfaces/lag_interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lag_interfaces/lag_interfaces.py @@ -21,6 +21,9 @@ except ImportError: import json +from copy import ( + deepcopy +) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -39,6 +42,11 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s to_request, edit_config ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff +) from ansible.module_utils._text import to_native from ansible.module_utils.connection import ConnectionError import traceback @@ -60,6 +68,10 @@ DELETE = 'delete' TEST_KEYS = [ {'interfaces': {'member': ''}}, ] +TEST_KEYS_formatted_diff = [ + {'config': {'name': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, + {'interfaces': {'member': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, +] class Lag_interfaces(ConfigBase): @@ -119,6 +131,19 @@ class Lag_interfaces(ConfigBase): if result['changed']: result['after'] = changed_lag_interfaces_facts + new_config = changed_lag_interfaces_facts + old_config = existing_lag_interfaces_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_lag_interfaces_facts, + TEST_KEYS_formatted_diff) + result['after(generated)'] = new_config + if self._module._diff: + self.sort_config(new_config) + self.sort_config(old_config) + result['diff'] = get_formatted_config_diff(old_config, + new_config, + self._module._verbosity) result['warnings'] = warnings return result @@ -188,7 +213,7 @@ class Lag_interfaces(ConfigBase): replaced_list.append(list_obj) requests = self.get_delete_lag_interfaces_requests(replaced_list) if requests: - commands.extend(update_states(replaced_list, "replaced")) + commands.extend(update_states(replaced_list, "deleted")) replaced_commands, replaced_requests = self.template_for_lag_creation(have, diff_members, diff_portchannels, "replaced") if replaced_requests: commands.extend(replaced_commands) @@ -208,20 +233,31 @@ class Lag_interfaces(ConfigBase): delete_list = list() delete_list = get_diff(have, want, TEST_KEYS) delete_members, delete_portchannels = self.diff_list_for_member_creation(delete_list) + replaced_list = list() for i in want: list_obj = search_obj_in_list(i['name'], delete_members, "name") if list_obj: replaced_list.append(list_obj) + requests = self.get_delete_lag_interfaces_requests(replaced_list) - commands.extend(update_states(replaced_list, "overridden")) - delete_members = get_diff(delete_members, replaced_list, TEST_KEYS) - commands_overridden, requests_overridden = self.template_for_lag_deletion(have, delete_members, delete_portchannels, "overridden") - requests.extend(requests_overridden) - commands.extend(commands_overridden) + commands.extend(update_states(replaced_list, "deleted")) + + deleted_po_list = list() + for i in delete_list: + list_obj = search_obj_in_list(i['name'], want, "name") + if not list_obj: + deleted_po_list.append(i) + + requests_deleted_po = self.get_delete_portchannel_requests(deleted_po_list) + requests.extend(requests_deleted_po) + commands_del = self.prune_commands(deleted_po_list) + commands.extend(update_states(commands_del, "deleted")) + override_commands, override_requests = self.template_for_lag_creation(have, diff_members, diff_portchannels, "overridden") commands.extend(override_commands) requests.extend(override_requests) + return commands, requests def _state_merged(self, want, have, diff_members, diff_portchannels): @@ -248,7 +284,8 @@ class Lag_interfaces(ConfigBase): requests = self.get_delete_all_lag_interfaces_requests() portchannel_requests = self.get_delete_all_portchannel_requests() requests.extend(portchannel_requests) - commands.extend(update_states(have, "Deleted")) + commands_del = self.prune_commands(have) + commands.extend(update_states(commands_del, "deleted")) else: # delete specific lag interfaces and specific portchannels commands = get_diff(want, diff, TEST_KEYS) commands = remove_empties_from_list(commands) @@ -312,7 +349,8 @@ class Lag_interfaces(ConfigBase): commands.extend(update_states(delete_members, state_name)) if delete_portchannels: portchannel_requests = self.get_delete_portchannel_requests(delete_portchannels) - commands.extend(update_states(delete_portchannels, state_name)) + commands_del = self.prune_commands(delete_portchannels) + commands.extend(update_states(commands_del, state_name)) if requests: requests.extend(portchannel_requests) else: @@ -336,8 +374,7 @@ class Lag_interfaces(ConfigBase): def build_create_payload_member(self, name): payload_template = """{\n"openconfig-if-aggregate:aggregate-id": "{{name}}"\n}""" - temp = name.split("PortChannel", 1)[1] - input_data = {"name": temp} + input_data = {"name": name} env = jinja2.Environment(autoescape=False) t = env.from_string(payload_template) intended_payload = t.render(input_data) @@ -419,3 +456,21 @@ class Lag_interfaces(ConfigBase): requests.append(request) return requests + + def sort_config(self, configs): + # natsort provides better result. + # The use of natsort causes sanity error due to it is not available in + # python version currently used. + # new_config = natsorted(new_config, key=lambda x: x['name']) + # For time-being, use simple "sort" + configs.sort(key=lambda x: x['name']) + + for conf in configs: + if conf.get('members', {}) and conf['members'].get('interfaces', []): + conf['members']['interfaces'].sort(key=lambda x: x['member']) + + def prune_commands(self, commands): + cmds = deepcopy(commands) + for cmd in cmds: + cmd.pop('members', None) + return cmds diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lldp_global/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lldp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lldp_global/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lldp_global/lldp_global.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lldp_global/lldp_global.py new file mode 100644 index 000000000..f27d63e81 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lldp_global/lldp_global.py @@ -0,0 +1,296 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_lldp_global class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts + +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + get_diff, + update_states +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible.module_utils.connection import ConnectionError + + +PATCH = 'patch' +DELETE = 'delete' + + +class Lldp_global(ConfigBase): + """ + The sonic_lldp_global class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'lldp_global', + ] + + lldp_global_path = 'data/openconfig-lldp:lldp/config' + lldp_global_config_path = { + 'enable': lldp_global_path + '/enabled', + 'hello_time': lldp_global_path + '/hello-timer', + 'mode': lldp_global_path + '/openconfig-lldp-ext:mode', + 'multiplier': lldp_global_path + '/openconfig-lldp-ext:multiplier', + 'system_description': lldp_global_path + '/system-description', + 'system_name': lldp_global_path + '/system-name', + 'tlv_select': lldp_global_path + '/suppress-tlv-advertisement', + } + lldp_suppress_tlv = '/data/openconfig-lldp:lldp/config/suppress-tlv-advertisement={lldp_suppress_tlv}' + + def __init__(self, module): + super(Lldp_global, self).__init__(module) + + def get_lldp_global_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + lldp_global_facts = facts['ansible_network_resources'].get('lldp_global') + if not lldp_global_facts: + return [] + return lldp_global_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = [] + + existing_lldp_global_facts = self.get_lldp_global_facts() + commands, requests = self.set_config(existing_lldp_global_facts) + if commands: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + result['changed'] = True + + changed_lldp_global_facts = self.get_lldp_global_facts() + + result['before'] = existing_lldp_global_facts + if result['changed']: + result['after'] = changed_lldp_global_facts + + result['commands'] = commands + result['warnings'] = warnings + return result + + def set_config(self, existing_lldp_global_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + have = existing_lldp_global_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + state = self._module.params['state'] + diff = get_diff(want, have) + if state == 'deleted': + commands, requests = self._state_deleted(want, have, diff) + elif state == 'merged': + commands, requests = self._state_merged(diff) + return commands, requests + + def _state_merged(self, diff): + """ The command generator when state is merged + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = diff + requests = [] + requests.extend(self.get_modify_specific_lldp_global_param_requests(commands)) + if commands and len(requests) > 0: + commands = update_states(commands, 'merged') + else: + commands = [] + + return commands, requests + + def _state_deleted(self, want, have, diff): + """ The command generator when state is deleted + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + requests = [] + + if not want: + commands = have + requests.extend(self.get_delete_lldp_global_completely_requests(commands)) + else: + commands = get_diff(want, diff) + requests.extend(self.get_delete_specific_lldp_global_param_requests(commands, have)) + + if len(requests) == 0: + commands = [] + + if commands: + commands = update_states(commands, "deleted") + + return commands, requests + + def get_modify_specific_lldp_global_param_requests(self, command): + """Get requests to modify specific LLDP Global configurations + based on the command specified for the interface + """ + requests = [] + + if not command: + return requests + if 'enable' in command and command['enable'] is not None: + payload = {'openconfig-lldp:enabled': command['enable']} + url = self.lldp_global_config_path['enable'] + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if 'hello_time' in command and command['hello_time'] is not None: + payload = {'openconfig-lldp:hello-timer': str(command['hello_time'])} + url = self.lldp_global_config_path['hello_time'] + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if 'mode' in command and command['mode'] is not None: + payload = {'openconfig-lldp-ext:mode': command['mode'].upper()} + url = self.lldp_global_config_path['mode'] + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if 'multiplier' in command and command['multiplier'] is not None: + payload = {'openconfig-lldp-ext:multiplier': int(command['multiplier'])} + url = self.lldp_global_config_path['multiplier'] + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if 'system_name' in command and command['system_name'] is not None: + payload = {'openconfig-lldp:system-name': command['system_name']} + url = self.lldp_global_config_path['system_name'] + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if 'system_description' in command and command['system_description'] is not None: + payload = {'openconfig-lldp:system-description': command['system_description']} + url = self.lldp_global_config_path['system_description'] + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + if 'tlv_select' in command: + if 'management_address' in command['tlv_select']: + payload = {'openconfig-lldp:suppress-tlv-advertisement': ["MANAGEMENT_ADDRESS"]} + url = self.lldp_global_config_path['tlv_select'] + if command['tlv_select']['management_address'] is False: + requests.append({'path': url, 'method': PATCH, 'data': payload}) + elif command['tlv_select']['management_address'] is True: + url = self.lldp_suppress_tlv.format(lldp_suppress_tlv="MANAGEMENT_ADDRESS") + requests.append({'path': url, 'method': DELETE}) + if 'system_capabilities' in command['tlv_select']: + payload = {'openconfig-lldp:suppress-tlv-advertisement': ["SYSTEM_CAPABILITIES"]} + url = self.lldp_global_config_path['tlv_select'] + if command['tlv_select']['system_capabilities'] is False: + requests.append({'path': url, 'method': PATCH, 'data': payload}) + elif command['tlv_select']['system_capabilities'] is True: + url = self.lldp_suppress_tlv.format(lldp_suppress_tlv="SYSTEM_CAPABILITIES") + requests.append({'path': url, 'method': DELETE}) + return requests + + def get_delete_lldp_global_completely_requests(self, have): + """Get requests to delete all existing LLDP global + configurations in the chassis + """ + default_config_dict = {"enable": True, "tlv_select": {"management_address": True, "system_capabilities": True}} + requests = [] + if default_config_dict != have: + return [{'path': self.lldp_global_path, 'method': DELETE}] + return requests + + def get_delete_specific_lldp_global_param_requests(self, command, config): + """Get requests to delete specific LLDP global configurations + based on the command specified for the interface + """ + requests = [] + + if not command: + return requests + if 'hello_time' in command: + url = self.lldp_global_config_path['hello_time'] + requests.append({'path': url, 'method': DELETE}) + + if 'enable' in command: + url = self.lldp_global_config_path['enable'] + if command['enable'] is False: + payload = {'openconfig-lldp:enabled': True} + elif command['enable'] is True: + payload = {'openconfig-lldp:enabled': False} + requests.append({'path': url, 'method': PATCH, 'data': payload}) + if 'mode' in command: + url = self.lldp_global_config_path['mode'] + requests.append({'path': url, 'method': DELETE}) + + if 'multiplier' in command: + url = self.lldp_global_config_path['multiplier'] + requests.append({'path': url, 'method': DELETE}) + + if 'system_name' in command: + url = self.lldp_global_config_path['system_name'] + requests.append({'path': url, 'method': DELETE}) + + if 'system_description' in command: + url = self.lldp_global_config_path['system_description'] + requests.append({'path': url, 'method': DELETE}) + # The tlv_select configs are enabled by default.Hence false leads deletion of configs. + if 'tlv_select' in command: + if 'management_address' in command['tlv_select']: + payload = {'openconfig-lldp:suppress-tlv-advertisement': ["MANAGEMENT_ADDRESS"]} + url = self.lldp_global_config_path['tlv_select'] + if command['tlv_select']['management_address'] is True: + requests.append({'path': url, 'method': PATCH, 'data': payload}) + elif command['tlv_select']['management_address'] is False: + url = self.lldp_suppress_tlv.format(lldp_suppress_tlv="MANAGEMENT_ADDRESS") + requests.append({'path': url, 'method': DELETE}) + if 'system_capabilities' in command['tlv_select']: + payload = {'openconfig-lldp:suppress-tlv-advertisement': ["SYSTEM_CAPABILITIES"]} + url = self.lldp_global_config_path['tlv_select'] + if command['tlv_select']['system_capabilities'] is True: + requests.append({'path': url, 'method': PATCH, 'data': payload}) + elif command['tlv_select']['system_capabilities'] is False: + url = self.lldp_suppress_tlv.format(lldp_suppress_tlv="SYSTEM_CAPABILITIES") + requests.append({'path': url, 'method': DELETE}) + return requests diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/logging/logging.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/logging/logging.py new file mode 100644 index 000000000..82262b561 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/logging/logging.py @@ -0,0 +1,458 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_logging class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + get_diff, + update_states, + get_normalize_interface_name, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff +) +from ansible.module_utils.connection import ConnectionError + +PATCH = 'PATCH' +DELETE = 'DELETE' + +DEFAULT_REMOTE_PORT = 514 +DEFAULT_LOG_TYPE = 'log' + +TEST_KEYS = [ + { + "remote_servers": {"host": ""} + } +] +TEST_KEYS_formatted_diff = [ + { + "remote_servers": {"host": "", '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG} + } +] + + +class Logging(ConfigBase): + """ + The sonic_logging class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'logging', + ] + + def __init__(self, module): + super(Logging, self).__init__(module) + + def get_logging_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + logging_facts = facts['ansible_network_resources'].get('logging') + if not logging_facts: + return [] + return logging_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = list() + commands = list() + requests = list() + + existing_logging_facts = self.get_logging_facts() + + commands, requests = self.set_config(existing_logging_facts) + if commands and len(requests) > 0: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + result['changed'] = True + result['commands'] = commands + + changed_logging_facts = self.get_logging_facts() + + result['before'] = existing_logging_facts + if result['changed']: + result['after'] = changed_logging_facts + + new_config = changed_logging_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_logging_facts, + TEST_KEYS_formatted_diff) + result['after(generated)'] = new_config + + if self._module._diff: + result['diff'] = get_formatted_config_diff(existing_logging_facts, + new_config, + self._module._verbosity) + result['warnings'] = warnings + return result + + def set_config(self, existing_logging_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + if want is None: + want = [] + + have = existing_logging_facts + resp = self.set_state(want, have) + + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + state = self._module.params['state'] + + self.validate_want(want, state) + self.preprocess_want(want, state) + + if state == 'deleted': + commands, requests = self._state_deleted(want, have) + elif state == 'merged': + commands, requests = self._state_merged(want, have) + elif state == 'overridden': + commands, requests = self._state_overridden(want, have) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have) + + return commands, requests + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :param want: the additive configuration as a dictionary + :param have: the current configuration as a dictionary + :returns: the commands necessary to merge the provided into + the current configuration + """ + diff = get_diff(want, have, TEST_KEYS) + + commands = diff + requests = [] + if commands: + requests = self.get_merge_requests(commands, have) + + if len(requests) > 0: + commands = update_states(commands, "merged") + else: + commands = [] + + return commands, requests + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :param want: the objects from which the configuration should be removed + :param have: the current configuration as a dictionary + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + # Get a list of requested servers to delete that are not present in the current + # configuration on the device. This list can be used to filter out these + # unconfigured servers from the list of "delete" commands to be sent to the switch. + unconfigured = get_diff(want, have, TEST_KEYS) + + want_none = {'remote_servers': None} + want_any = get_diff(want, want_none, TEST_KEYS) + # if want_any is none, then delete all NTP configurations + + delete_all = False + if not want_any: + commands = have + delete_all = True + else: + if not unconfigured: + commands = want_any + else: + # Some of the servers requested for deletion are not in the current + # device configuration. Filter these out of the list to be used for sending + # "delete" commands to the device. + commands = get_diff(want_any, unconfigured, TEST_KEYS) + + requests = [] + if commands: + requests = self.get_delete_requests(commands, delete_all) + + if len(requests) > 0: + commands = update_states(commands, "deleted") + else: + commands = [] + + return commands, requests + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + + replaced_config = self.get_replaced_config(have, want) + if 'remote_servers' in replaced_config: + replaced_config['remote_servers'].sort(key=self.get_host) + if 'remote_servers' in want: + want['remote_servers'].sort(key=self.get_host) + + if replaced_config and replaced_config != want: + delete_all = False + del_requests = self.get_delete_requests(replaced_config, delete_all) + requests.extend(del_requests) + commands.extend(update_states(replaced_config, "deleted")) + replaced_config = [] + + if not replaced_config and want: + add_commands = want + add_requests = self.get_merge_requests(add_commands, replaced_config) + + if len(add_requests) > 0: + requests.extend(add_requests) + commands.extend(update_states(add_commands, "replaced")) + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + if 'remote_servers' in have: + have['remote_servers'].sort(key=self.get_host) + if 'remote_servers' in want: + want['remote_servers'].sort(key=self.get_host) + + commands = [] + requests = [] + + if have and have != want: + delete_all = True + del_requests = self.get_delete_requests(have, delete_all) + requests.extend(del_requests) + commands.extend(update_states(have, "deleted")) + have = [] + + if not have and want: + add_commands = want + add_requests = self.get_merge_requests(add_commands, have) + + if len(add_requests) > 0: + requests.extend(add_requests) + commands.extend(update_states(add_commands, "overridden")) + + return commands, requests + + def get_host(self, remote_server): + return remote_server.get('host') + + def search_config_servers(self, host, servers): + + if servers is not None: + for server in servers: + if server['host'] == host: + return server + return [] + + def get_replaced_config(self, have, want): + + replaced_config = dict() + replaced_servers = [] + if 'remote_servers' in have and 'remote_servers' in want: + for server in want['remote_servers']: + replaced_server = self.search_config_servers(server['host'], have['remote_servers']) + if replaced_server: + replaced_servers.append(replaced_server) + + replaced_config['remote_servers'] = replaced_servers + return replaced_config + + def validate_want(self, want, state): + + if state == 'deleted': + + if 'remote_servers' in want and want['remote_servers'] is not None: + for server in want['remote_servers']: + source_interface_config = server.get('source_interface', None) + remote_port_config = server.get('remote_port', None) + message_type_config = server.get('message_type', None) + vrf_config = server.get('vrf', None) + if source_interface_config or remote_port_config or \ + message_type_config or vrf_config: + err_msg = "Logging remote_server parameter(s) can not be deleted." + self._module.fail_json(msg=err_msg, code=405) + + def preprocess_want(self, want, state): + + if state == 'merged': + if 'remote_servers' in want and want['remote_servers'] is not None: + for server in want['remote_servers']: + if 'source_interface' in server and not server['source_interface']: + server.pop('source_interface', None) + else: + server['source_interface'] = \ + get_normalize_interface_name(server['source_interface'], self._module) + if 'remote_port' in server and not server['remote_port']: + server.pop('remote_port', None) + if 'message_type' in server and not server['message_type']: + server.pop('message_type', None) + if 'vrf' in server and not server['vrf']: + server.pop('vrf', None) + + if state == 'replaced' or state == 'overridden': + if 'remote_servers' in want and want['remote_servers'] is not None: + for server in want['remote_servers']: + if 'source_interface' in server and not server['source_interface']: + server.pop('source_interface', None) + else: + server['source_interface'] = \ + get_normalize_interface_name(server['source_interface'], self._module) + if 'remote_port' in server and not server['remote_port']: + server['remote_port'] = DEFAULT_REMOTE_PORT + if 'message_type' in server and not server['message_type']: + server['message_type'] = DEFAULT_LOG_TYPE + + def get_merge_requests(self, configs, have): + + requests = [] + + servers_config = configs.get('remote_servers', None) + if servers_config: + servers_request = self.get_create_servers_requests(servers_config, have) + if servers_request: + requests.extend(servers_request) + + return requests + + def get_delete_requests(self, configs, delete_all): + + requests = [] + + servers_config = configs.get('remote_servers', None) + if servers_config: + servers_request = [] + if delete_all: + servers_request = self.get_delete_all_servers_requests() + else: + servers_request = self.get_delete_servers_requests(servers_config) + + if servers_request: + requests.extend(servers_request) + + return requests + + def get_create_servers_requests(self, configs, have): + + requests = [] + + # Create URL and payload + method = PATCH + url = 'data/openconfig-system:system/logging/remote-servers' + server_configs = [] + for config in configs: + req_config = dict() + req_config['host'] = config['host'] + if 'source_interface' in config: + req_config['source-interface'] = config['source_interface'] + if 'message_type' in config: + req_config['message-type'] = config['message_type'] + if 'remote_port' in config: + req_config['remote-port'] = config['remote_port'] + if 'vrf' in config: + req_config['vrf-name'] = config['vrf'] + + server_host = config['host'] + server_config = {"host": server_host, "config": req_config} + server_configs.append(server_config) + + payload = {"openconfig-system:remote-servers": {"remote-server": server_configs}} + request = {"path": url, "method": method, "data": payload} + requests.append(request) + + return requests + + def get_delete_servers_requests(self, configs): + + requests = [] + + # Create URL and payload + method = DELETE + for config in configs: + server_host = config['host'] + url = 'data/openconfig-system:system/logging/remote-servers/remote-server={0}'.format(server_host) + request = {"path": url, "method": method} + requests.append(request) + + return requests + + def get_delete_all_servers_requests(self): + + requests = [] + + # Create URL and payload + method = DELETE + url = 'data/openconfig-system:system/logging/remote-servers' + request = {"path": url, "method": method} + requests.append(request) + + return requests diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mac/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mac/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mac/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mac/mac.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mac/mac.py new file mode 100644 index 000000000..866ff3934 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mac/mac.py @@ -0,0 +1,431 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_mac class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + update_states, + get_diff, + get_replaced_config, + send_requests +) + +NETWORK_INSTANCE_PATH = '/data/openconfig-network-instance:network-instances/network-instance' +PATCH = 'patch' +DELETE = 'delete' +TEST_KEYS = [ + {'config': {'vrf_name': ''}}, + {'mac_table_entries': {'mac_address': '', 'vlan_id': ''}} +] + + +class Mac(ConfigBase): + """ + The sonic_mac class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'mac', + ] + + def __init__(self, module): + super(Mac, self).__init__(module) + + def get_mac_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + mac_facts = facts['ansible_network_resources'].get('mac') + if not mac_facts: + return [] + return mac_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = [] + commands = [] + + existing_mac_facts = self.get_mac_facts() + commands, requests = self.set_config(existing_mac_facts) + if commands and len(requests) > 0: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + result['changed'] = True + result['commands'] = commands + + changed_mac_facts = self.get_mac_facts() + + result['before'] = existing_mac_facts + if result['changed']: + result['after'] = changed_mac_facts + + result['warnings'] = warnings + return result + + def set_config(self, existing_mac_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + have = existing_mac_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + state = self._module.params['state'] + + diff = get_diff(want, have, TEST_KEYS) + + if state == 'overridden': + commands, requests = self._state_overridden(want, have) + elif state == 'deleted': + commands, requests = self._state_deleted(want, have) + elif state == 'merged': + commands, requests = self._state_merged(diff) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have, diff) + return commands, requests + + def _state_replaced(self, want, have, diff): + """ The command generator when state is replaced + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + replaced_config = get_replaced_config(want, have, TEST_KEYS) + + if replaced_config: + self.sort_lists_in_config(replaced_config) + self.sort_lists_in_config(have) + is_delete_all = (replaced_config == have) + requests = self.get_delete_mac_requests(replaced_config, have, is_delete_all) + send_requests(self._module, requests) + + commands = want + else: + commands = diff + + requests = [] + + if commands: + requests = self.get_modify_mac_requests(commands) + + if len(requests) > 0: + commands = update_states(commands, "replaced") + else: + commands = [] + else: + commands = [] + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + self.sort_lists_in_config(want) + self.sort_lists_in_config(have) + + if have and have != want: + is_delete_all = True + requests = self.get_delete_mac_requests(have, None, is_delete_all) + send_requests(self._module, requests) + have = [] + + commands = [] + requests = [] + + if not have and want: + commands = want + requests = self.get_modify_mac_requests(commands) + + if len(requests) > 0: + commands = update_states(commands, "overridden") + else: + commands = [] + + return commands, requests + + def _state_merged(self, diff): + """ The command generator when state is merged + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = diff + requests = self.get_modify_mac_requests(commands) + + if commands and len(requests) > 0: + commands = update_states(commands, "merged") + else: + commands = [] + + return commands, requests + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + is_delete_all = False + # if want is none, then delete ALL + if not want: + commands = have + is_delete_all = True + else: + commands = want + + commands = self.remove_default_entries(commands) + requests = self.get_delete_mac_requests(commands, have, is_delete_all) + + if commands and len(requests) > 0: + commands = update_states(commands, "deleted") + else: + commands = [] + + return commands, requests + + def get_modify_mac_requests(self, commands): + + requests = [] + + if not commands: + return requests + + for cmd in commands: + vrf_name = cmd.get('vrf_name', None) + mac = cmd.get('mac', {}) + if mac: + aging_time = mac.get('aging_time', None) + dampening_interval = mac.get('dampening_interval', None) + dampening_threshold = mac.get('dampening_threshold', None) + mac_table_entries = mac.get('mac_table_entries', []) + fdb_dict = {} + dampening_cfg_dict = {} + if aging_time: + fdb_dict['config'] = {'mac-aging-time': aging_time} + if dampening_interval: + dampening_cfg_dict['interval'] = dampening_interval + if dampening_threshold: + dampening_cfg_dict['threshold'] = dampening_threshold + if mac_table_entries: + entry_list = [] + entries_dict = {} + mac_table_dict = {} + for entry in mac_table_entries: + entry_dict = {} + entry_cfg_dict = {} + mac_address = entry.get('mac_address', None) + vlan_id = entry.get('vlan_id', None) + interface = entry.get('interface', None) + if mac_address: + entry_dict['mac-address'] = mac_address + entry_cfg_dict['mac-address'] = mac_address + if vlan_id: + entry_dict['vlan'] = vlan_id + entry_cfg_dict['vlan'] = vlan_id + if entry_cfg_dict: + entry_dict['config'] = entry_cfg_dict + if interface: + entry_dict['interface'] = {'interface-ref': {'config': {'interface': interface, 'subinterface': 0}}} + if entry_dict: + entry_list.append(entry_dict) + if entry_list: + entries_dict['entry'] = entry_list + if entries_dict: + mac_table_dict['entries'] = entries_dict + if mac_table_dict: + fdb_dict['mac-table'] = mac_table_dict + if fdb_dict: + url = '%s=%s/fdb' % (NETWORK_INSTANCE_PATH, vrf_name) + payload = {'openconfig-network-instance:fdb': fdb_dict} + requests.append({'path': url, 'method': PATCH, 'data': payload}) + if dampening_cfg_dict: + url = '%s=%s/openconfig-mac-dampening:mac-dampening' % (NETWORK_INSTANCE_PATH, vrf_name) + payload = {'openconfig-mac-dampening:mac-dampening': {'config': dampening_cfg_dict}} + requests.append({'path': url, 'method': PATCH, 'data': payload}) + + return requests + + def get_delete_mac_requests(self, commands, have, is_delete_all): + requests = [] + + for cmd in commands: + vrf_name = cmd.get('vrf_name', None) + if vrf_name and is_delete_all: + requests.extend(self.get_delete_all_mac_requests(vrf_name)) + else: + mac = cmd.get('mac', {}) + if mac: + aging_time = mac.get('aging_time', None) + dampening_interval = mac.get('dampening_interval', None) + dampening_threshold = mac.get('dampening_threshold', None) + mac_table_entries = mac.get('mac_table_entries', []) + + for cfg in have: + cfg_vrf_name = cfg.get('vrf_name', None) + cfg_mac = cfg.get('mac', {}) + if cfg_mac: + cfg_aging_time = cfg_mac.get('aging_time', None) + cfg_dampening_interval = cfg_mac.get('dampening_interval', None) + cfg_dampening_threshold = cfg_mac.get('dampening_threshold', None) + cfg_mac_table_entries = cfg_mac.get('mac_table_entries', []) + + if vrf_name and vrf_name == cfg_vrf_name: + if aging_time and aging_time == cfg_aging_time: + requests.append(self.get_delete_fdb_cfg_attr(vrf_name, 'mac-aging-time')) + if dampening_interval and dampening_interval == cfg_dampening_interval: + requests.append(self.get_delete_mac_dampening_attr(vrf_name, 'interval')) + if dampening_threshold and dampening_threshold == cfg_dampening_threshold: + requests.append(self.get_delete_mac_dampening_attr(vrf_name, 'threshold')) + + if mac_table_entries: + for entry in mac_table_entries: + mac_address = entry.get('mac_address', None) + vlan_id = entry.get('vlan_id', None) + interface = entry.get('interface', None) + + if cfg_mac_table_entries: + for cfg_entry in cfg_mac_table_entries: + cfg_mac_address = cfg_entry.get('mac_address', None) + cfg_vlan_id = cfg_entry.get('vlan_id', None) + cfg_interface = cfg_entry.get('interface', None) + if mac_address and vlan_id and mac_address == cfg_mac_address and vlan_id == cfg_vlan_id: + if interface and interface == cfg_interface: + requests.append(self.get_delete_mac_table_intf(vrf_name, mac_address, vlan_id)) + elif not interface: + requests.append(self.get_delete_mac_table_entry(vrf_name, mac_address, vlan_id)) + return requests + + def get_delete_all_mac_requests(self, vrf_name): + requests = [] + url = '%s=%s/fdb' % (NETWORK_INSTANCE_PATH, vrf_name) + requests.append({'path': url, 'method': DELETE}) + url = '%s=%s/openconfig-mac-dampening:mac-dampening' % (NETWORK_INSTANCE_PATH, vrf_name) + requests.append({'path': url, 'method': DELETE}) + + return requests + + def get_delete_fdb_cfg_attr(self, vrf_name, attr): + url = '%s=%s/fdb/config/%s' % (NETWORK_INSTANCE_PATH, vrf_name, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_mac_dampening_attr(self, vrf_name, attr): + url = '%s=%s/openconfig-mac-dampening:mac-dampening/config/%s' % (NETWORK_INSTANCE_PATH, vrf_name, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_mac_table_entry(self, vrf_name, mac_address, vlan_id): + url = '%s=%s/fdb/mac-table/entries/entry=%s,%s' % (NETWORK_INSTANCE_PATH, vrf_name, mac_address, vlan_id) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_mac_table_intf(self, vrf_name, mac_address, vlan_id): + url = '%s=%s/fdb/mac-table/entries/entry=%s,%s/interface' % (NETWORK_INSTANCE_PATH, vrf_name, mac_address, vlan_id) + request = {'path': url, 'method': DELETE} + + return request + + def get_mac_vrf_name(self, vrf_name): + return vrf_name.get('vrf_name') + + def sort_lists_in_config(self, config): + if config: + config.sort(key=self.get_mac_vrf_name) + for cfg in config: + if 'mac' in cfg and cfg['mac'] is not None: + if 'mac_table_entries' in cfg['mac'] and cfg['mac']['mac_table_entries'] is not None: + cfg['mac']['mac_table_entries'].sort(key=lambda x: (x['mac_address'], x['vlan_id'])) + + def remove_default_entries(self, data): + new_data = [] + + if not data: + return new_data + + for conf in data: + new_conf = {} + vrf_name = conf.get('vrf_name', None) + mac = conf.get('mac', None) + if mac: + new_mac = {} + aging_time = mac.get('aging_time', None) + dampening_interval = mac.get('dampening_interval', None) + dampening_threshold = mac.get('dampening_threshold', None) + mac_table_entries = mac.get('mac_table_entries', None) + + if aging_time and aging_time != 600: + new_mac['aging_time'] = aging_time + if dampening_interval and dampening_interval != 5: + new_mac['dampening_interval'] = dampening_interval + if dampening_threshold and dampening_threshold != 5: + new_mac['dampening_threshold'] = dampening_threshold + if mac_table_entries is not None: + new_mac['mac_table_entries'] = mac_table_entries + if new_mac: + new_conf['mac'] = new_mac + new_conf['vrf_name'] = vrf_name + if new_conf: + new_data.append(new_conf) + + return new_data diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mclag/mclag.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mclag/mclag.py index 88215e8fc..68c99f0ab 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mclag/mclag.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mclag/mclag.py @@ -14,16 +14,21 @@ created from __future__ import absolute_import, division, print_function __metaclass__ = type + +import re +from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, to_list ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( update_states, get_diff, + get_ranges_in_list, get_normalize_interface_name, normalize_interface_name ) @@ -57,6 +62,17 @@ class Mclag(ConfigBase): 'mclag', ] + mclag_simple_attrs = set({ + 'peer_address', + 'source_address', + 'peer_link', + 'system_mac', + 'keepalive', + 'session_timeout', + 'delay_restore', + 'gateway_mac' + }) + def __init__(self, module): super(Mclag, self).__init__(module) @@ -123,6 +139,11 @@ class Mclag(ConfigBase): vlans_list = unique_ip['vlans'] if vlans_list: normalize_interface_name(vlans_list, self._module, 'vlan') + peer_gateway = want.get('peer_gateway', None) + if peer_gateway: + vlans_list = peer_gateway['vlans'] + if vlans_list: + normalize_interface_name(vlans_list, self._module, 'vlan') members = want.get('members', None) if members: portchannels_list = members['portchannels'] @@ -143,11 +164,13 @@ class Mclag(ConfigBase): """ state = self._module.params['state'] if state == 'deleted': - commands = self._state_deleted(want, have) + commands, requests = self._state_deleted(want, have) elif state == 'merged': diff = get_diff(want, have, TEST_KEYS) - commands = self._state_merged(want, have, diff) - return commands + commands, requests = self._state_merged(want, have, diff) + elif state in ('replaced', 'overridden'): + commands, requests = self._state_replaced_overridden(want, have, state) + return commands, requests def _state_merged(self, want, have, diff): """ The command generator when state is merged @@ -159,7 +182,21 @@ class Mclag(ConfigBase): requests = [] commands = [] if diff: - requests = self.get_create_mclag_request(want, diff) + # Obtain diff for VLAN ranges in unique_ip + if 'unique_ip' in diff and diff['unique_ip'] is not None and diff['unique_ip'].get('vlans'): + if 'unique_ip' in have and have['unique_ip'] is not None and have['unique_ip'].get('vlans'): + diff['unique_ip']['vlans'] = self.get_vlan_range_diff(diff['unique_ip']['vlans'], have['unique_ip']['vlans']) + if not diff['unique_ip']['vlans']: + diff.pop('unique_ip') + + # Obtain diff for VLAN ranges in peer_gateway + if 'peer_gateway' in diff and diff['peer_gateway'] is not None and diff['peer_gateway'].get('vlans'): + if 'peer_gateway' in have and have['peer_gateway'] is not None and have['peer_gateway'].get('vlans'): + diff['peer_gateway']['vlans'] = self.get_vlan_range_diff(diff['peer_gateway']['vlans'], have['peer_gateway']['vlans']) + if not diff['peer_gateway']['vlans']: + diff.pop('peer_gateway') + + requests = self.get_create_mclag_requests(want, diff) if len(requests) > 0: commands = update_states(diff, "merged") return commands, requests @@ -175,19 +212,159 @@ class Mclag(ConfigBase): requests = [] if not want: if have: - requests = self.get_delete_all_mclag_domain_request() + requests = self.get_delete_all_mclag_domain_requests(have) if len(requests) > 0: commands = update_states(have, "deleted") else: + del_unique_ip_vlans = [] + del_peer_gateway_vlans = [] + # Create list of VLANs to be deleted based on VLAN ranges in unique_ip + if 'unique_ip' in want and want['unique_ip'] is not None and want['unique_ip'].get('vlans'): + want_unique_ip = want.pop('unique_ip') + if 'unique_ip' in have and have['unique_ip'] is not None and have['unique_ip'].get('vlans'): + del_unique_ip_vlans = self.get_vlan_range_common(want_unique_ip['vlans'], have['unique_ip']['vlans']) + + # Create list of VLANs to be deleted based on VLAN ranges in peer_gateway + if 'peer_gateway' in want and want['peer_gateway'] is not None and want['peer_gateway'].get('vlans'): + want_peer_gateway = want.pop('peer_gateway') + if 'peer_gateway' in have and have['peer_gateway'] is not None and have['peer_gateway'].get('vlans'): + del_peer_gateway_vlans = self.get_vlan_range_common(want_peer_gateway['vlans'], have['peer_gateway']['vlans']) + new_have = self.remove_default_entries(have) d_diff = get_diff(want, new_have, TEST_KEYS, is_skeleton=True) diff_want = get_diff(want, d_diff, TEST_KEYS, is_skeleton=True) + + if del_unique_ip_vlans: + diff_want['unique_ip'] = {'vlans': del_unique_ip_vlans} + if del_peer_gateway_vlans: + diff_want['peer_gateway'] = {'vlans': del_peer_gateway_vlans} + if diff_want: - requests = self.get_delete_mclag_attribute_request(want, diff_want) + requests = self.get_delete_mclag_attribute_requests(have['domain_id'], diff_want) if len(requests) > 0: commands = update_states(diff_want, "deleted") return commands, requests + def _state_replaced_overridden(self, want, have, state): + """ The command generator when state is replaced/overridden + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + requests = [] + if want and not have: + commands = [update_states(want, state)] + requests = self.get_create_mclag_requests(want, want) + elif not want and have: + commands = [update_states(have, 'deleted')] + requests = self.get_delete_all_mclag_domain_requests(have) + elif want and have: + add_command = {} + del_command = {} + delete_all = False + + # If 'domain_id' is modified, delete all mclag configuration. + if want['domain_id'] != have['domain_id']: + del_command = have + add_command = want + delete_all = True + else: + have = have.copy() + want = want.copy() + delete_all_vlans = { + 'unique_ip': False, + 'peer_gateway': False + } + + # Delete unspecified configurations when: + # 1) state is overridden. + # 2) state is replaced and configuration other than + # unique_ip, peer_gateway or members is specified. + delete_unspecified = True + if state == 'replaced' and not self.mclag_simple_attrs.intersection(remove_empties(want).keys()): + delete_unspecified = False + + # Create lists of VLANs to be deleted and added based on VLAN ranges + for option in ('unique_ip', 'peer_gateway'): + have_cfg = {} + want_cfg = {} + # The options are removed from the dict to avoid + # comparing the VLAN ranges two more times using get_diff + if have.get(option) and have[option].get('vlans'): + have_cfg = have.pop(option) + if want.get(option) and 'vlans' in want[option]: + want_cfg = want.pop(option) + + if want_cfg: + if have_cfg: + # Delete all VLANs if empty 'vlans' list is provided + if not want_cfg['vlans']: + delete_all_vlans[option] = True + del_command[option] = have_cfg + else: + have_vlans = set(self.get_vlan_id_list(have_cfg['vlans'])) + want_vlans = set(self.get_vlan_id_list(want_cfg['vlans'])) + if have_vlans.intersection(want_vlans): + del_command[option] = {'vlans': self.get_vlan_range_list(list(have_vlans - want_vlans))} + if not del_command[option]['vlans']: + del_command.pop(option) + add_command[option] = {'vlans': self.get_vlan_range_list(list(want_vlans - have_vlans))} + if not add_command[option]['vlans']: + add_command.pop(option) + else: + delete_all_vlans[option] = True + del_command[option] = have_cfg + add_command[option] = want_cfg + else: + if want_cfg['vlans']: + add_command[option] = want_cfg + else: + if have_cfg and delete_unspecified: + delete_all_vlans[option] = True + del_command[option] = have_cfg + + del_diff = get_diff(self.remove_default_entries(have), want, TEST_KEYS) + for option in del_diff: + if not want.get(option): + if delete_unspecified: + del_command[option] = del_diff[option] + else: + # Delete portchannels that are not specified + if option == 'members' and want.get(option): + del_command[option] = del_diff[option] + + # To update 'gateway_mac' configuration in the device, + # delete already configured value. + if option == 'gateway_mac' and want.get(option): + del_command[option] = del_diff[option] + + diff = get_diff(want, have, TEST_KEYS) + add_command.update(diff) + + if del_command: + del_command['domain_id'] = have['domain_id'] + commands.extend(update_states(del_command, 'deleted')) + if delete_all: + requests = self.get_delete_all_mclag_domain_requests(del_command) + else: + if any(delete_all_vlans.values()): + del_command = deepcopy(del_command) + + # Set 'vlans' to None to delete all VLANs + for option in delete_all_vlans: + if delete_all_vlans[option]: + del_command[option]['vlans'] = None + requests = self.get_delete_mclag_attribute_requests(del_command['domain_id'], del_command) + + if add_command: + add_command['domain_id'] = want['domain_id'] + commands.extend(update_states(add_command, state)) + requests.extend(self.get_create_mclag_requests(add_command, add_command)) + + return commands, requests + def remove_default_entries(self, data): new_data = {} if not data: @@ -196,6 +373,7 @@ class Mclag(ConfigBase): default_val_dict = { 'keepalive': 1, 'session_timeout': 30, + 'delay_restore': 300 } for key, val in data.items(): if not (val is None or (key in default_val_dict and val == default_val_dict[key])): @@ -203,9 +381,9 @@ class Mclag(ConfigBase): return new_data - def get_delete_mclag_attribute_request(self, want, command): + def get_delete_mclag_attribute_requests(self, domain_id, command): requests = [] - url_common = 'data/openconfig-mclag:mclag/mclag-domains/mclag-domain=%s/config' % (want["domain_id"]) + url_common = 'data/openconfig-mclag:mclag/mclag-domains/mclag-domain=%s/config' % (domain_id) method = DELETE if 'source_address' in command and command["source_address"] is not None: url = url_common + '/source-address' @@ -231,16 +409,30 @@ class Mclag(ConfigBase): url = url_common + '/mclag-system-mac' request = {'path': url, 'method': method} requests.append(request) + if 'delay_restore' in command and command['delay_restore'] is not None: + url = url_common + '/delay-restore' + request = {'path': url, 'method': method} + requests.append(request) + if 'peer_gateway' in command and command['peer_gateway'] is not None: + if command['peer_gateway']['vlans'] is None: + request = {'path': 'data/openconfig-mclag:mclag/vlan-ifs/vlan-if', 'method': method} + requests.append(request) + elif command['peer_gateway']['vlans'] is not None: + vlan_id_list = self.get_vlan_id_list(command['peer_gateway']['vlans']) + for vlan in vlan_id_list: + peer_gateway_url = 'data/openconfig-mclag:mclag/vlan-ifs/vlan-if=Vlan{0}'.format(vlan) + request = {'path': peer_gateway_url, 'method': method} + requests.append(request) if 'unique_ip' in command and command['unique_ip'] is not None: if command['unique_ip']['vlans'] is None: request = {'path': 'data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface', 'method': method} requests.append(request) elif command['unique_ip']['vlans'] is not None: - for each in command['unique_ip']['vlans']: - if each: - unique_ip_url = 'data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=%s' % (each['vlan']) - request = {'path': unique_ip_url, 'method': method} - requests.append(request) + vlan_id_list = self.get_vlan_id_list(command['unique_ip']['vlans']) + for vlan in vlan_id_list: + unique_ip_url = 'data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=Vlan{0}'.format(vlan) + request = {'path': unique_ip_url, 'method': method} + requests.append(request) if 'members' in command and command['members'] is not None: if command['members']['portchannels'] is None: request = {'path': 'data/openconfig-mclag:mclag/interfaces/interface', 'method': method} @@ -251,17 +443,29 @@ class Mclag(ConfigBase): portchannel_url = 'data/openconfig-mclag:mclag/interfaces/interface=%s' % (each['lag']) request = {'path': portchannel_url, 'method': method} requests.append(request) + if 'gateway_mac' in command and command['gateway_mac'] is not None: + request = {'path': 'data/openconfig-mclag:mclag/mclag-gateway-macs/mclag-gateway-mac', 'method': method} + requests.append(request) return requests - def get_delete_all_mclag_domain_request(self): + def get_delete_all_mclag_domain_requests(self, have): requests = [] path = 'data/openconfig-mclag:mclag/mclag-domains' method = DELETE + if have.get('peer_gateway'): + request = {'path': 'data/openconfig-mclag:mclag/vlan-ifs/vlan-if', 'method': method} + requests.append(request) + if have.get('unique_ip'): + request = {'path': 'data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface', 'method': method} + requests.append(request) + if have.get('gateway_mac'): + request = {'path': 'data/openconfig-mclag:mclag/mclag-gateway-macs/mclag-gateway-mac', 'method': method} + requests.append(request) request = {'path': path, 'method': method} requests.append(request) return requests - def get_create_mclag_request(self, want, commands): + def get_create_mclag_requests(self, want, commands): requests = [] path = 'data/openconfig-mclag:mclag/mclag-domains/mclag-domain' method = PATCH @@ -269,6 +473,17 @@ class Mclag(ConfigBase): if payload: request = {'path': path, 'method': method, 'data': payload} requests.append(request) + if 'gateway_mac' in commands and commands['gateway_mac'] is not None: + gateway_mac_path = 'data/openconfig-mclag:mclag/mclag-gateway-macs/mclag-gateway-mac' + gateway_mac_method = PATCH + gateway_mac_payload = { + 'openconfig-mclag:mclag-gateway-mac': [{ + 'gateway-mac': commands['gateway_mac'], + 'config': {'gateway-mac': commands['gateway_mac']} + }] + } + request = {'path': gateway_mac_path, 'method': gateway_mac_method, 'data': gateway_mac_payload} + requests.append(request) if 'unique_ip' in commands and commands['unique_ip'] is not None: if commands['unique_ip']['vlans'] and commands['unique_ip']['vlans'] is not None: unique_ip_path = 'data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface' @@ -276,6 +491,13 @@ class Mclag(ConfigBase): unique_ip_payload = self.build_create_unique_ip_payload(commands['unique_ip']['vlans']) request = {'path': unique_ip_path, 'method': unique_ip_method, 'data': unique_ip_payload} requests.append(request) + if 'peer_gateway' in commands and commands['peer_gateway'] is not None: + if commands['peer_gateway']['vlans'] and commands['peer_gateway']['vlans'] is not None: + peer_gateway_path = 'data/openconfig-mclag:mclag/vlan-ifs/vlan-if' + peer_gateway_method = PATCH + peer_gateway_payload = self.build_create_peer_gateway_payload(commands['peer_gateway']['vlans']) + request = {'path': peer_gateway_path, 'method': peer_gateway_method, 'data': peer_gateway_payload} + requests.append(request) if 'members' in commands and commands['members'] is not None: if commands['members']['portchannels'] and commands['members']['portchannels'] is not None: portchannel_path = 'data/openconfig-mclag:mclag/interfaces/interface' @@ -299,6 +521,8 @@ class Mclag(ConfigBase): temp['peer-link'] = str(commands['peer_link']) if 'system_mac' in commands and commands['system_mac'] is not None: temp['openconfig-mclag:mclag-system-mac'] = str(commands['system_mac']) + if 'delay_restore' in commands and commands['delay_restore'] is not None: + temp['delay-restore'] = commands['delay_restore'] mclag_dict = {} if temp: domain_id = {"domain-id": want["domain_id"]} @@ -312,8 +536,18 @@ class Mclag(ConfigBase): def build_create_unique_ip_payload(self, commands): payload = {"openconfig-mclag:vlan-interface": []} - for each in commands: - payload['openconfig-mclag:vlan-interface'].append({"name": each['vlan'], "config": {"name": each['vlan'], "unique-ip-enable": "ENABLE"}}) + vlan_id_list = self.get_vlan_id_list(commands) + for vlan in vlan_id_list: + vlan_name = 'Vlan{0}'.format(vlan) + payload['openconfig-mclag:vlan-interface'].append({"name": vlan_name, "config": {"name": vlan_name, "unique-ip-enable": "ENABLE"}}) + return payload + + def build_create_peer_gateway_payload(self, commands): + payload = {"openconfig-mclag:vlan-if": []} + vlan_id_list = self.get_vlan_id_list(commands) + for vlan in vlan_id_list: + vlan_name = 'Vlan{0}'.format(vlan) + payload['openconfig-mclag:vlan-if'].append({"name": vlan_name, "config": {"name": vlan_name, "peer-gateway-enable": "ENABLE"}}) return payload def build_create_portchannel_payload(self, want, commands): @@ -321,3 +555,63 @@ class Mclag(ConfigBase): for each in commands: payload['openconfig-mclag:interface'].append({"name": each['lag'], "config": {"name": each['lag'], "mclag-domain-id": want['domain_id']}}) return payload + + def get_vlan_range_common(self, config_vlans, match_vlans): + """Returns the vlan ranges present in both 'config_vlans' + and 'match_vlans' in vlans spec format + """ + if not config_vlans: + return [] + + if not match_vlans: + return [] + + config_vlans = self.get_vlan_id_list(config_vlans) + match_vlans = self.get_vlan_id_list(match_vlans) + return self.get_vlan_range_list(list(set(config_vlans).intersection(set(match_vlans)))) + + def get_vlan_range_diff(self, config_vlans, match_vlans): + """Returns the vlan ranges present only in 'config_vlans' + and not in 'match_vlans' in vlans spec format + """ + if not config_vlans: + return [] + + if not match_vlans: + return config_vlans + + config_vlans = self.get_vlan_id_list(config_vlans) + match_vlans = self.get_vlan_id_list(match_vlans) + return self.get_vlan_range_list(list(set(config_vlans) - set(match_vlans))) + + @staticmethod + def get_vlan_id_list(vlan_range_list): + """Returns a list of all VLAN IDs specified in VLAN range list""" + vlan_id_list = [] + if vlan_range_list: + for vlan_range in vlan_range_list: + vlan_val = vlan_range['vlan'] + if '-' in vlan_val: + match = re.match(r'Vlan(\d+)-(\d+)', vlan_val) + if match: + vlan_id_list.extend(range(int(match.group(1)), int(match.group(2)) + 1)) + else: + # Single VLAN ID + match = re.match(r'Vlan(\d+)', vlan_val) + if match: + vlan_id_list.append(int(match.group(1))) + + return vlan_id_list + + @staticmethod + def get_vlan_range_list(vlan_id_list): + """Returns a list of VLAN ranges for given list of VLAN IDs + in vlans spec format""" + vlan_range_list = [] + + if vlan_id_list: + vlan_id_list.sort() + for vlan_range in get_ranges_in_list(vlan_id_list): + vlan_range_list.append({'vlan': 'Vlan{0}'.format('-'.join(map(str, (vlan_range[0], vlan_range[-1])[:len(vlan_range)])))}) + + return vlan_range_list diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/ntp/ntp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/ntp/ntp.py index a4fdc7e0a..b48fa54f6 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/ntp/ntp.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/ntp/ntp.py @@ -14,8 +14,6 @@ created from __future__ import absolute_import, division, print_function __metaclass__ = type -import re - from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -29,20 +27,30 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( get_diff, + get_replaced_config, update_states, - normalize_interface_name, normalize_interface_name_list ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + __DELETE_LEAFS_OR_CONFIG_IF_NO_NON_KEY_LEAF, + get_new_config, + get_formatted_config_diff +) + from ansible.module_utils.connection import ConnectionError PATCH = 'PATCH' DELETE = 'DELETE' TEST_KEYS = [ - { - "vrf": "", "enable_ntp_auth": "", "source_interfaces": "", "trusted_keys": "", - "servers": {"address": ""}, "ntp_keys": {"key_id": ""} - } + {"servers": {"address": ""}}, + {"ntp_keys": {"key_id": ""}} +] +TEST_KEYS_formatted_diff = [ + {'__default_ops': {'__delete_op': __DELETE_LEAFS_OR_CONFIG_IF_NO_NON_KEY_LEAF}}, + {"servers": {"address": "", '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, + {"ntp_keys": {"key_id": "", '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}} ] @@ -106,6 +114,17 @@ class Ntp(ConfigBase): if result['changed']: result['after'] = changed_ntp_facts + new_config = changed_ntp_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_ntp_facts, + TEST_KEYS_formatted_diff) + result['after(generated)'] = new_config + + if self._module._diff: + result['diff'] = get_formatted_config_diff(existing_ntp_facts, + new_config, + self._module._verbosity) result['warnings'] = warnings return result @@ -119,7 +138,7 @@ class Ntp(ConfigBase): """ want = self._module.params['config'] if want is None: - want = [] + want = {} have = existing_ntp_facts @@ -145,6 +164,10 @@ class Ntp(ConfigBase): commands, requests = self._state_deleted(want, have) elif state == 'merged': commands, requests = self._state_merged(want, have) + elif state == 'overridden': + commands, requests = self._state_overridden(want, have) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have) return commands, requests @@ -160,6 +183,8 @@ class Ntp(ConfigBase): commands = diff requests = [] + + self.preprocess_merge_commands(commands, want) if commands: requests = self.get_merge_requests(commands, have) @@ -207,6 +232,77 @@ class Ntp(ConfigBase): return commands, requests + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + replaced_config = get_replaced_config(want, have, TEST_KEYS) + + add_commands = [] + if replaced_config: + self.sort_lists_in_config(replaced_config) + self.sort_lists_in_config(have) + delete_all = (replaced_config == have) + del_requests = self.get_delete_requests(replaced_config, delete_all) + requests.extend(del_requests) + commands.extend(update_states(replaced_config, "deleted")) + + add_commands = want + else: + diff = get_diff(want, have, TEST_KEYS) + add_commands = diff + + if add_commands: + self.preprocess_merge_commands(add_commands, want) + add_requests = self.get_merge_requests(add_commands, have) + + if len(add_requests) > 0: + requests.extend(add_requests) + commands.extend(update_states(add_commands, "replaced")) + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + self.sort_lists_in_config(want) + self.sort_lists_in_config(have) + + commands = [] + requests = [] + + if have and have != want: + delete_all = True + del_requests = self.get_delete_requests(have, delete_all) + requests.extend(del_requests) + commands.extend(update_states(have, "deleted")) + have = [] + + if not have and want: + add_commands = want + add_requests = self.get_merge_requests(add_commands, have) + + if len(add_requests) > 0: + requests.extend(add_requests) + commands.extend(update_states(add_commands, "overridden")) + + return commands, requests + def validate_want(self, want, state): if state == 'deleted': @@ -215,7 +311,9 @@ class Ntp(ConfigBase): key_id_config = server.get('key_id', None) minpoll_config = server.get('minpoll', None) maxpoll_config = server.get('maxpoll', None) - if key_id_config or minpoll_config or maxpoll_config: + prefer_config = server.get('prefer', None) + if key_id_config or minpoll_config or maxpoll_config or \ + prefer_config is not None: err_msg = "NTP server parameter(s) can not be deleted." self._module.fail_json(msg=err_msg, code=405) @@ -247,6 +345,52 @@ class Ntp(ConfigBase): server.pop('minpoll') if 'maxpoll' in server and not server['maxpoll']: server.pop('maxpoll') + if 'prefer' in server and server['prefer'] is None: + server.pop('prefer') + + if state == 'replaced' or state == 'overridden': + enable_auth_want = want.get('enable_ntp_auth', None) + if enable_auth_want is None: + want['enable_ntp_auth'] = False + if 'servers' in want and want['servers'] is not None: + for server in want['servers']: + if 'prefer' in server and server['prefer'] is None: + server['prefer'] = False + + def search_servers(self, svr_address, servers): + + found_server = dict() + if servers is not None: + for server in servers: + if server['address'] == svr_address: + found_server = server + return found_server + + def preprocess_merge_commands(self, commands, want): + + if 'servers' in commands and commands['servers'] is not None: + for server in commands['servers']: + if 'minpoll' in server and 'maxpoll' not in server: + want_server = dict() + if 'servers' in want: + want_server = self.search_servers(server['address'], want['servers']) + + if want_server: + server['maxpoll'] = want_server['maxpoll'] + else: + err_msg = "Internal error with NTP server maxpoll configuration." + self._module.fail_json(msg=err_msg, code=500) + + if 'maxpoll' in server and 'minpoll' not in server: + want_server = dict() + if 'servers' in want: + want_server = self.search_servers(server['address'], want['servers']) + + if want_server: + server['minpoll'] = want_server['minpoll'] + else: + err_msg = "Internal error with NTP server minpoll configuration." + self._module.fail_json(msg=err_msg, code=500) def get_merge_requests(self, configs, have): @@ -448,18 +592,23 @@ class Ntp(ConfigBase): # Create URL and payload method = DELETE - servers_config = configs.get('servers', None) src_intf_config = configs.get('source_interfaces', None) vrf_config = configs.get('vrf', None) enable_auth_config = configs.get('enable_ntp_auth', None) trusted_key_config = configs.get('trusted_keys', None) - if servers_config or src_intf_config or vrf_config or \ + if src_intf_config or vrf_config or \ trusted_key_config or enable_auth_config is not None: url = 'data/openconfig-system:system/ntp' request = {"path": url, "method": method} requests.append(request) + servers_config = configs.get('servers', None) + if servers_config: + url = 'data/openconfig-system:system/ntp/servers' + request = {"path": url, "method": method} + requests.append(request) + keys_config = configs.get('ntp_keys', None) if keys_config: url = 'data/openconfig-system:system/ntp/ntp-keys' @@ -546,3 +695,20 @@ class Ntp(ConfigBase): requests.append(request) return requests + + def get_server_address(self, ntp_server): + return ntp_server.get('address') + + def get_ntp_key_id(self, ntp_key): + return ntp_key.get('key_id') + + def sort_lists_in_config(self, config): + + if 'source_interfaces' in config and config['source_interfaces'] is not None: + config['source_interfaces'].sort() + if 'servers' in config and config['servers'] is not None: + config['servers'].sort(key=self.get_server_address) + if 'trusted_keys' in config and config['trusted_keys'] is not None: + config['trusted_keys'].sort() + if 'ntp_keys' in config and config['ntp_keys'] is not None: + config['ntp_keys'].sort(key=self.get_ntp_key_id) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/pki/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/pki/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/pki/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/pki/pki.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/pki/pki.py new file mode 100644 index 000000000..163e59023 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/pki/pki.py @@ -0,0 +1,563 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell EMC +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_pki class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + remove_empties, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import ( + Facts, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + update_states, + get_diff, +) + +from urllib.parse import quote + + +TRUST_STORES_PATH = "data/openconfig-pki:pki/trust-stores" +SECURITY_PROFILES_PATH = "data/openconfig-pki:pki/security-profiles" +TRUST_STORE_PATH = "data/openconfig-pki:pki/trust-stores/trust-store" +SECURITY_PROFILE_PATH = ( + "data/openconfig-pki:pki/security-profiles/security-profile" +) + +PATCH = "patch" +DELETE = "delete" +PUT = "put" +TEST_KEYS = [ + {"security_profiles": {"profile_name": ""}}, + {"trust_stores": {"name": ""}}, +] + + +class Pki(ConfigBase): + """ + The sonic_pki class + """ + + gather_subset = [ + "!all", + "!min", + ] + + gather_network_resources = [ + "pki", + ] + + def get_pki_facts(self): + """Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, self.gather_network_resources + ) + pki_facts = facts["ansible_network_resources"].get("pki") + if not pki_facts: + return {} + return pki_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + warnings = list() + commands = list() + + existing_pki_facts = self.get_pki_facts() + commands, requests = self.set_config(existing_pki_facts) + if commands and len(requests) > 0: + if not self._module.check_mode: + try: + edit_config( + self._module, to_request(self._module, requests) + ) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + result["changed"] = True + result["commands"] = commands + + changed_pki_facts = self.get_pki_facts() + + result["before"] = existing_pki_facts + if result["changed"]: + result["after"] = changed_pki_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_pki_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_pki_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + state = self._module.params["state"] + if not want: + want = {} + + diff = get_diff(want, have, list(TEST_KEYS)) + + if state == "overridden": + commands, requests = self._state_overridden(want, have, diff) + elif state == "deleted": + commands, requests = self._state_deleted(want, have, diff) + elif state == "merged": + commands, requests = self._state_merged(want, have, diff) + elif state == "replaced": + commands, requests = self._state_replaced(want, have) + return commands, requests + + def _state_replaced(self, want, have): + """Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + spdiff = sp_diff(want, have) + tsdiff = ts_diff(want, have) + commands = [] + requests = [] + have_dict = { + "security_profiles": { + sp.get("profile_name"): sp + for sp in (have.get("security_profiles") or []) + }, + "trust_stores": { + ts.get("name"): ts for ts in (have.get("trust_stores") or []) + }, + } + for ts in tsdiff: + requests.append( + { + "path": TRUST_STORE_PATH + "=" + ts.get("name"), + "method": PUT, + "data": mk_ts_config(ts), + } + ) + commands.append( + update_states( + have_dict["trust_stores"][ts.get("name")], "replaced" + ) + ) + for sp in spdiff: + requests.append( + { + "path": SECURITY_PROFILE_PATH + + "=" + + sp.get("profile_name"), + "method": PUT, + "data": mk_sp_config(sp), + } + ) + commands.append( + update_states( + have_dict["security_profiles"][sp.get("profile_name")], + "replaced", + ) + ) + + return commands, requests + + def _state_overridden(self, want, have, diff): + """The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + + commands = [] + requests = [] + want_tss = [ts.get("name") for ts in (want.get("trust_stores") or [])] + want_sps = [ + sp.get("profile_name") + for sp in (want.get("security_profiles") or []) + ] + have_tss = [ts.get("name") for ts in (have.get("trust_stores") or [])] + have_sps = [ + sp.get("profile_name") + for sp in (have.get("security_profiles") or []) + ] + + have_dict = { + "security_profiles": { + sp.get("profile_name"): sp + for sp in (have.get("security_profiles") or []) + }, + "trust_stores": { + ts.get("name"): ts for ts in (have.get("trust_stores") or []) + }, + } + used_ts = [] + for sp in have_sps: + if sp not in want_sps: + requests.append( + { + "path": SECURITY_PROFILE_PATH + "=" + sp, + "method": DELETE, + } + ) + commands.append( + update_states( + have_dict["security_profiles"][sp], "deleted" + ) + ) + else: + ts_name = have_dict.get("security_profiles", {}).get(sp, {}).get("trust_store") + if ts_name and ts_name not in used_ts: + used_ts.append(ts_name) + + for ts in have_tss: + if ts not in want_tss and ts not in used_ts: + requests.append( + {"path": TRUST_STORE_PATH + "=" + ts, "method": DELETE} + ) + commands.append( + update_states(have_dict["trust_stores"][ts], "deleted") + ) + + for ts in want.get("trust_stores") or []: + if ts != have_dict["trust_stores"].get(ts.get("name")): + requests.append( + { + "path": TRUST_STORE_PATH + "=" + ts.get("name"), + "method": PUT, + "data": mk_ts_config(ts), + } + ) + commands.append(update_states(ts, "overridden")) + for sp in want.get("security_profiles") or []: + if sp != have_dict["security_profiles"].get( + sp.get("profile_name") + ): + requests.append( + { + "path": SECURITY_PROFILE_PATH + + "=" + + sp.get("profile_name"), + "method": PUT, + "data": mk_sp_config(sp), + } + ) + commands.append(update_states(sp, "overridden")) + + return commands, requests + + def _state_merged(self, want, have, diff): + """The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = diff or {} + requests = [] + + for ts in commands.get("trust_stores") or []: + requests.append( + { + "path": TRUST_STORE_PATH, + "method": PATCH, + "data": mk_ts_config(ts), + } + ) + + for sp in commands.get("security_profiles") or []: + requests.append( + { + "path": SECURITY_PROFILE_PATH, + "method": PATCH, + "data": mk_sp_config(sp), + } + ) + + if commands and requests: + commands = update_states(commands, "merged") + else: + commands = [] + + return commands, requests + + def _state_deleted(self, want, have, diff): + """The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + requests = [] + current_ts = [ + ts.get("name") + for ts in (have.get("trust_stores") or []) + if ts.get("name") + ] + current_sp = [ + sp.get("profile_name") + for sp in (have.get("security_profiles") or []) + if sp.get("profile_name") + ] + if not want: + commands = have + for sp in current_sp: + requests.append( + { + "path": SECURITY_PROFILE_PATH + "=" + sp, + "method": DELETE, + } + ) + for ts in current_ts: + requests.append( + {"path": TRUST_STORE_PATH + "=" + ts, "method": DELETE} + ) + else: + commands = remove_empties(want) + + for sp in commands.get("security_profiles") or []: + if sp.get("profile_name") in current_sp: + requests.extend(mk_sp_delete(sp, have)) + for ts in commands.get("trust_stores") or []: + if ts.get("name") in current_ts: + requests.extend(mk_ts_delete(ts, have)) + + if commands and requests: + commands = update_states([commands], "deleted") + else: + commands = [] + + return commands, requests + + +def sp_diff(want, have): + hsps = {} + wsps = {} + dsps = [] + for hsp in have.get("security_profiles") or []: + hsps[hsp.get("profile_name")] = hsp + for wsp in want.get("security_profiles") or []: + wsps[wsp.get("profile_name")] = wsp + + for spn, sp in wsps.items(): + dsp = dict(hsps.get(spn)) + # Pop each leaf from dsp that is not in sp + for k, v in dsp.items(): + if not isinstance(dsp.get(k), list) and not isinstance( + dsp.get(k), dict + ): + if k not in sp: + dsp.pop(k) + for k, v in sp.items(): + if not isinstance(dsp.get(k), list) and not isinstance( + dsp.get(k), dict + ): + if dsp.get(k) != v: + dsp[k] = v + else: + if v is not None: + dsp[k] = v + if dsp != hsps.get(spn): + dsps.append(dsp) + return dsps + + +def ts_diff(want, have): + htss = {} + wtss = {} + dtss = [] + for hts in have.get("trust_stores") or []: + htss[hts.get("name")] = hts + for wts in want.get("trust_stores") or []: + wtss[wts.get("name")] = wts + + for tsn, ts in wtss.items(): + dts = dict(htss.get(tsn)) + for k, v in ts.items(): + if not isinstance(dts.get(k), list) and not isinstance( + dts.get(k), dict + ): + if dts.get(k) != v: + dts[k] = v + else: + if v is not None: + dts[k] = v + if dts != htss.get(tsn): + dtss.append(dts) + return dtss + + +def mk_sp_config(indata): + outdata = { + k.replace("_", "-"): v for k, v in indata.items() if v is not None + } + output = { + "openconfig-pki:security-profile": [ + {"profile-name": outdata.get("profile-name"), "config": outdata} + ] + } + return output + + +def mk_ts_config(indata): + outdata = { + k.replace("_", "-"): v for k, v in indata.items() if v is not None + } + output = { + "openconfig-pki:trust-store": [ + {"name": outdata.get("name"), "config": outdata} + ] + } + return output + + +def mk_sp_delete(want_sp, have): + requests = [] + cur_sp = None + del_sp = {} + for csp in have.get("security_profiles") or []: + if csp.get("profile_name") == want_sp.get("profile_name"): + cur_sp = csp + break + if cur_sp: + for k, v in want_sp.items(): + if v is not None and k != "profile_name": + if v == cur_sp.get(k) or isinstance(v, list): + del_sp[k] = v + if len(del_sp) == 0 and len(want_sp) <= 1: + requests = [ + { + "path": SECURITY_PROFILE_PATH + + "=" + + want_sp.get("profile_name"), + "method": DELETE, + } + ] + else: + for k, v in del_sp.items(): + if isinstance(v, list): + for li in v: + if li in (cur_sp.get(k) or []): + requests.append( + { + "path": SECURITY_PROFILE_PATH + + "=" + + want_sp.get("profile_name") + + "/config/" + + k.replace("_", "-") + + "=" + + quote(li, safe=""), + "method": DELETE, + } + ) + else: + requests.append( + { + "path": SECURITY_PROFILE_PATH + + "=" + + want_sp.get("profile_name") + + "/config/" + + k.replace("_", "-"), + "method": DELETE, + } + ) + return requests + + +def mk_ts_delete(want_ts, have): + requests = [] + cur_ts = None + del_ts = {} + for cts in have.get("trust_stores") or []: + if cts.get("name") == want_ts.get("name"): + cur_ts = cts + break + if cur_ts: + for k, v in want_ts.items(): + if v is not None and k != "name": + if v == cur_ts.get(k) or isinstance(v, list): + del_ts[k] = v + if len(del_ts) == 0 and len(want_ts) <= 1: + requests = [ + { + "path": TRUST_STORE_PATH + "=" + want_ts.get("name"), + "method": DELETE, + } + ] + else: + for k, v in del_ts.items(): + if isinstance(v, list): + for li in v: + if li in (cur_ts.get(k) or []): + requests.append( + { + "path": TRUST_STORE_PATH + + "=" + + want_ts.get("name") + + "/config/" + + k.replace("_", "-") + + "=" + + quote(li, safe=""), + "method": DELETE, + } + ) + else: + requests.append( + { + "path": TRUST_STORE_PATH + + "=" + + want_ts.get("name") + + "/config/" + + k.replace("_", "-"), + "method": DELETE, + } + ) + return requests diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/port_breakout/port_breakout.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/port_breakout/port_breakout.py index 371019d04..654e34dee 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/port_breakout/port_breakout.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/port_breakout/port_breakout.py @@ -17,6 +17,7 @@ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.c ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( to_list, + search_obj_in_list ) from ansible.module_utils.connection import ConnectionError from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts @@ -33,7 +34,6 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s PATCH = 'patch' DELETE = 'delete' -POST = 'post' class Port_breakout(ConfigBase): @@ -152,6 +152,52 @@ class Port_breakout(ConfigBase): return commands, requests + def _state_replaced(self, want, have, diff): + """ The command generator when state is replaced + + :param want: the additive configuration as a dictionary + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = diff + requests = self.get_modify_port_breakout_requests(commands, have) + if commands and len(requests) > 0: + commands = update_states(commands, "replaced") + else: + commands = [] + + return commands, requests + + def _state_overridden(self, want, have, diff): + """ The command generator when state is merged + + :param want: the additive configuration as a dictionary + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + requests = [] + + # Delete port-breakout configuration for interfaces that are not specified + for cfg in have: + if not search_obj_in_list(cfg['name'], want, 'name'): + commands.append(cfg) + requests.append(self.get_delete_single_port_breakout(cfg['name'], cfg)) + + if commands: + commands = update_states(commands, "deleted") + + add_requests = self.get_modify_port_breakout_requests(diff, have) + if len(add_requests) > 0: + commands.extend(update_states(diff, "overridden")) + requests.extend(add_requests) + + return commands, requests + def _state_deleted(self, want, have, diff): """ The command generator when state is deleted @@ -161,7 +207,7 @@ class Port_breakout(ConfigBase): :returns: the commands necessary to remove the current configuration of the provided objects """ - # if want is none, then delete all the port_breakouti except admin + # if want is none, then delete all the port_breakout except admin if not want: commands = have else: @@ -215,27 +261,6 @@ class Port_breakout(ConfigBase): requests.append(req) return requests - def get_default_port_breakout_modes(self): - def_port_breakout_modes = [] - request = [{"path": "operations/sonic-port-breakout:breakout_capabilities", "method": POST}] - try: - response = edit_config(self._module, to_request(self._module, request)) - except ConnectionError as exc: - self._module.fail_json(msg=str(exc), code=exc.code) - - raw_port_breakout_list = [] - if "sonic-port-breakout:output" in response[0][1]: - raw_port_breakout_list = response[0][1].get("sonic-port-breakout:output", {}).get('caps', []) - - for port_breakout in raw_port_breakout_list: - name = port_breakout.get('port', None) - mode = port_breakout.get('defmode', None) - if name and mode: - if '[' in mode: - mode = mode[:mode.index('[')] - def_port_breakout_modes.append({'name': name, 'mode': mode}) - return def_port_breakout_modes - def get_delete_port_breakout_requests(self, commands, have): requests = [] if not commands: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/port_group/port_group.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/port_group/port_group.py new file mode 100644 index 000000000..37281403e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/port_group/port_group.py @@ -0,0 +1,380 @@ +# +# -*- coding: utf-8 -*- +# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_port_group class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +""" +The use of natsort causes sanity error due to it is not available in python version currently used. +When natsort becomes available, the code here and below using it will be applied. +from natsort import ( + natsorted, + ns +) +""" +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import ( + Facts, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + get_diff, + update_states, + remove_empties_from_list +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff +) +from ansible.module_utils.connection import ConnectionError + +GET = "get" +PATCH = 'patch' +DELETE = 'delete' +url = 'data/openconfig-port-group:port-groups/port-group' + +TEST_KEYS = [ + { + 'config': {'id': ''} + } +] +TEST_KEYS_formatted_diff = [ + {'config': {'id': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}} +] + + +class Port_group(ConfigBase): + """ + The sonic_port_group class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'port_group', + ] + + pg_default_speeds_ready = False + pg_default_speeds = [] + + def __init__(self, module): + super(Port_group, self).__init__(module) + + if not Port_group.pg_default_speeds_ready: + Port_group.pg_default_speeds = self.get_port_group_default_speed() + Port_group.pg_default_speeds_ready = True + + def get_port_group_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + port_group_facts = facts['ansible_network_resources'].get('port_group') + if not port_group_facts: + return [] + return port_group_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = list() + + existing_port_group_facts = self.get_port_group_facts() + commands, requests = self.set_config(existing_port_group_facts) + if commands and len(requests) > 0: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + result['changed'] = True + result['commands'] = commands + + changed_port_group_facts = self.get_port_group_facts() + + result['before'] = existing_port_group_facts + if result['changed']: + result['after'] = changed_port_group_facts + + new_config = changed_port_group_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_port_group_facts, + TEST_KEYS_formatted_diff) + # See the above comment about natsort module + # new_config = natsorted(new_config, key=lambda x: x['id']) + result['after(generated)'] = new_config + + if self._module._diff: + result['diff'] = get_formatted_config_diff(existing_port_group_facts, + new_config, + self._module._verbosity) + result['warnings'] = warnings + return result + + def set_config(self, existing_port_group_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + have = existing_port_group_facts + + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + + state = self._module.params['state'] + + diff = get_diff(want, have, TEST_KEYS) + + tmp_want = remove_empties_from_list(want) + new_want = self.remove_empty_dict_from_list(tmp_want) + + new_diff = self.remove_empty_dict_from_list(diff) + + if state == 'overridden': + commands, requests = self._state_overridden(new_want, have, new_diff) + elif state == 'deleted': + commands, requests = self._state_deleted(new_want, have, new_diff) + elif state == 'merged': + commands, requests = self._state_merged(new_want, have, new_diff) + elif state == 'replaced': + commands, requests = self._state_replaced(new_want, have, new_diff) + + return commands, requests + + def _state_replaced(self, want, have, diff): + """ The command generator when state is replaced + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = diff + requests = [] + if commands: + requests = self.build_merge_requests(commands) + + if len(requests) > 0: + commands = update_states(commands, "merged") + else: + commands = [] + + return commands, requests + + def _state_overridden(self, want, have, diff): + """ The command generator when state is overridden + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + new_want = self.patch_want_with_default(want) + commands = get_diff(new_want, have, TEST_KEYS) + requests = [] + if commands: + requests = self.build_merge_requests(commands) + + if len(requests) > 0: + commands = update_states(commands, "merged") + else: + commands = [] + + return commands, requests + + def _state_merged(self, want, have, diff): + """ The command generator when state is merged + + :param want: the additive configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = diff + requests = [] + if commands: + requests = self.build_merge_requests(commands) + + if len(requests) > 0: + commands = update_states(commands, "merged") + else: + commands = [] + + return commands, requests + + def _state_deleted(self, want, have, diff): + """ The command generator when state is deleted + + :param want: the objects from which the configuration should be removed + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + # if want is none, then delete all the port groups + + if not want: + tmp_commands = have + else: + tmp_commands = want + tmp_commands = self.preprocess_delete_commands(tmp_commands, have) + + commands = get_diff(tmp_commands, Port_group.pg_default_speeds, TEST_KEYS) + + requests = [] + if commands: + requests = self.build_delete_requests(commands) + + if len(requests) > 0: + commands = update_states(commands, "deleted") + else: + commands = [] + + return commands, requests + + def search_port_groups(self, id, pgs): + + found_pg = dict() + if pgs is not None: + for pg in pgs: + if pg['id'] == id: + found_pg = pg + return found_pg + + def preprocess_delete_commands(self, commands, have): + new_commands = [] + for cmd in commands: + pg_id = cmd['id'] + pg = self.search_port_groups(pg_id, have) + if pg: + new_cmd = {'id': pg_id, 'speed': pg['speed']} + new_commands.append(new_cmd) + + return new_commands + + def remove_empty_dict_from_list(self, dict_list): + new_dict_list = [] + if dict_list: + for dictt in dict_list: + if dictt: + new_dict_list.append(dictt) + + return new_dict_list + + def build_delete_requests(self, confs): + requests = [] + + for conf in confs: + pg_id = conf['id'] + method = DELETE + pg_url = (url + '=%s/config/speed') % (pg_id) + request = {"path": pg_url, "method": method} + requests.append(request) + + return requests + + def build_merge_requests(self, confs): + requests = [] + pgs = [] + for conf in confs: + pg_id = conf['id'] + if 'speed' in conf: + pg_conf = {'id': pg_id, 'speed': 'openconfig-if-ethernet:' + conf['speed']} + pg = {'id': pg_id, 'config': pg_conf} + pgs.append(pg) + + if pgs: + payload = {"openconfig-port-group:port-group": pgs} + method = PATCH + pg_url = url + request = {"path": pg_url, "method": method, "data": payload} + requests.append(request) + + return requests + + def patch_want_with_default(self, want): + new_want = list() + for dpg in Port_group.pg_default_speeds: + pg_id = dpg['id'] + pg = self.search_port_groups(pg_id, want) + if pg: + new_pg = {'id': pg_id, 'speed': pg['speed']} + else: + new_pg = {'id': pg_id, 'speed': dpg['speed']} + + new_want.append(new_pg) + return new_want + + def get_port_group_default_speed(self): + """Get all the port group default speeds""" + + pgs_request = [{"path": "data/openconfig-port-group:port-groups/port-group", "method": GET}] + try: + pgs_response = edit_config(self._module, to_request(self._module, pgs_request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + pgs_config = [] + if "openconfig-port-group:port-group" in pgs_response[0][1]: + pgs_config = pgs_response[0][1].get("openconfig-port-group:port-group", []) + + pgs_dft_speeds = [] + for pg_config in pgs_config: + pg_state = dict() + if 'state' in pg_config: + pg_state['id'] = pg_config['id'] + dft_speed_str = pg_config['state'].get('default-speed', None) + if dft_speed_str: + pg_state['speed'] = dft_speed_str.split(":", 1)[-1] + pgs_dft_speeds.append(pg_state) + + return pgs_dft_speeds diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/prefix_lists/prefix_lists.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/prefix_lists/prefix_lists.py index d5c36d3e2..f4dc71214 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/prefix_lists/prefix_lists.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/prefix_lists/prefix_lists.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2019 Red Hat +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -40,7 +40,7 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s TEST_KEYS = [ {"config": {"afi": "", "name": ""}}, - {"prefixes": {"action": "", "ge": "", "le": "", "prefix": "", "sequence": ""}} + {"prefixes": {"ge": "", "le": "", "prefix": "", "sequence": ""}} ] DELETE = "delete" @@ -149,6 +149,10 @@ openconfig-routing-policy-ext:extended-prefixes/extended-prefix={},{},{}' commands, requests = self._state_deleted(want, have) elif state == 'merged': commands, requests = self._state_merged(diff) + elif state == 'replaced': + commands, requests = self._state_replaced(diff) + elif state == 'overridden': + commands, requests = self._state_overridden(want, have) ret_commands = commands return ret_commands, requests @@ -188,6 +192,51 @@ openconfig-routing-policy-ext:extended-prefixes/extended-prefix={},{},{}' commands = [] return commands, requests + def _state_replaced(self, diff): + """ The command generator when state is replaced + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = diff + requests = self.get_modify_prefix_lists_requests(commands) + if commands and len(requests) > 0: + commands = update_states(commands, "replaced") + else: + commands = [] + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + self.sort_lists_in_config(want) + self.sort_lists_in_config(have) + + if have and have != want: + del_requests = self.get_delete_all_prefix_list_cfg_requests() + requests.extend(del_requests) + commands.extend(update_states(have, "deleted")) + have = [] + + if not have and want: + mod_commands = want + mod_requests = self.get_modify_prefix_lists_requests(mod_commands) + + if len(mod_requests) > 0: + requests.extend(mod_requests) + commands.extend(update_states(mod_commands, "overridden")) + + return commands, requests + def get_modify_prefix_lists_requests(self, commands): '''Traverse the input list of configuration "modify" commands obtained from parsing the input playbook parameters. For each command, @@ -456,3 +505,13 @@ openconfig-routing-policy-ext:extended-prefixes/extended-prefix={},{},{}' prefix_net['prefixlen'] = int(prefix_val.split("/")[1]) return prefix_net + + def sort_lists_in_config(self, config): + if config: + config.sort(key=self.get_name) + for cfg in config: + if 'prefixes' in cfg and cfg['prefixes']: + cfg['prefixes'].sort(key=lambda x: (x['sequence'], x['action'], x['prefix'])) + + def get_name(self, name): + return name.get('name') diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/radius_server/radius_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/radius_server/radius_server.py index dfa65482f..264ffa014 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/radius_server/radius_server.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/radius_server/radius_server.py @@ -27,14 +27,25 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( update_states, get_diff, + get_replaced_config, normalize_interface_name, ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + __DELETE_LEAFS_OR_CONFIG_IF_NO_NON_KEY_LEAF, + get_new_config, + get_formatted_config_diff +) PATCH = 'patch' DELETE = 'delete' TEST_KEYS = [ {'host': {'name': ''}}, ] +TEST_KEYS_formatted_diff = [ + {'__default_ops': {'__delete_op': __DELETE_LEAFS_OR_CONFIG_IF_NO_NON_KEY_LEAF}}, + {'host': {'name': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, +] class Radius_server(ConfigBase): @@ -91,6 +102,17 @@ class Radius_server(ConfigBase): if result['changed']: result['after'] = changed_radius_server_facts + new_config = changed_radius_server_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_radius_server_facts, + TEST_KEYS_formatted_diff) + result['after(generated)'] = new_config + + if self._module._diff: + result['diff'] = get_formatted_config_diff(existing_radius_server_facts, + new_config, + self._module._verbosity) result['warnings'] = warnings return result @@ -180,6 +202,67 @@ class Radius_server(ConfigBase): return commands, requests + def _state_replaced(self, want, have, diff): + """ The command generator when state is replaced + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + replaced_config = get_replaced_config(want, have, TEST_KEYS) + + add_commands = [] + if replaced_config: + del_requests = self.get_delete_radius_server_requests(replaced_config, have) + requests.extend(del_requests) + commands.extend(update_states(replaced_config, "deleted")) + add_commands = want + else: + add_commands = diff + + if add_commands: + add_requests = self.get_modify_radius_server_requests(add_commands, have) + if len(add_requests) > 0: + requests.extend(add_requests) + commands.extend(update_states(add_commands, "replaced")) + + return commands, requests + + def _state_overridden(self, want, have, diff): + """ The command generator when state is overridden + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + + r_diff = get_diff(have, want, TEST_KEYS) + if have and (diff or r_diff): + del_requests = self.get_delete_radius_server_requests(have, have) + requests.extend(del_requests) + commands.extend(update_states(have, "deleted")) + have = [] + + if not have and want: + want_commands = want + want_requests = self.get_modify_radius_server_requests(want_commands, have) + + if len(want_requests) > 0: + requests.extend(want_requests) + commands.extend(update_states(want_commands, "overridden")) + + return commands, requests + def get_radius_global_payload(self, conf): payload = {} global_cfg = {} diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/route_maps/route_maps.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/route_maps/route_maps.py new file mode 100644 index 000000000..0b40c30f2 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/route_maps/route_maps.py @@ -0,0 +1,2354 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_route_maps class +The code in this file compares the current configuration (as a dict) +to the configuration provided (as a dict) based on the contents of the +currently executing playbook. The result of the comparison and the end state +requested by the executing playbook are used to to determine the command set +necessary to bring the current configuration to it's desired end-state. +The resulting commands are then transmitted to the target device. +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + validate_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts \ + import Facts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils \ + import ( + get_diff, + update_states, + remove_empties_from_list, + get_normalize_interface_name, + check_required + ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) + + +TEST_KEYS = [ + {"config": {"map_name": "", "sequence_num": ""}} +] + +DELETE = "delete" +PATCH = "patch" + + +class Route_maps(ConfigBase): + """ + The sonic_route_maps class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'route_maps', + ] + + route_maps_uri = 'data/openconfig-routing-policy:routing-policy/policy-definitions' + route_map_uri = route_maps_uri + '/policy-definition={0}' + route_map_stmt_uri = route_map_uri + '/statements/statement={1}' + route_map_stmt_base_uri = route_map_uri + '/statements/statement={1}/' + route_maps_data_path = 'openconfig-routing-policy:policy-definitions' + + set_community_rest_names = { + 'additive': 'openconfig-routing-policy-ext:ADDITIVE', + 'local_as': 'openconfig-bgp-types:NO_EXPORT_SUBCONFED', + 'no_advertise': 'openconfig-bgp-types:NO_ADVERTISE', + 'no_export': 'openconfig-bgp-types:NO_EXPORT', + 'no_peer': 'openconfig-bgp-types:NOPEER', + 'none': 'openconfig-bgp-types:NONE' + } + + set_extcomm_rest_names = { + 'rt': 'route-target:', + 'soo': 'route-origin:' + } + + def __init__(self, module): + super(Route_maps, self).__init__(module) + + def get_route_maps_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, + self.gather_network_resources) + route_maps_facts = facts['ansible_network_resources'].get('route_maps') + if not route_maps_facts: + return [] + return route_maps_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = list() + + existing_route_maps_facts = self.get_route_maps_facts() + commands, requests = self.set_config(existing_route_maps_facts) + if commands and len(requests) > 0: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.errno) + result['changed'] = True + result['commands'] = commands + + changed_route_maps_facts = self.get_route_maps_facts() + + result['before'] = existing_route_maps_facts + if result['changed']: + result['after'] = changed_route_maps_facts + + result['warnings'] = warnings + return result + + def set_config(self, existing_route_maps_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + if want: + want = self.validate_and_normalize_config(want) + else: + want = [] + + have = existing_route_maps_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + state = self._module.params['state'] + if state == 'deleted': + commands, requests = self._state_deleted(want, have) + elif state == 'merged': + commands, requests = self._state_merged(want, have) + elif state == 'overridden': + commands, requests = self._state_overridden(want, have) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have) + return commands, requests + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + + # Delete replaced groupings + commands = deepcopy(want) + requests = self.get_delete_replaced_groupings(commands, have) + if not requests: + commands = [] + if commands and len(requests) > 0: + commands = update_states(commands, "deleted") + + if requests: + modify_have = [] + else: + modify_have = have + + # Apply the commands from the playbook + diff = get_diff(want, modify_have, TEST_KEYS) + merged_commands = diff + + replaced_requests = self.get_modify_route_maps_requests(merged_commands, want, modify_have) + requests.extend(replaced_requests) + if merged_commands and len(replaced_requests) > 0: + merged_commands = update_states(merged_commands, "replaced") + commands.extend(merged_commands) + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + if not want: + return commands, requests + + # Determine if there is any configuration specified in the playbook + # that is not contained in the current configuration. + diff_requested = get_diff(want, have, TEST_KEYS) + + # Determine if there is anything already configured that is not + # specified in the playbook. + diff_unwanted = get_diff(have, want, TEST_KEYS) + + # Idempotency check: If the configuration already matches the + # requested configuration with no extra attributes, no + # commands should be executed on the device. + if not diff_requested and not diff_unwanted: + return commands, requests + + # Delete all current route map configuration + commands = have + requests = self.get_delete_all_route_map_cfg_request() + if commands and len(requests) > 0: + commands = update_states(commands, "deleted") + + # Apply the commands from the playbook + merged_commands = want + overridden_requests = self.get_modify_route_maps_requests(merged_commands, want, []) + requests.extend(overridden_requests) + if merged_commands and len(overridden_requests) > 0: + merged_commands = update_states(merged_commands, "overridden") + commands.extend(merged_commands) + return commands, requests + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + diff = get_diff(want, have, TEST_KEYS) + commands = diff + requests = self.get_modify_route_maps_requests(commands, want, have) + if commands and len(requests) > 0: + commands = update_states(commands, "merged") + else: + commands = [] + + return commands, requests + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + requests = [] + if not have or have == []: + commands = [] + elif not want or want == []: + commands = have + requests = self.get_delete_all_route_map_cfg_request() + else: + commands = want + requests = self.get_delete_route_maps_requests(have, commands) + + if commands and len(requests) > 0: + commands = update_states(commands, "deleted") + else: + commands = [] + + return commands, requests + + def get_modify_route_maps_requests(self, commands, want, have): + '''Traverse the input list of configuration "modify" commands + obtained from parsing the input playbook parameters. For each + command, create a route map configuration REST API to modify the route + map specified by the current command.''' + + requests = [] + if not commands: + return requests + + # Create URL and payload + route_maps_payload_list = [] + route_maps_payload_dict = {'policy-definition': route_maps_payload_list} + for command in commands: + if command.get('action') is None: + self.insert_route_map_cmd_action(command, want) + route_map_payload = self.get_modify_single_route_map_request(command, have) + if route_map_payload: + route_maps_payload_list.append(route_map_payload) + + # Note: This is consistent with current CLI behavior, but should be + # revisited if and when the SONiC REST implementation is enhanced + # for the "match peer" attribute. + self.route_map_remove_configured_match_peer(route_map_payload, have, requests) + + route_maps_data = {self.route_maps_data_path: route_maps_payload_dict} + request = {'path': self.route_maps_uri, 'method': PATCH, 'data': route_maps_data} + requests.append(request) + return requests + + def insert_route_map_cmd_action(self, command, want): + '''Insert the "action" value into the specified "command" if it is not + already present. This dictionary member will not be present in the + command obtained from the "diff" utility if it is unchanged from its + currently configured value because it is not a "difference" in the + configuration requested by the playbook versus the current + configuration. It is, however, needed in order to create the + appropriate REST API for modifying other attributes in the route map.''' + + conf_map_name = command.get('map_name', None) + conf_seq_num = command.get('sequence_num', None) + if not conf_map_name or not conf_seq_num: + return + + conf_action = command.get('action', None) + if conf_action: + return + + # Find the corresponding route map statement in the "want" dict + # list and insert it into the current "command" dict. + matching_map_in_want = self.get_matching_map(conf_map_name, conf_seq_num, want) + if matching_map_in_want: + conf_action = matching_map_in_want.get('action') + if conf_action is not None: + command['action'] = conf_action + + def get_modify_single_route_map_request(self, command, have): + '''Create and return the appropriate set of route map REST API attributes + to modify the route map configuration specified by the current "command".''' + + request = {} + if not command: + return request + + conf_map_name = command.get('map_name', None) + conf_action = command.get('action', None) + conf_seq_num = command.get('sequence_num', None) + if not conf_map_name or not conf_action or not conf_seq_num: + return request + + req_seq_num = str(conf_seq_num) + + if conf_action == 'permit': + req_action = 'ACCEPT_ROUTE' + elif conf_action == 'deny': + req_action = 'REJECT_ROUTE' + else: + return request + + # Create a "blank" template for the request + route_map_request = { + 'name': conf_map_name, + 'config': {'name': conf_map_name}, + 'statements': { + 'statement': [ + { + 'name': req_seq_num, + 'config': { + 'name': req_seq_num + }, + 'actions': { + 'config': { + 'policy-result': req_action + } + } + } + ] + } + } + + route_map_statement = route_map_request['statements']['statement'][0] + + self.get_route_map_modify_match_attr(command, route_map_statement) + self.get_route_map_modify_set_attr(command, route_map_statement, have) + self.get_route_map_modify_call_attr(command, route_map_statement) + + return route_map_request + + def get_route_map_modify_match_attr(self, command, route_map_statement): + '''In the dict specified by the input route_map_statement paramenter, + provide REST API definitions of all "match" attributes contained in the + user input command dict specified by the "command" input parameter + to this function.''' + + match_top = command.get('match') + if not match_top: + return + + route_map_statement['conditions'] = {} + + # + # Handle configuration for BGP policy "match" conditions + # ------------------------------------------------------ + route_map_statement['conditions']['openconfig-bgp-policy:bgp-conditions'] = {} + route_map_match_bgp_policy = \ + route_map_statement['conditions']['openconfig-bgp-policy:bgp-conditions'] + + # Handle match as_path + if match_top.get('as_path'): + route_map_match_bgp_policy['match-as-path-set'] = { + 'config': { + 'as-path-set': match_top['as_path'], + 'match-set-options': 'ANY' + } + } + # Handle match evpn + if match_top.get('evpn'): + route_map_match_bgp_policy['openconfig-policy-ext:match-evpn-set'] = \ + {'config': {}} + route_map_match_bgp_evpn = \ + route_map_match_bgp_policy[ + 'openconfig-policy-ext:match-evpn-set']['config'] + if match_top['evpn'].get('default_route') is not None: + boolval = self.yaml_bool_to_python_bool(match_top['evpn']['default_route']) + route_map_match_bgp_evpn['default-type5-route'] = boolval + if match_top['evpn'].get('route_type'): + route_type_rest_name = ('openconfig-bgp-policy-ext:' + + match_top['evpn']['route_type'].upper()) + route_map_match_bgp_evpn['route-type'] = route_type_rest_name + if match_top['evpn'].get('vni'): + route_map_match_bgp_evpn['vni-number'] = match_top['evpn']['vni'] + if not route_map_match_bgp_evpn: + route_map_match_bgp_policy.pop('openconfig-policy-ext:match-evpn-set') + + # Handle BGP policy match configuration under the "config" dictionary + route_map_match_bgp_policy['config'] = {} + if match_top.get('local_preference'): + route_map_match_bgp_policy['config']['local-pref-eq'] = \ + match_top['local_preference'] + if match_top.get('metric'): + route_map_match_bgp_policy['config']['med-eq'] = match_top['metric'] + if match_top.get('origin'): + route_map_match_bgp_policy['config']['origin-eq'] = match_top['origin'].upper() + if match_top.get('community'): + route_map_match_bgp_policy['config']['community-set'] = match_top['community'] + if match_top.get('ext_comm'): + route_map_match_bgp_policy['config']['ext-community-set'] = match_top['ext_comm'] + if match_top.get('ip') and match_top['ip'].get('next_hop'): + route_map_match_bgp_policy[ + 'config']['openconfig-bgp-policy-ext:next-hop-set'] = match_top['ip']['next_hop'] + if not route_map_match_bgp_policy['config']: + route_map_match_bgp_policy.pop('config') + + if not route_map_match_bgp_policy: + route_map_statement['conditions'].pop('openconfig-bgp-policy:bgp-conditions') + + # Handle match interface + if match_top.get('interface'): + route_map_statement['conditions']['match-interface'] = { + 'config': {'interface': match_top['interface']} + } + + # Handle match IP address/prefix + if match_top.get('ip') and match_top['ip'].get('address'): + route_map_statement['conditions']['match-prefix-set'] = { + 'config': { + 'prefix-set': match_top['ip']['address'], + 'match-set-options': 'ANY' + } + } + + # Handle match IPv6 address/prefix + if match_top.get('ipv6') and match_top['ipv6'].get('address'): + if not route_map_statement['conditions'].get('match-prefix-set'): + route_map_statement['conditions']['match-prefix-set'] = { + 'config': { + 'openconfig-routing-policy-ext:ipv6-prefix-set': match_top[ + 'ipv6']['address'], 'match-set-options': 'ANY' + } + } + else: + route_map_statement[ + 'conditions']['match-prefix-set']['config'][ + 'openconfig-routing-policy-ext:ipv6-prefix-set'] = \ + match_top['ipv6']['address'] + + # Handle match peer + if match_top.get('peer'): + peer_list = list(match_top['peer'].values()) + route_map_statement['conditions']['match-neighbor-set'] = { + 'config': { + 'openconfig-routing-policy-ext:address': peer_list + } + } + + # Handle match source protocol + if match_top.get('source_protocol'): + rest_protocol_name = '' + if match_top['source_protocol'] in ('bgp', 'ospf', 'static'): + rest_protocol_name = ('openconfig-policy-types:' + + match_top['source_protocol'].upper()) + elif match_top['source_protocol'] == 'connected': + rest_protocol_name = 'openconfig-policy-types:DIRECTLY_CONNECTED' + + route_map_statement['conditions']['config'] = \ + {'install-protocol-eq': rest_protocol_name} + + # Handle match source VRF + if match_top.get('source_vrf'): + route_map_statement[ + 'conditions'][ + 'openconfig-routing-policy-ext:match-src-network-instance' + ] = {'config': {'name': match_top['source_vrf']}} + + # Handle match tag + if match_top.get('tag'): + route_map_statement['conditions']['match-tag-set'] = { + 'config': { + 'openconfig-routing-policy-ext:tag-value': [match_top['tag']] + } + } + + def get_route_map_modify_set_attr(self, command, route_map_statement, have): + '''In the dict specified by the input route_map_statement paramenter, + provide REST API definitions of all "set" attributes contained in the + user input command dict specified by the "command" input parameter + to this function.''' + + cmd_set_top = command.get('set') + if not cmd_set_top: + return + + # Get the current configuration (if any) for this route map statement + cfg_set_top = {} + conf_map_name = command.get('map_name') + conf_seq_num = command.get('sequence_num') + cmd_rmap_have = self.get_matching_map(conf_map_name, conf_seq_num, have) + if cmd_rmap_have: + cfg_set_top = cmd_rmap_have.get('set') + + route_map_actions = route_map_statement['actions'] + + # Handle configuration for BGP policy "set" conditions + # ---------------------------------------------------- + route_map_actions['openconfig-bgp-policy:bgp-actions'] = {} + route_map_bgp_actions = \ + route_map_actions['openconfig-bgp-policy:bgp-actions'] = {} + # Handle 'set' AS path prepend + if cmd_set_top.get('as_path_prepend'): + route_map_bgp_actions['set-as-path-prepend'] = { + 'config': { + 'openconfig-routing-policy-ext:asn-list': cmd_set_top['as_path_prepend'] + } + } + + # Handle'set' community list delete + if cmd_set_top.get('comm_list_delete'): + route_map_bgp_actions['set-community-delete'] = { + 'config': { + 'community-set-delete': cmd_set_top['comm_list_delete'] + } + } + + # Handle 'set' community + if cmd_set_top.get('community'): + route_map_bgp_actions['set-community'] = { + 'config': { + 'method': 'INLINE', + 'options': 'ADD' + }, + 'inline': { + 'config': { + 'communities': [] + } + } + } + + rmap_set_communities_cfg = \ + route_map_bgp_actions['set-community']['inline']['config']['communities'] + + if cmd_set_top['community'].get('community_number'): + + # Abort the playbook if the Community "none' attribute is configured. + if cfg_set_top: + if (cfg_set_top.get('community') and + cfg_set_top['community'].get('community_attributes') and + 'none' in cfg_set_top['community']['community_attributes']): + self._module.fail_json( + msg='\nPlaybook aborted: The route map "set" community ' + '"none" attribute is configured.\n\nPlease remove ' + 'the conflicting configuration to configure other ' + 'community "set" attributes.\n') + + comm_num_list = cmd_set_top['community']['community_number'] + + for comm_num in comm_num_list: + rmap_set_communities_cfg.append(comm_num) + + if cmd_set_top['community'].get('community_attributes'): + comm_attr_list = [] + comm_attr_list = cmd_set_top['community']['community_attributes'] + if 'none' in comm_attr_list: + # Verify that no other community attributes are being requested + # at the same time as the "none" attribute and that no + # community attributes are currently configured. Abort the + # playbook execution if these conditions are not met. + if len(comm_attr_list) > 1 or rmap_set_communities_cfg: + self._module.fail_json( + msg='\nPlaybook aborted: The route map "set" community "none"' + 'attribute cannot be configured when other "set" community ' + 'attributes are requested or configured.\n\n' + 'Please revise the playbook to configure the "none"' + 'attribute.\n') + + # Abort the playbook if other Community "set" attributes are + # currently configured. + if cfg_set_top: + if (cfg_set_top.get('community') and + (cfg_set_top['community'].get('community_number') or + (cfg_set_top['community'].get('community_attributes') and + 'none' not in cfg_set_top['community']['community_attributes']))): + self._module.fail_json( + msg='\nPlaybook aborted: The route map "set" community "none" ' + ' attribute cannot be configured when other"set" community ' + 'attributes are requested or configured.\n\n' + 'Please remove the conflicting configuration to ' + 'configure the "none" attribue.\n') + + # Proceed with configuring 'none' if the validity checks passed. + rmap_set_communities_cfg.append('openconfig-bgp-types:NONE') + else: + + # Abort the playbook if the Community "none' attribute is configured. + if cfg_set_top: + if (cfg_set_top.get('community') and + cfg_set_top['community'].get('community_attributes') and + 'none' in cfg_set_top['community']['community_attributes']): + self._module.fail_json( + msg='\nPlaybook aborted: The route map "set"community "none" attribute is ' + 'configured.\n\n' + 'Please remove the conflicting configuration to configure ' + 'other community "set" attributes.\n') + + comm_attr_rest_name = { + 'local_as': 'openconfig-bgp-types:NO_EXPORT_SUBCONFED', + 'no_advertise': 'openconfig-bgp-types:NO_ADVERTISE', + 'no_export': 'openconfig-bgp-types:NO_EXPORT', + 'no_peer': 'openconfig-bgp-types:NOPEER', + 'additive': 'openconfig-routing-policy-ext:ADDITIVE' + } + + for comm_attr in comm_attr_list: + rmap_set_communities_cfg.append(comm_attr_rest_name[comm_attr]) + + # Handle set extcommunity + if cmd_set_top.get('extcommunity'): + route_map_bgp_actions['set-ext-community'] = { + 'config': { + 'method': 'INLINE', + 'options': 'ADD' + }, + 'inline': { + 'config': { + 'communities': [] + } + } + } + + rmap_set_extcommunities_cfg = \ + route_map_bgp_actions['set-ext-community']['inline']['config']['communities'] + + if cmd_set_top['extcommunity'].get('rt'): + rt_list = cmd_set_top['extcommunity']['rt'] + + for rt_val in rt_list: + rmap_set_extcommunities_cfg.append("route-target:" + rt_val) + + if cmd_set_top['extcommunity'].get('soo'): + soo_list = cmd_set_top['extcommunity']['soo'] + + for soo in soo_list: + rmap_set_extcommunities_cfg.append("route-origin:" + soo) + + # + # Handle configuration for BGP policy "set" conditions + # to be located within the "config" sub-dictionary + # ---------------------------------------------------- + route_map_bgp_actions['config'] = {} + route_map_bgp_actions_cfg = \ + route_map_actions['openconfig-bgp-policy:bgp-actions']['config'] + + # Handle set IP next hop. + if cmd_set_top.get('ip_next_hop'): + route_map_bgp_actions_cfg['set-next-hop'] = cmd_set_top['ip_next_hop'] + + # Handle set IPv6 next hop. + if cmd_set_top.get('ipv6_next_hop'): + if cmd_set_top['ipv6_next_hop'].get('global_addr'): + route_map_bgp_actions_cfg['set-ipv6-next-hop-global'] = \ + cmd_set_top['ipv6_next_hop']['global_addr'] + if cmd_set_top['ipv6_next_hop'].get('prefer_global') is not None: + boolval = \ + self.yaml_bool_to_python_bool(cmd_set_top['ipv6_next_hop']['prefer_global']) + route_map_bgp_actions_cfg['set-ipv6-next-hop-prefer-global'] = boolval + + # Handle set local preference. + if cmd_set_top.get('local_preference'): + route_map_bgp_actions_cfg['set-local-pref'] = cmd_set_top['local_preference'] + + # Handle set metric + if cmd_set_top.get('metric'): + route_map_actions['metric-action'] = {'config': {}} + route_map_metric_actions = route_map_actions['metric-action']['config'] + + if cmd_set_top['metric'].get('value'): + route_map_metric_actions['metric'] = cmd_set_top['metric']['value'] + route_map_metric_actions['action'] = \ + 'openconfig-routing-policy:METRIC_SET_VALUE' + route_map_bgp_actions_cfg['set-med'] = cmd_set_top['metric']['value'] + elif cmd_set_top['metric'].get('rtt_action'): + if cmd_set_top['metric']['rtt_action'] == 'set': + route_map_metric_actions['action'] = \ + 'openconfig-routing-policy:METRIC_SET_RTT' + elif cmd_set_top['metric']['rtt_action'] == 'add': + route_map_metric_actions['action'] = \ + 'openconfig-routing-policy:METRIC_ADD_RTT' + elif cmd_set_top['metric']['rtt_action'] == 'subtract': + route_map_metric_actions['action'] = \ + 'openconfig-routing-policy:METRIC_SUBTRACT_RTT' + + if not route_map_metric_actions: + route_map_actions.pop('metric-action') + + # Handle set origin + if cmd_set_top.get('origin'): + route_map_bgp_actions_cfg['set-route-origin'] = cmd_set_top['origin'].upper() + + # Handle set weight + if cmd_set_top.get('weight'): + route_map_bgp_actions_cfg['set-weight'] = cmd_set_top['weight'] + + @staticmethod + def get_route_map_modify_call_attr(command, route_map_statement): + '''In the dict specified by the input route_map_statement paramenter, + provide REST API definitions of the "call" attribute (if present) + contained in the user input command dict specified by the "command" + input parameter to this function.''' + + call_val = command.get('call') + if not call_val: + return + + if not route_map_statement.get('conditions'): + route_map_statement['conditions'] = {'config': {}} + elif not route_map_statement['conditions'].get('config'): + route_map_statement['conditions']['config'] = {} + route_map_statement['conditions']['config']['call-policy'] = call_val + + def get_delete_all_route_map_cfg_request(self): + '''Append to the input list of REST API requests the REST API to + Delete all route map configuration''' + requests = [{'path': self.route_maps_uri, 'method': DELETE}] + return requests + + def get_delete_one_route_map_cfg(self, conf_map_name, requests): + '''Append to the input list of REST API requests the REST API to + delete all configuration for the specified route map.''' + + delete_rmap_path = self.route_map_uri.format(conf_map_name) + request = {'path': delete_rmap_path, 'method': DELETE} + requests.append(request) + + def get_delete_route_map_stmt_cfg(self, command, requests): + '''Append to the input list of REST API requests the REST API to + delete all configuration for the route map "statement" (route + map sub-section) specified by the combination of the route + map name and "statement" sequence number in the input + "command" dict.''' + conf_map_name = command.get('map_name') + conf_seq_num = command.get('sequence_num') + req_seq_num = str(conf_seq_num) + + delete_rmap_stmt_path = self.route_map_stmt_uri.format(conf_map_name, req_seq_num) + request = {'path': delete_rmap_stmt_path, 'method': DELETE} + requests.append(request) + + def get_delete_route_maps_requests(self, have, commands): + '''Traverse the input list of configuration "delete" commands obtained + from parsing the input playbook parameters. For each command, + create and return the appropriate set of REST API requests to delete + the appropriate elements from the route map specified by the current command.''' + + requests = [] + if commands: + for command in commands: + # Create requests for "eligible" attributes within the current route + # map statement. The content of the "command" object, on return from + # execution has only the subset of currently configured attributes + # within the full group of requested attributes for deletion from + # this route map statement. + self.get_delete_single_route_map_requests(have, command, requests) + return requests + + def get_delete_single_route_map_requests(self, have, command, requests): + '''Create and return the appropriate set of route map REST APIs + to delete the eligible requestd attributes from the route map + configuration specified by the current "command".''' + + if not command: + return + + # Validate the current command. + conf_map_name = command.get('map_name', None) + if not conf_map_name: + command = {} + return + conf_seq_num = command.get('sequence_num', None) + if not conf_seq_num: + if self.any_rmap_inst_in_have(conf_map_name, have): + self.get_delete_one_route_map_cfg(conf_map_name, requests) + return + + # Get the current configuration (if any) for this route map statement + cmd_rmap_have = self.get_matching_map(conf_map_name, conf_seq_num, have) + if not cmd_rmap_have: + command = {} + return + + # Check for route map statement deletion before proceeding further. + cmd_match_top = command.get('match') + if cmd_match_top: + cmd_match_top = command['match'] + + cmd_set_top = command.get('set') + if cmd_set_top: + cmd_set_top = command['set'] + + if not cmd_match_top and not cmd_set_top: + self.get_delete_route_map_stmt_cfg(command, requests) + return + + # Proceed with validity checking and execution + conf_action = command.get('action', None) + if not conf_action: + self._module.fail_json( + msg="\nThe 'action' attribute is required, but is absent" + "for route map {0} sequence number {1}\n".format( + conf_map_name, conf_seq_num)) + + if conf_action not in ('permit', 'deny'): + self._module.fail_json( + msg="\nInvalid 'action' attribute value {0} for" + "route map {1} sequence number {2}\n".format( + conf_action, conf_map_name, conf_seq_num)) + command = {} + return + + if cmd_match_top: + self.get_route_map_delete_match_attr(command, cmd_rmap_have, requests) + if cmd_set_top: + self.get_route_map_delete_set_attr(command, cmd_rmap_have, requests) + if command: + self.get_route_map_delete_call_attr(command, cmd_rmap_have, requests) + + return + + @staticmethod + def get_matching_map(conf_map_name, conf_seq_num, input_list): + '''In the input list of command or configuration dicts, find the route map + configuration "statement" (if it exists) for the specified map name + and sequence number.''' + for cfg_route_map in input_list: + if cfg_route_map.get('map_name') and cfg_route_map.get('sequence_num'): + if (cfg_route_map['map_name'] == conf_map_name and + cfg_route_map.get('sequence_num') == conf_seq_num): + return cfg_route_map + + return {} + + @staticmethod + def any_rmap_inst_in_have(conf_map_name, have): + '''In the current configuration on the target device, determine if there + is at least one configuration "statement" for the specified route map name + from the input playbook request.''' + for cfg_route_map in have: + if cfg_route_map.get('map_name'): + if cfg_route_map['map_name'] == conf_map_name: + return True + + return False + + def get_route_map_delete_match_attr(self, command, cmd_rmap_have, requests): + '''Append to the input list of REST API requests the REST APIs needed + for deletion of all eligible "match" attributes contained in the + user input command dict specified by the "command" input parameter + to this function. Modify the contents of the "command" object to + remove any attributes that are not currently configured. These + attributes are not "eligible" for deletion and no REST API "request" + is generated for them.''' + + conf_map_name = command['map_name'] + conf_seq_num = command['sequence_num'] + req_seq_num = str(conf_seq_num) + + match_top = command.get('match') + if not match_top: + return + match_keys = match_top.keys() + + cfg_match_top = cmd_rmap_have.get('match') + if not cfg_match_top: + command.pop('match') + return + cfg_match_keys = cfg_match_top.keys() + + match_both_keys = set(match_keys).intersection(cfg_match_keys) + + # Remove any requested deletion items that aren't configured + match_pop_keys = set(match_keys).difference(match_both_keys) + for key in match_pop_keys: + match_top.pop(key) + if not match_top or not match_both_keys: + command.pop('match') + return + + # Handle configuration for BGP policy "match" conditions + self.get_route_map_delete_match_bgp(command, match_both_keys, cmd_rmap_have, requests) + if not command.get('match'): + if 'match' in command: + command.pop('match') + return + + # Handle generic top level match attributes. + generic_match_rest_attr = { + 'interface': 'match-interface', + 'source_vrf': 'openconfig-routing-policy-ext:match-src-network-instance', + 'tag': 'match-tag-set/config/openconfig-routing-policy-ext:tag-value', + 'source_protocol': 'config/install-protocol-eq' + } + + match_delete_req_base = (self.route_map_stmt_base_uri.format(conf_map_name, req_seq_num) + + 'conditions/') + + for key in generic_match_rest_attr: + if key in match_both_keys and match_top[key] == cfg_match_top[key]: + request_uri = match_delete_req_base + generic_match_rest_attr[key] + request = {'path': request_uri, 'method': DELETE} + requests.append(request) + elif key in match_top: + match_top.pop(key) + if not match_top: + command.pop('match') + return + + # Handle match peer + peer_str = '' + if 'peer' in match_both_keys: + if (match_top['peer'].get('interface') and cfg_match_top['peer'].get('interface') and + match_top['peer']['interface'] == cfg_match_top['peer']['interface']): + peer_str = match_top['peer']['interface'] + elif (match_top['peer'].get('ip') and cfg_match_top['peer'].get('ip') and + match_top['peer']['ip'] == cfg_match_top['peer']['ip']): + peer_str = match_top['peer']['ip'] + elif (match_top['peer'].get('ipv6') and cfg_match_top['peer'].get('ipv6') and + match_top['peer']['ipv6'] == cfg_match_top['peer']['ipv6']): + peer_str = match_top['peer']['ipv6'] + else: + match_top.pop('peer') + if not match_top: + command.pop('match') + return + + if peer_str: + request_uri = (match_delete_req_base + + 'match-neighbor-set/config/' + 'openconfig-routing-policy-ext:address={0}'.format(peer_str)) + request = {'path': request_uri, 'method': DELETE} + requests.append(request) + + elif 'peer' in match_top: + match_top.pop('peer') + if not match_top: + command.pop('match') + return + + # Handle match IP address/prefix + if ('ip' in match_both_keys and match_top['ip'].get('address') and + match_top['ip']['address'] == cfg_match_top['ip'].get('address')): + request_uri = match_delete_req_base + 'match-prefix-set/config/prefix-set' + request = {'path': request_uri, 'method': DELETE} + requests.append(request) + elif 'ip' in match_top: + match_top.pop('ip') + if not match_top: + command.pop('match') + return + + # Handle match IPv6 address/prefix + if ('ipv6' in match_both_keys and match_top['ipv6'].get('address') and + match_top['ipv6']['address'] == cfg_match_top['ipv6'].get('address')): + ipv6_attr_name = \ + 'match-prefix-set/config/openconfig-routing-policy-ext:ipv6-prefix-set' + request_uri = (match_delete_req_base + ipv6_attr_name) + request = {'path': request_uri, 'method': DELETE} + requests.append(request) + elif 'ipv6' in match_top: + match_top.pop('ipv6') + if not match_top: + command.pop('match') + return + + def get_route_map_delete_match_bgp(self, command, match_both_keys, cmd_rmap_have, requests): + '''Append to the input list of REST API requests the REST APIs needed + for deletion of all eligible "match" attributes defined within the + BGP match conditions section of the openconfig routing-policy + definitions for "policy-definitions" (route maps).''' + + conf_map_name = command.get('map_name', None) + conf_seq_num = command.get('sequence_num', None) + req_seq_num = str(conf_seq_num) + match_top = command['match'] + cfg_match_top = cmd_rmap_have.get('match') + route_map_stmt_base_uri_fmt = self.route_map_stmt_base_uri.format(conf_map_name, + req_seq_num) + bgp_match_delete_req_base = (route_map_stmt_base_uri_fmt + + 'conditions/openconfig-bgp-policy:bgp-conditions/') + + # Handle BGP match items within the "config" sub-tree in the openconfig REST API definitons. + self.get_route_map_delete_match_bgp_cfg(command, match_both_keys, cmd_rmap_have, requests) + + # Handle as_path + if 'as_path' in match_both_keys and match_top['as_path'] == cfg_match_top['as_path']: + request_uri = bgp_match_delete_req_base + 'match-as-path-set' + request = {'path': request_uri, 'method': DELETE} + requests.append(request) + elif match_top.get('as_path'): + match_top.pop('as_path') + + # Handle match evpn + if 'evpn' in match_both_keys: + evpn_cfg_delete_base = \ + bgp_match_delete_req_base + 'openconfig-bgp-policy-ext:match-evpn-set/config/' + evpn_attrs = match_top['evpn'] + evpn_match_keys = evpn_attrs.keys() + evpn_rest_attr = { + 'default_route': 'default-type5-route', + 'route_type': 'route-type', + 'vni': 'vni-number' + } + pop_list = [] + for key in evpn_match_keys: + if (key not in cfg_match_top['evpn'] or + evpn_attrs[key] != cfg_match_top['evpn'][key]): + pop_list.append(key) + else: + request_uri = evpn_cfg_delete_base + evpn_rest_attr[key] + request = {'path': request_uri, 'method': DELETE} + requests.append(request) + for key in pop_list: + match_top['evpn'].pop(key) + if not match_top['evpn']: + match_top.pop('evpn') + + def get_route_map_delete_match_bgp_cfg(self, command, match_both_keys, cmd_rmap_have, requests): + '''Append to the input list of REST API requests the REST APIs needed + for deletion of all eligible "match" attributes defined within the + BGP match conditions 'config' section of the openconfig routing-policy + definitions for "policy-definitions" (route maps).''' + + match_top = command['match'] + cfg_match_top = cmd_rmap_have.get('match') + conf_map_name = command['map_name'] + conf_seq_num = command['sequence_num'] + req_seq_num = str(conf_seq_num) + bgp_keys = {'metric', 'origin', 'local_preference', 'community', 'ext_comm', 'ip'} + delete_bgp_keys = bgp_keys.intersection(match_both_keys) + if not delete_bgp_keys: + return + delete_bgp_attrs = [] + bgp_match_delete_req_base = (self.route_map_stmt_base_uri.format(conf_map_name, + req_seq_num) + + 'conditions/openconfig-bgp-policy:bgp-conditions/config/') + + # Check for IP next hop deletion. This is a special case because "next_hop" is + # a level below "ip" in the argspec hierarchy. If 'ip' is the only key in + # delete_bgp_keys, and IP next hop deletion is not required, there is no + # BGP condition match attribute deletion required. + if 'ip' in delete_bgp_keys: + if not match_top['ip'].get('next_hop') or not cfg_match_top['ip'].get('next_hop'): + delete_bgp_keys.remove('ip') + if 'next_hop' in match_top['ip']: + match_top['ip'].pop('next_hop') + if not match_top['ip']: + match_top.pop('ip') + if not match_top: + command.pop('match') + return + + if not delete_bgp_keys: + return + else: + if match_top['ip']['next_hop'] == cfg_match_top['ip']['next_hop']: + request_uri = (bgp_match_delete_req_base + + 'openconfig-bgp-policy-ext:next-hop-set') + request = {'path': request_uri, 'method': DELETE} + requests.append(request) + else: + match_top['ip'].pop('next_hop') + if not match_top['ip']: + match_top.pop('ip') + if not match_top: + command.pop('match') + return + + delete_bgp_keys.remove('ip') + if not delete_bgp_keys: + return + + # Check for deletion of other BGP match attributes. + bgp_rest_attr = { + 'community': 'community-set', + 'ext_comm': 'ext-community-set', + 'local_preference': 'local-pref-eq', + 'metric': 'med-eq', + 'origin': 'origin-eq' + } + for key in delete_bgp_keys: + if match_top[key] == cfg_match_top[key]: + bgp_rest_attr_key = bgp_rest_attr[key] + delete_bgp_attrs.append(bgp_rest_attr_key) + else: + match_top.pop(key) + if not match_top: + command.pop('match') + return + + if not delete_bgp_attrs: + return + + # Create requests for deletion of the eligible BGP match attributes. + for attr in delete_bgp_attrs: + request_uri = bgp_match_delete_req_base + attr + request = {'path': request_uri, 'method': DELETE} + requests.append(request) + + def get_route_map_delete_set_attr(self, command, cmd_rmap_have, requests): + '''Append to the input list of REST API requests the REST APIs needed + for deletion of all eligible "set" attributes contained in the + user input command dict specified by the "command" input parameter + to this function. Modify the contents of the "command" object to + remove any attributes that are not currently configured. These + attributes are not "eligible" for deletion and no REST API "request" + is generated for them.''' + + cmd_set_top = command.get('set') + if not cmd_set_top: + return + set_keys = cmd_set_top.keys() + + cfg_set_top = cmd_rmap_have.get('set') + if not cfg_set_top: + command.pop('set') + return + cfg_set_keys = cfg_set_top.keys() + + set_both_keys = set(set_keys).intersection(cfg_set_keys) + if not set_both_keys: + command.pop('set') + return + + conf_map_name = command['map_name'] + conf_seq_num = command['sequence_num'] + req_seq_num = str(conf_seq_num) + set_delete_base = (self.route_map_stmt_base_uri.format(conf_map_name, + req_seq_num) + 'actions/') + + # Handle configuration for BGP policy "set" conditions + self.get_route_map_delete_set_bgp(command, set_both_keys, cmd_rmap_have, requests) + cmd_set_top = command.get('set') + if not cmd_set_top: + command.pop('set') + return + + # Handle metric "set" attributes. + if 'metric' in set_both_keys: + set_delete_metric_base = set_delete_base + 'metric-action/config' + if cmd_set_top['metric'].get('rtt_action'): + if cmd_set_top['metric']['rtt_action'] == cfg_set_top['metric'].get('rtt_action'): + request_uri = set_delete_metric_base + request = {'path': request_uri, 'method': DELETE} + requests.append(request) + else: + cmd_set_top.pop('metric') + if not cmd_set_top: + command.pop('set') + elif cmd_set_top['metric'].get('value'): + set_delete_bgp_base = set_delete_base + 'openconfig-bgp-policy:bgp-actions/' + if cmd_set_top['metric']['value'] == cfg_set_top['metric'].get('value'): + request = {'path': set_delete_metric_base, 'method': DELETE} + requests.append(request) + request = { + 'path': set_delete_bgp_base + 'config/set-med', + 'method': DELETE + } + requests.append(request) + + else: + cmd_set_top.pop('metric') + if not cmd_set_top: + command.pop('set') + else: + # 'metric' is not in set_both_keys + if cmd_set_top.get('metric'): + cmd_set_top.pop('metric') + if not cmd_set_top: + command.pop('set') + return + + def get_route_map_delete_set_bgp(self, command, set_both_keys, cmd_rmap_have, requests): + '''Append to the input list of REST API requests the REST APIs needed + for deletion of all eligible "set" attributes defined within the + BGP "set" conditions section of the openconfig routing-policy + definitions for "policy-definitions" (route maps).''' + + cmd_set_top = command['set'] + cfg_set_top = cmd_rmap_have.get('set') + conf_map_name = command['map_name'] + conf_seq_num = command['sequence_num'] + req_seq_num = str(conf_seq_num) + bgp_set_delete_req_base = (self.route_map_stmt_base_uri.format(conf_map_name, req_seq_num) + + 'actions/openconfig-bgp-policy:bgp-actions/') + + # Handle BGP "set" items within the "config" sub-tree in the openconfig REST API definitons. + self.get_route_map_delete_set_bgp_cfg(command, set_both_keys, cmd_rmap_have, requests) + + # Handle as_path_prepend + if ('as_path_prepend' in set_both_keys and + cmd_set_top['as_path_prepend'] == cfg_set_top['as_path_prepend']): + request_uri = bgp_set_delete_req_base + 'set-as-path-prepend' + request = {'path': request_uri, 'method': DELETE} + requests.append(request) + else: + if cmd_set_top.get('as_path_prepend'): + cmd_set_top.pop('as_path_prepend') + if not cmd_set_top: + return + + # Handle the "community list delete" (comm_list_delete) attribute + if ('comm_list_delete' in set_both_keys and + cmd_set_top['comm_list_delete'] == cfg_set_top['comm_list_delete']): + request_uri = bgp_set_delete_req_base + 'set-community-delete' + request = {'path': request_uri, 'method': DELETE} + requests.append(request) + else: + if cmd_set_top.get('comm_list_delete'): + cmd_set_top.pop('comm_list_delete') + if not cmd_set_top: + return + + # Handle "set community": Handle named attributes first, then handle community numbers + if 'community' not in set_both_keys: + if cmd_set_top.get('community'): + cmd_set_top.pop('community') + if not cmd_set_top: + return + else: + community_attr_remove_list = [] + set_community_delete_attrs = [] + if cmd_set_top['community'].get('community_attributes'): + if cfg_set_top['community'].get('community_attributes'): + # Append eligible entries to the delete list. Remember which entries + # are ineligible. + for community_attr in cmd_set_top['community']['community_attributes']: + if community_attr in cfg_set_top['community']['community_attributes']: + community_rest_name = self.set_community_rest_names[community_attr] + set_community_delete_attrs.append(community_rest_name) + else: + community_attr_remove_list.append(community_attr) + + # Delete ineligible entries from the command list. + for community_attr in community_attr_remove_list: + cmd_set_top['community']['community_attributes'].remove(community_attr) + if not cmd_set_top['community']['community_attributes']: + cmd_set_top['community'].pop('community_attributes') + else: + # No community attribute entries are configured. Pop the corresponding + # commands from the command list. + cmd_set_top['community'].pop('community_attributes') + + if not cmd_set_top['community']: + cmd_set_top.pop('community') + if not cmd_set_top: + return + + # Handle deletion of "set" community numbers. + if cmd_set_top.get('community') and cmd_set_top['community'].get('community_number'): + community_number_remove_list = [] + if cfg_set_top['community'].get('community_number'): + # Append eligible entries to the delete list. Remember which entries + # are ineligible. + for community_number in cmd_set_top['community']['community_number']: + if community_number in cfg_set_top['community']['community_number']: + set_community_delete_attrs.append(community_number) + else: + community_number_remove_list.append(community_number) + + # Delete ineligible entries from the command list. + for community_number in community_number_remove_list: + cmd_set_top['community']['community_number'].remove(community_number) + if not cmd_set_top['community']['community_number']: + cmd_set_top['community'].pop('community_number') + else: + # If no community number entries are configured, pop the entire + # community number command dict. + cmd_set_top['community'].pop('community_number') + + if not cmd_set_top['community']: + cmd_set_top.pop('community') + if not cmd_set_top: + return + + # Format and enqueue a request to delete eligible community attributes + if set_community_delete_attrs: + bgp_set_delete_community_uri = bgp_set_delete_req_base + 'set-community' + bgp_set_delete_comm_payload = \ + {'openconfig-bgp-policy:set-community': {}} + bgp_set_delete_comm_payload_contents = \ + bgp_set_delete_comm_payload['openconfig-bgp-policy:set-community'] + bgp_set_delete_comm_payload_contents['config'] = \ + {'method': 'INLINE', 'options': 'REMOVE'} + bgp_set_delete_comm_payload_contents['inline'] = \ + {'config': {'communities': set_community_delete_attrs}} + + request = { + 'path': bgp_set_delete_community_uri, + 'method': PATCH, + 'data': bgp_set_delete_comm_payload + } + requests.append(request) + + # Handle set "extended community" deletion + if 'extcommunity' not in set_both_keys: + if cmd_set_top.get('extcommunity'): + cmd_set_top.pop('extcommunity') + if not cmd_set_top: + return + else: + set_extcommunity_delete_attrs = [] + + for extcomm_type in self.set_extcomm_rest_names: + ext_comm_number_remove_list = [] + if cmd_set_top['extcommunity'].get(extcomm_type): + if cfg_set_top['extcommunity'].get(extcomm_type): + # Append eligible entries to the delete list. Remember which entries + # are ineligible. + for extcomm_number in cmd_set_top['extcommunity'][extcomm_type]: + if extcomm_number in cfg_set_top['extcommunity'][extcomm_type]: + set_extcommunity_delete_attrs.append( + self.set_extcomm_rest_names[extcomm_type] + extcomm_number) + else: + ext_comm_number_remove_list.append(extcomm_number) + + # Delete ineligible entries from the command list. + for extcomm_number in ext_comm_number_remove_list: + cmd_set_top['extcommunity'][extcomm_type].remove(extcomm_number) + if not cmd_set_top['extcommunity'][extcomm_type]: + cmd_set_top['extcommunity'].pop(extcomm_type) + else: + # If no extcommunity entries of this type are configured, + # pop the entire extcommunity command sub-dict for this type. + cmd_set_top['extcommunity'].pop(extcomm_type) + + if not cmd_set_top['extcommunity']: + cmd_set_top.pop('extcommunity') + if not cmd_set_top: + return + + # Format and enqueue a request to delete eligible extcommunity attributes + if set_extcommunity_delete_attrs: + bgp_set_delete_extcomm_uri = bgp_set_delete_req_base + 'set-ext-community' + bgp_set_delete_extcomm_payload = \ + {'openconfig-bgp-policy:set-ext-community': {}} + bgp_set_delete_comm_payload_contents = \ + bgp_set_delete_extcomm_payload['openconfig-bgp-policy:set-ext-community'] + bgp_set_delete_comm_payload_contents['config'] = \ + {'method': 'INLINE', 'options': 'REMOVE'} + bgp_set_delete_comm_payload_contents['inline'] = \ + {'config': {'communities': set_extcommunity_delete_attrs}} + + request = { + 'path': bgp_set_delete_extcomm_uri, + 'method': PATCH, + 'data': bgp_set_delete_extcomm_payload + } + requests.append(request) + + def get_route_map_delete_set_bgp_cfg(self, command, set_both_keys, cmd_rmap_have, requests): + '''Append to the input list of REST API requests the REST APIs needed + for deletion of all eligible "set" attributes defined within the + BGP set conditions 'config' section of the openconfig routing-policy + definitions for "policy-definitions" (route maps).''' + + cmd_set_top = command['set'] + + cfg_set_top = cmd_rmap_have.get('set') + conf_map_name = command['map_name'] + conf_seq_num = command['sequence_num'] + req_seq_num = str(conf_seq_num) + bgp_set_delete_req_base = (self.route_map_stmt_base_uri.format(conf_map_name, req_seq_num) + + 'actions/openconfig-bgp-policy:bgp-actions/config/') + # Note: Although 'metric' (REST API 'set-med') is in this REST API configuration + # group, it is handled separately as part of deleting the top level, functionally + # related 'metric-action' attribute. + bgp_cfg_keys = {'ip_next_hop', 'origin', 'local_preference', 'ipv6_next_hop', 'weight'} + delete_bgp_keys = bgp_cfg_keys.intersection(set_both_keys) + if not delete_bgp_keys: + for bgp_key in bgp_cfg_keys: + if bgp_key in cmd_set_top: + cmd_set_top.pop(bgp_key) + return + + delete_bgp_attrs = [] + + # Handle the special case of ipv6_next_hop + if 'ipv6_next_hop' in delete_bgp_keys: + delete_bgp_keys.remove('ipv6_next_hop') + ipv6_next_hop_rest_names = { + 'global_addr': 'set-ipv6-next-hop-global', + 'prefer_global': 'set-ipv6-next-hop-prefer-global' + } + for ipv6_next_hop_key in ipv6_next_hop_rest_names: + if cmd_set_top['ipv6_next_hop'].get(ipv6_next_hop_key) is not None: + if (cmd_set_top['ipv6_next_hop'][ipv6_next_hop_key] == + cfg_set_top['ipv6_next_hop'].get(ipv6_next_hop_key)): + delete_bgp_attrs.append(ipv6_next_hop_rest_names[ipv6_next_hop_key]) + else: + cmd_set_top['ipv6_next_hop'].pop(ipv6_next_hop_key) + if not cmd_set_top['ipv6_next_hop']: + cmd_set_top.pop('ipv6_next_hop') + if not cmd_set_top: + return + + if not delete_bgp_keys and not delete_bgp_attrs: + return + + # Handle other BGP "config" attributes + bgp_cfg_rest_names = { + 'ip_next_hop': 'set-next-hop', + 'local_preference': 'set-local-pref', + 'origin': 'set-route-origin', + 'weight': 'set-weight' + } + + for bgp_cfg_key in bgp_cfg_rest_names: + if bgp_cfg_key in delete_bgp_keys: + if cmd_set_top[bgp_cfg_key] == cfg_set_top[bgp_cfg_key]: + delete_bgp_attrs.append(bgp_cfg_rest_names[bgp_cfg_key]) + else: + cmd_set_top.pop(bgp_cfg_key) + + if not cmd_set_top: + command.pop('set') + return + + for delete_bgp_attr in delete_bgp_attrs: + del_set_bgp_cfg_uri = bgp_set_delete_req_base + delete_bgp_attr + request = {'path': del_set_bgp_cfg_uri, 'method': DELETE} + requests.append(request) + + def get_route_map_delete_call_attr(self, command, cmd_rmap_have, requests): + '''Append to the input list of REST API requests the REST API needed + for deletion of the "call" attribute if this attribute it contained in the + user input command dict specified by the "command" input parameter + to this function and it is currently configured. Modify the contents of + the "command" object to remove the "call" attribute if it is not currently + configured.''' + + if not command.get('call'): + return + + if not command['call'] == cmd_rmap_have.get('call'): + command.pop('call') + return + + conf_map_name = command['map_name'] + req_seq_num = str(command['sequence_num']) + + call_delete_req_uri = \ + (self.route_map_stmt_base_uri.format( + conf_map_name, req_seq_num) + 'conditions/config/call-policy') + request = {'path': call_delete_req_uri, 'method': DELETE} + requests.append(request) + + @staticmethod + def yaml_bool_to_python_bool(yaml_bool): + '''Convert the input YAML bool value to a Python bool value''' + boolval = False + if yaml_bool is None: + boolval = False + elif yaml_bool: + boolval = True + + return boolval + + def route_map_remove_configured_match_peer(self, route_map_payload, have, requests): + '''If a route map "match peer" condition is configured in the route map + statement corresponding to the incoming route map update request + specified by the "route_map_payload" input parameter, equeue a REST API request + to delete it.''' + + if (route_map_payload['statements']['statement'][0].get('conditions') and + route_map_payload['statements']['statement'][0] + ['conditions'].get('match-neighbor-set')): + peer = self.match_peer_configured(route_map_payload, have) + if peer: + request = self.create_match_peer_delete_request(route_map_payload, peer) + if request: + requests.append(request) + + def match_peer_configured(self, route_map_payload, have): + '''Determine if the "match peer ..." condition is already configured for the + route map statement corresponding to the incoming route map update request + specified by the "route_map_payload" input parameter. Return the peer string + if a "match peer" condition is already configured. Otherwise, return an empty + string''' + + if not route_map_payload or not have: + return '' + + conf_map_name = route_map_payload.get('name') + conf_seq_num = (route_map_payload['statements']['statement'][0]['name']) + if not conf_map_name or not conf_seq_num: + return '' + + # Get the current configuration (if any) for this route map statement + cmd_rmap_have = self.get_matching_map(conf_map_name, int(conf_seq_num), have) + if (not cmd_rmap_have or not cmd_rmap_have.get('match') or + not cmd_rmap_have['match'].get('peer')): + return '' + + peer_dict = cmd_rmap_have['match']['peer'] + if peer_dict.get('interface'): + peer_str = peer_dict['interface'] + elif peer_dict.get('ip'): + peer_str = peer_dict['ip'] + elif peer_dict.get('ipv6'): + peer_str = peer_dict['ipv6'] + else: + return '' + + return peer_str + + def create_match_peer_delete_request(self, route_map_payload, peer_str): + '''Create a request to delete the current "match peer" configuration for the + route map statement corresponding to the incoming route map update request + specified by the "route_map_payload," input parameter. Return the created request.''' + + if not route_map_payload: + return {} + + conf_map_name = route_map_payload.get('name') + conf_seq_num = route_map_payload['statements']['statement'][0]['name'] + if not conf_map_name or not conf_seq_num: + return {} + match_delete_req_base = (self.route_map_stmt_base_uri.format(conf_map_name, conf_seq_num) + + 'conditions/') + + request_uri = (match_delete_req_base + + 'match-neighbor-set/config/' + 'openconfig-routing-policy-ext:address={0}'.format(peer_str)) + request = {'path': request_uri, 'method': DELETE} + return request + + def get_delete_replaced_groupings(self, commands, have): + '''For each of the route maps specified in the "commands" input list, + create requests to delete any existing route map configuration + groupings for which modified attribute requests are specified.''' + + requests = [] + for command in commands: + self.get_delete_one_map_replaced_groupings(command, have, requests) + return requests + + def get_delete_one_map_replaced_groupings(self, command, have, requests): + '''For the route map specified by the input "command", create requests + to delete any existing route map configuration groupings for which + modified attribute requests are specified''' + + if not command: + return {} + + conf_map_name = command.get('map_name', None) + conf_seq_num = command.get('sequence_num', None) + if not conf_map_name or not conf_seq_num: + return {} + + # Get the current configuration (if any) for this route map + cmd_rmap_have = self.get_matching_map(conf_map_name, conf_seq_num, have) + + # If there's nothing configured for this route map, there's nothing + # to delete. + if not cmd_rmap_have: + command = {} + return command + + self.get_delete_route_map_replaced_match_groupings(command, cmd_rmap_have, requests) + replaced_set_group_requests = [] + self.get_delete_route_map_replaced_set_groupings(command, cmd_rmap_have, + replaced_set_group_requests) + if replaced_set_group_requests: + requests.extend(replaced_set_group_requests) + + # Note: Because the "call" route map attribute is a "flat" attribute, not + # a dictionary, no "pre-delete" is required for this branch of the route map + # argspec for handling of "replaced" state + + return command + + def get_delete_route_map_replaced_match_groupings(self, command, cmd_rmap_have, requests): + '''For the route map specified by the input "command", create requests + to delete any existing route map "match" configuration groupings for which + modified attribute requests are specified''' + + if not command.get('match'): + return + + conf_map_name = command.get('map_name', None) + conf_seq_num = command.get('sequence_num', None) + req_seq_num = str(conf_seq_num) + + cmd_match_top = command['match'] + cfg_match_top = cmd_rmap_have.get('match') + + # If there are no 'match' attributes configured for this route map, + # there's nothing to delete. + if not cfg_match_top: + command.pop('match') + return + + match_delete_req_base = (self.route_map_stmt_base_uri.format(conf_map_name, req_seq_num) + + 'conditions/') + + # Obtain the set of "match" keys for which changes have been requested and + # the subset of those keys for which configuration currently exists. + cmd_match_keys = cmd_match_top.keys() + cfg_match_keys = cfg_match_top.keys() + + peer_str = '' + if 'peer' in cfg_match_keys: + peer_dict = cfg_match_top['peer'] + # Only one peer key at a time can be configured. + peer_key = list(peer_dict.keys())[0] + peer_str = peer_dict[peer_key] + + bgp_match_delete_req_base = match_delete_req_base + 'openconfig-bgp-policy:bgp-conditions/' + match_top_level_keys = [ + 'as_path', + 'community', + 'ext_comm', + 'interface', + 'ipv6', + 'local_preference', + 'metric', + 'origin', + 'peer', + 'source_protocol', + 'source_vrf', + 'tag' + ] + + match_multi_level_keys = [ + 'evpn', + 'ip', + ] + + match_uri_attr = { + 'as_path': bgp_match_delete_req_base + 'match-as-path-set', + 'community': bgp_match_delete_req_base + 'config/community-set', + 'evpn': bgp_match_delete_req_base + 'openconfig-bgp-policy-ext:match-evpn-set/config/', + 'ext_comm': bgp_match_delete_req_base + 'config/ext-community-set', + 'interface': match_delete_req_base + 'match-interface', + 'ip': { + 'address': match_delete_req_base + 'match-prefix-set/config/prefix-set', + 'next_hop': (bgp_match_delete_req_base + + 'config/openconfig-bgp-policy-ext:next-hop-set') + }, + 'ipv6': (match_delete_req_base + + 'match-prefix-set/config/openconfig-routing-policy-ext:ipv6-prefix-set'), + 'local_preference': bgp_match_delete_req_base + 'config/local-pref-eq', + 'metric': bgp_match_delete_req_base + 'config/med-eq', + 'origin': bgp_match_delete_req_base + 'config/origin-eq', + 'peer': (match_delete_req_base + + 'match-neighbor-set/config/' + 'openconfig-routing-policy-ext:address={0}'.format(peer_str)), + 'source_protocol': match_delete_req_base + 'config/install-protocol-eq', + 'source_vrf': (match_delete_req_base + + 'openconfig-routing-policy-ext:match-src-network-instance'), + 'tag': (match_delete_req_base + + 'match-tag-set/config/openconfig-routing-policy-ext:tag-value') + } + + # Remove all appropriate "match" configuration for this route map if any of the + # following criteria are met: (See the note below regarding what configuration + # is "appropriate"for deletion.) + # + # 1) Any top level attribute is specified with a value different from its current + # configured value. + # 2) Any top level attribute is specified that is not currently configured. + # 3) The set of top level attributes specified does not include all currently + # configured attributes (regardless of whether the specified values for + # these attributes are the same as the ones courrently configured). + # (Note: Although the IPv6 attribute is defined as a nested dictionary + # to allow for future expansion, it is handled here as a top level + # attrbute because it currently has only one member.) + # + # When deletion has been triggered, an attribute is deleted only if it is + # not present at all in the requested configuration. (If it is present in + # the requested configuration, the "merge" phase of the "replaced" state + # operation will modify it as needed, so it doesn't need to be explicitly + # deleted during the "deletion" phase.) + # + cfg_top_level_key_set = set(cfg_match_keys).intersection(set(match_top_level_keys)) + cmd_top_level_key_set = set(cmd_match_keys).intersection(set(match_top_level_keys)) + symmetric_diff_set = cmd_top_level_key_set.symmetric_difference(cfg_top_level_key_set) + intersection_diff_set = cmd_top_level_key_set.intersection(cfg_top_level_key_set) + cmd_delete_dict = {} + if (cmd_top_level_key_set and symmetric_diff_set or + (any(keyname for keyname in intersection_diff_set if + cmd_match_top[keyname] != cfg_match_top[keyname]))): + + # Deletion has been triggered. First, delete all approriate top level + # attributes + self.delete_replaced_dict_config( + cfg_key_set=cfg_top_level_key_set, + cmd_key_set=cmd_top_level_key_set, + cfg_parent_dict=cfg_match_top, + uri_attr=match_uri_attr, + uri_dict_key='cfg_dict_member_key', + deletion_dict=cmd_delete_dict, + requests=requests) + + # Next, delete all appropriate sub dictionary attributes. + match_dict_deletions = {} + for match_key in match_multi_level_keys: + cfg_key_set = {} + cmd_key_set = {} + if match_key in cfg_match_top: + cfg_key_set = set(cfg_match_top[match_key].keys()) + if match_key in cfg_match_top: + cmd_key_set = ([]) + if cmd_match_top.get(match_key): + cmd_key_set = set(cmd_match_top[match_key].keys()) + match_dict_deletions[match_key] = {} + match_dict_deletions_subdict = match_dict_deletions[match_key] + self.delete_replaced_dict_config( + cfg_key_set=cfg_key_set, + cmd_key_set=cmd_key_set, + cfg_parent_dict=cfg_match_top[match_key], + uri_attr=match_uri_attr, + uri_dict_key=match_key, + deletion_dict=match_dict_deletions_subdict, + requests=requests) + + # Update the dict specifying deleted commands + command.pop('match') + if cmd_delete_dict: + command['match'] = cmd_delete_dict + command['match'].update(match_dict_deletions) + return + + # If no top level attribute changes were requested, check for changes in + # dictionaries nested below the top level. + # ----------------------------------------------------------------------- + match_key_deletions = {} + for match_key in match_multi_level_keys: + if match_key in cmd_match_top: + if match_key in cfg_match_top: + cmd_key_set = set((cmd_match_top[match_key].keys())) + cfg_key_set = set(cfg_match_top[match_key].keys()) + symmetric_diff_set = cmd_key_set.symmetric_difference(cfg_key_set) + intersection_diff_set = cmd_key_set.intersection(cfg_key_set) + if (symmetric_diff_set or + (any(keyname for keyname in intersection_diff_set if + cmd_match_top[match_key][keyname] != + cfg_match_top[match_key][keyname]))): + + match_key_deletions[match_key] = {} + match_key_deletions_subdict = match_key_deletions[match_key] + self.delete_replaced_dict_config( + cfg_key_set=cfg_key_set, + cmd_key_set=cmd_key_set, + cfg_parent_dict=cfg_match_top[match_key], + uri_attr=match_uri_attr, + uri_dict_key=match_key, + deletion_dict=match_key_deletions_subdict, + requests=requests) + + command.pop('match') + if match_key_deletions: + command['match'] = match_key_deletions + + @staticmethod + def delete_replaced_dict_config(**in_args): + ''' Create and enqueue deletion requests for the appropriate attributes in the dictionary + specified by "dict_key". Update the input deletion_dict with the deleted attributes. + The input 'inargs' is assumed to contain the following keyword arguments: + + cfg_key_set: The set of currently configured keys for the target dict + + cmd_key_set: The set of currently requested update keys for the target dict + + cfg_parent_dict: The configured dictionary containing the input key set + + uri_attr: a dictionary specifying REST URIs keyed by argspec keys + + uri_dict_key: The key for top level attribue to be used for uri lookup. If set + to the string value 'cfg_dict_member_key', the current value of 'cfg_dict_member_key' + is used. Otherwise, the specified value is used directly. + + deletion_dict: a dictionary containing attributes deleted from the parent dict + + requests: The list of REST API requests for the executing playbook section + ''' + + # Set the default uri_key value. + uri_key = in_args['uri_dict_key'] + + # Iterate through members of the parent dict. + for cfg_dict_member_key in in_args['cfg_key_set'].difference(in_args['cmd_key_set']): + cfg_dict_member_val = in_args['cfg_parent_dict'][cfg_dict_member_key] + if in_args['uri_dict_key'] == 'cfg_dict_member_key': + uri_key = cfg_dict_member_key + uri = in_args['uri_attr'][uri_key] + in_args['deletion_dict'].update( + {cfg_dict_member_key: cfg_dict_member_val}) + if isinstance(uri, dict): + for member_key in uri: + if in_args['cfg_parent_dict'].get(member_key) is not None: + request = {'path': uri[member_key], + 'method': DELETE} + in_args['requests'].append(request) + elif isinstance(uri, list): + for set_uri_item in uri: + request = {'path': set_uri_item, 'method': DELETE} + else: + request = {'path': uri, 'method': DELETE} + in_args['requests'].append(request) + + def get_delete_route_map_replaced_set_groupings(self, command, cmd_rmap_have, + requests): + '''For the route map specified by the input "command", create requests + to delete any existing route map "set" configuration groupings for which + modified attribute requests are specified''' + + if not command.get('set'): + return + + conf_map_name = command.get('map_name', None) + conf_seq_num = command.get('sequence_num', None) + req_seq_num = str(conf_seq_num) + + cmd_set_top = command['set'] + cfg_set_top = cmd_rmap_have.get('set') + + # If there are no 'set' attributes configured for this route map, + # there's nothing to delete. + if not cfg_set_top: + command.pop('set') + return + + set_delete_req_base = (self.route_map_stmt_base_uri.format(conf_map_name, req_seq_num) + + 'actions/') + bgp_set_delete_req_base = set_delete_req_base + 'openconfig-bgp-policy:bgp-actions/' + + # Obtain the set of "set" keys for which changes have been requested and the set + # of keys currently configured. + cmd_set_keys = cmd_set_top.keys() + cfg_set_keys = cfg_set_top.keys() + + metric_uri = '' + if 'metric' in cfg_set_top: + if cfg_set_top['metric'].get('rtt_action'): + metric_uri = set_delete_req_base + 'metric-action/config' + elif cfg_set_top['metric'].get('value'): + metric_uri = [set_delete_req_base + 'metric-action/config', + bgp_set_delete_req_base + 'config/set-med'] + # Top level keys: Note: Although "metric" is defined as a dictionary, it + # is handled as a "top level" attribute because it can contain + # only one configured member (either an rtt_action or a "value"). + set_top_level_keys = [ + 'as_path_prepend', + 'comm_list_delete', + 'ip_next_hop', + 'local_preference', + 'metric', + 'origin', + 'weight', + ] + + set_uri_attr = { + 'as_path_prepend': bgp_set_delete_req_base + 'set-as-path-prepend', + 'comm_list_delete': bgp_set_delete_req_base + 'set-community-delete', + 'community': bgp_set_delete_req_base + 'set-community', + 'extcommunity': bgp_set_delete_req_base + 'set-ext-community', + 'ip_next_hop': bgp_set_delete_req_base + 'config/set-next-hop', + 'ipv6_next_hop': { + 'global_addr': bgp_set_delete_req_base + 'config/set-ipv6-next-hop-global', + 'prefer_global': bgp_set_delete_req_base + 'config/set-ipv6-next-hop-prefer-global' + }, + 'local_preference': bgp_set_delete_req_base + 'config/set-local-pref', + 'metric': metric_uri, + 'origin': bgp_set_delete_req_base + 'config/set-route-origin', + 'weight': bgp_set_delete_req_base + 'config/set-weight' + } + + # Remove all appropriate "set" configuration for this route map if any of the + # following criteria are met: (See the note below regarding what configuration + # is "appropriate"for deletion.) + # + # 1) Any top level attribute is specified with a value different from its current + # configured value. + # 2) Any top level attribute is specified that is not currently configured. + # 3) The set of top level attributes specified does not include all currently + # configured attributes (regardless of whether the specified values for + # these attributes are the same as the ones courrently configured). + # (Note: Although the IPv6 attribute is defined as a nested dictionary + # to allow for future expansion, it is handled here as a top level + # attrbute because it currently has only one member.) + # + # When deletion has been triggered, an attribute is deleted only if it is + # not present at all in the requested configuration. (If it is present in + # the requested configuration, the "merge" phase of the "replaced" state + # operation will modify it as needed, so it doesn't need to be explicitly + # deleted during the "deletion" phase.) + # + # Handle top level attributes first. If top level attribute deletion is + # triggered, proceed with deletion of dictionaries and lists below the + # top level. + cfg_top_level_key_set = set(cfg_set_keys).intersection(set(set_top_level_keys)) + cmd_top_level_key_set = set(cmd_set_keys).intersection(set(set_top_level_keys)) + cmd_nested_level_key_set = set(cmd_set_keys).difference(set_top_level_keys) + symmetric_diff_set = cmd_top_level_key_set.symmetric_difference(cfg_top_level_key_set) + intersection_diff_set = cmd_top_level_key_set.intersection(cfg_top_level_key_set) + cmd_delete_dict = {} + if (cmd_top_level_key_set and symmetric_diff_set or + (any(keyname for keyname in intersection_diff_set if + cmd_set_top[keyname] != cfg_set_top[keyname]))): + # Deletion has been triggered. First, delete all approriate top level + # attributes + self.delete_replaced_dict_config( + cfg_key_set=cfg_top_level_key_set, + cmd_key_set=cmd_top_level_key_set, + cfg_parent_dict=cfg_set_top, + uri_attr=set_uri_attr, + uri_dict_key='cfg_dict_member_key', + deletion_dict=cmd_delete_dict, + requests=requests) + + # Save nested command "set" items and refresh top level command "set" items. + cmd_set_nested = {} + for nested_key in cmd_nested_level_key_set: + if command['set'].get(nested_key) is not None: + cmd_set_nested[nested_key] = command['set'][nested_key] + + command.pop('set') + if cmd_delete_dict: + command['set'] = cmd_delete_dict + if cmd_set_nested: + if not command.get('set'): + command['set'] = {} + command['set'].update(cmd_set_nested) + if not command.get('set'): + command['set'] = {} + cmd_set_top = command['set'] + + # Proceed with deletion of dictionaries and lists below the top level. + # --------------------------------------------------------------------- + + dict_delete_requests = [] + + # Check for deletion of set "community" lists. Delete the items in + # the currently configured list if it exists. As an optimization, + # avoid deleting list items that will be replaced by the received + # command. + + set_community_delete_attrs = [] + if 'community' not in cfg_set_top: + if command['set'].get('community'): + command['set'].pop('community') + if command['set'] is None: + command.pop('set') + return + else: + set_community_number_deletions = [] + if 'community_number' in cfg_set_top['community']: + + # Delete eligible configured community numbers. + cfg_community_number_set = set(cfg_set_top['community']['community_number']) + cmd_community_number_set = ([]) + if cmd_set_top.get('community') and 'community_number' in cmd_set_top['community']: + cmd_community_number_set = set(cmd_set_top['community']['community_number']) + command['set']['community'].pop('community_number') + + for cfg_community_number in cfg_community_number_set.difference(cmd_community_number_set): + set_community_delete_attrs.append(cfg_community_number) + set_community_number_deletions.append(cfg_community_number) + + if set_community_number_deletions: + # Update the list of deleted community numbers in the "command" dict. + if not cmd_set_top.get('community'): + command['set']['community'] = {} + command['set']['community']['community_number'] = set_community_number_deletions + + set_community_attributes_deletions = [] + if 'community_attributes' in cfg_set_top['community']: + + # Delete eligible configured community attributes. + cfg_community_attributes_set = set(cfg_set_top['community']['community_attributes']) + cmd_community_attributes_set = ([]) + if cmd_set_top.get('community') and 'community_attributes' in cmd_set_top['community']: + cmd_community_attributes_set = set(cmd_set_top['community']['community_attributes']) + command['set']['community'].pop('community_attributes') + + for cfg_community_attribute in cfg_community_attributes_set.difference(cmd_community_attributes_set): + set_community_delete_attrs.append(self.set_community_rest_names[cfg_community_attribute]) + set_community_attributes_deletions.append(cfg_community_attribute) + + if set_community_attributes_deletions: + # Update the list of deleted community attributes in the "command" dict. + if not cmd_set_top.get('community'): + command['set']['community'] = {} + command['set']['community']['community_attributes'] = set_community_attributes_deletions + + if command['set'].get('community') is not None and not command['set']['community']: + command['set'].pop('community') + + # Format and enqueue a request to delete eligible community attributes + if set_community_delete_attrs: + bgp_set_delete_community_uri = bgp_set_delete_req_base + 'set-community' + bgp_set_delete_comm_payload = \ + {'openconfig-bgp-policy:set-community': {}} + bgp_set_delete_comm_payload_contents = \ + bgp_set_delete_comm_payload['openconfig-bgp-policy:set-community'] + bgp_set_delete_comm_payload_contents['config'] = \ + {'method': 'INLINE', 'options': 'REMOVE'} + bgp_set_delete_comm_payload_contents['inline'] = \ + {'config': {'communities': set_community_delete_attrs}} + + request = { + 'path': bgp_set_delete_community_uri, + 'method': PATCH, + 'data': bgp_set_delete_comm_payload + } + dict_delete_requests.append(request) + + # Check for deletion of set "extcommunity" lists. Delete the items in + # the currently configured list if it exists. As an optimization, + # avoid deleting list items that will be replaced by the received + # command. + set_extcommunity_delete_attrs = [] + + if 'extcommunity' not in cfg_set_top: + if command['set'].get('extcommunity'): + command['set'].pop('extcommunity') + if command['set'] is None: + command.pop('set') + return + else: + for extcomm_type in self.set_extcomm_rest_names: + set_extcommunity_delete_attrs_type = [] + if extcomm_type in cfg_set_top['extcommunity']: + # Delete eligible configured extcommunity list items for this + # extcommunity list + cfg_extcommunity_list_set = set(cfg_set_top['extcommunity'][extcomm_type]) + cmd_extcommunity_list_set = ([]) + if cmd_set_top.get('extcommunity') and extcomm_type in cmd_set_top['extcommunity']: + cmd_extcommunity_list_set = set(cmd_set_top['extcommunity'][extcomm_type]) + command['set']['extcommunity'].pop(extcomm_type) + for extcomm_number in cfg_extcommunity_list_set.difference(cmd_extcommunity_list_set): + set_extcommunity_delete_attrs.append( + self.set_extcomm_rest_names[extcomm_type] + + extcomm_number) + set_extcommunity_delete_attrs_type.append(extcomm_number) + + if set_extcommunity_delete_attrs_type: + # Update the list of deleted extcommunity list items of this type + # in the "command" dict. + if not cmd_set_top.get('extcommunity'): + command['set']['extcommunity'] = {} + command['set']['extcommunity'][extcomm_type] = set_extcommunity_delete_attrs_type + + if command['set'].get('extcommunity') is not None and not command['set']['extcommunity']: + command['set'].pop('extcommunity') + + # Format and enqueue a request to delete eligible extcommunity attributes + if set_extcommunity_delete_attrs: + bgp_set_delete_extcomm_uri = bgp_set_delete_req_base + 'set-ext-community' + bgp_set_delete_extcomm_payload = \ + {'openconfig-bgp-policy:set-ext-community': {}} + bgp_set_delete_comm_payload_contents = \ + bgp_set_delete_extcomm_payload[ + 'openconfig-bgp-policy:set-ext-community'] + bgp_set_delete_comm_payload_contents['config'] = \ + {'method': 'INLINE', 'options': 'REMOVE'} + bgp_set_delete_comm_payload_contents['inline'] = \ + {'config': {'communities': set_extcommunity_delete_attrs}} + + request = { + 'path': bgp_set_delete_extcomm_uri, + 'method': PATCH, + 'data': bgp_set_delete_extcomm_payload + } + dict_delete_requests.append(request) + + # Check for deletion of ipv6_next_hop attributes. Delete the attributes + # in the currently configured ipv6_next_hop dict list if they exist. + # As an optimization, avoid deleting attributes that will be replaced + # by the received command. + ipv6_next_hop_deleted_members = {} + if 'ipv6_next_hop' not in cfg_set_top: + if command['set'].get('ipv6_next_hop'): + command['set'].pop('ipv6_next_hop') + if command['set'] is None: + command.pop('set') + return + else: + # Delete eligible configured ipv6_next_hop members. + cfg_ipv6_next_hop_key_set = set(cfg_set_top['ipv6_next_hop'].keys()) + cmd_ipv6_next_hop_key_set = ([]) + if cmd_set_top.get('ipv6_next_hop'): + cmd_ipv6_next_hop_key_set = set(cfg_set_top['ipv6_next_hop'].keys()) + command['set'].pop('ipv6_next_hop') + + set_uri = set_uri_attr['ipv6_next_hop'] + for ipv6_next_hop_key in cfg_ipv6_next_hop_key_set.difference(cmd_ipv6_next_hop_key_set): + ipv6_next_hop_deleted_members[ipv6_next_hop_key] = \ + cfg_set_top['ipv6_next_hop'][ipv6_next_hop_key] + request = {'path': set_uri[ipv6_next_hop_key], 'method': DELETE} + dict_delete_requests.append(request) + + if ipv6_next_hop_deleted_members: + # Update the list of deleted ipv6_next_hop attributes in the "command" dict. + if not cmd_set_top.get('ipv6_next_hop'): + command['set']['ipv6_next_hop'] = {} + command['set']['ipv6_next_hop'] = ipv6_next_hop_deleted_members + + if dict_delete_requests: + requests.extend(dict_delete_requests) + + return + + # If no top level attribute changes were requested, check for changes in + # dictionaries nested below the top level. + # ----------------------------------------------------------------------- + + # Check for replacement of set "community" lists. Delete the items in + # the currently configured list if it exists and any items for that + # list are specified in the received command. + dict_delete_requests = [] + set_community_delete_attrs = [] + if 'community' in cmd_set_top: + if 'community' not in cfg_set_top: + command['set'].pop('community') + if command['set'] is None: + command.pop('set') + return + else: + if 'community_number' in cmd_set_top['community']: + set_community_number_deletions = [] + if 'community_number' in cfg_set_top['community']: + symmetric_diff_set = \ + (set(cmd_set_top['community']['community_number']).symmetric_difference( + set(cfg_set_top['community']['community_number']))) + if symmetric_diff_set: + for community_number in cfg_set_top['community']['community_number']: + if (community_number not in cmd_set_top['community'] + ['community_number']): + set_community_delete_attrs.append(community_number) + set_community_number_deletions.append(community_number) + command['set']['community'].pop('community_number') + if set_community_delete_attrs: + command['set']['community']['community_number'] = \ + set_community_number_deletions + + if 'community_attributes' in cmd_set_top['community']: + set_community_named_attr_deletions = [] + if 'community_attributes' in cfg_set_top['community']: + symmetric_diff_set = \ + (set(cmd_set_top[ + 'community']['community_attributes']).symmetric_difference( + set(cfg_set_top['community']['community_attributes']))) + if symmetric_diff_set: + cfg_set_top_comm_attr = cfg_set_top['community']['community_attributes'] + for community_attr in cfg_set_top_comm_attr: + if (community_attr not in cmd_set_top['community'] + ['community_attributes']): + set_community_delete_attrs.append( + self.set_community_rest_names[community_attr]) + set_community_named_attr_deletions.append(community_attr) + command['set']['community'].pop('community_attributes') + if set_community_named_attr_deletions: + command['set']['community']['community_attributes'] = \ + set_community_named_attr_deletions + if command['set']['community'] is None: + command['set'].pop('community') + + # Format and enqueue a request to delete eligible community attributes + if set_community_delete_attrs: + bgp_set_delete_community_uri = bgp_set_delete_req_base + 'set-community' + bgp_set_delete_comm_payload = \ + {'openconfig-bgp-policy:set-community': {}} + bgp_set_delete_comm_payload_contents = \ + bgp_set_delete_comm_payload['openconfig-bgp-policy:set-community'] + bgp_set_delete_comm_payload_contents['config'] = \ + {'method': 'INLINE', 'options': 'REMOVE'} + bgp_set_delete_comm_payload_contents['inline'] = \ + {'config': {'communities': set_community_delete_attrs}} + + request = { + 'path': bgp_set_delete_community_uri, + 'method': PATCH, + 'data': bgp_set_delete_comm_payload + } + dict_delete_requests.append(request) + + # Check for replacement of set "extcommunity" lists. Delete any items in + # the currently configured list if the corresponding item is not + # specified in the received command. + set_extcommunity_delete_attrs = [] + if 'extcommunity' in cmd_set_top: + if 'extcommunity' not in cfg_set_top: + command['set'].pop('extcommunity') + else: + for extcomm_type in self.set_extcomm_rest_names: + set_extcommunity_delete_attrs_type = [] + if cmd_set_top['extcommunity'].get(extcomm_type): + if extcomm_type in cfg_set_top['extcommunity']: + symmetric_diff_set = \ + (set( + cmd_set_top['extcommunity'][extcomm_type]).symmetric_difference( + set(cfg_set_top['extcommunity'][extcomm_type]))) + if symmetric_diff_set: + # Append eligible entries to the delete list. + for extcomm_number in cfg_set_top['extcommunity'][extcomm_type]: + if (extcomm_number not in + cmd_set_top['extcommunity'][extcomm_type]): + set_extcommunity_delete_attrs.append( + self.set_extcomm_rest_names[extcomm_type] + + extcomm_number) + set_extcommunity_delete_attrs_type.append(extcomm_number) + # Replace the requested extcommunity numbers for this type with the list of + # deleted extcommunity numbers (if any) for this type. + command['set']['extcommunity'].pop(extcomm_type) + if set_extcommunity_delete_attrs_type: + command['set']['extcommunity'][extcomm_type] = \ + set_extcommunity_delete_attrs_type + + if command['set']['extcommunity'] is None: + command['set'].pop('extcommunity') + + # Format and enqueue a request to delete eligible extcommunity attributes + if set_extcommunity_delete_attrs: + bgp_set_delete_extcomm_uri = bgp_set_delete_req_base + 'set-ext-community' + bgp_set_delete_extcomm_payload = \ + {'openconfig-bgp-policy:set-ext-community': {}} + bgp_set_delete_comm_payload_contents = \ + bgp_set_delete_extcomm_payload[ + 'openconfig-bgp-policy:set-ext-community'] + bgp_set_delete_comm_payload_contents['config'] = \ + {'method': 'INLINE', 'options': 'REMOVE'} + bgp_set_delete_comm_payload_contents['inline'] = \ + {'config': {'communities': set_extcommunity_delete_attrs}} + + request = { + 'path': bgp_set_delete_extcomm_uri, + 'method': PATCH, + 'data': bgp_set_delete_extcomm_payload + } + dict_delete_requests.append(request) + + # If the "replaced" command set includes ipv6_next_hop attributes that + # differ from the currently configured attributes, delete + # ipv6_next_hop configuration, if it exists, for any ipv6_next hop + # attributes that are not specified in the received command. + if 'ipv6_next_hop' in cmd_set_top: + ipv6_next_hop_deleted_members = {} + if 'ipv6_next_hop' in cfg_set_top: + symmetric_diff_set = \ + (set(cmd_set_top['ipv6_next_hop'].keys()).symmetric_difference( + set(cfg_set_top['ipv6_next_hop'].keys()))) + intersection_diff_set = \ + (set(cmd_set_top['ipv6_next_hop'].keys()).intersection( + set(cfg_set_top['ipv6_next_hop'].keys()))) + if (symmetric_diff_set or + (any(keyname for keyname in intersection_diff_set if + cmd_set_top['ipv6_next_hop'][keyname] != + cfg_set_top['ipv6_next_hop'][keyname]))): + set_uri = set_uri_attr['ipv6_next_hop'] + for member_key in set_uri: + if (cfg_set_top['ipv6_next_hop'].get(member_key) is not None and + cmd_set_top['ipv6_next_hop'].get(member_key) is None): + ipv6_next_hop_deleted_members[member_key] = \ + cfg_set_top['ipv6_next_hop'][member_key] + request = {'path': set_uri[member_key], 'method': DELETE} + dict_delete_requests.append(request) + command['set'].pop('ipv6_next_hop') + if ipv6_next_hop_deleted_members: + command['set']['ipv6_next_hop'] = ipv6_next_hop_deleted_members + + if dict_delete_requests: + requests.extend(dict_delete_requests) + + def validate_and_normalize_config(self, input_config_list): + '''For each input route map dict in the input_config_list list, + remove empty entries, validate the contents of the dict against the + argspec constraints for route maps, and convert input interface names to + the format required for the currently configured interface naming + mode.''' + updated_config_list = remove_empties_from_list(input_config_list) + validate_config(self._module.argument_spec, {'config': updated_config_list}) + + # - Verify that parameters required for most "states" are present in + # each dict in the input list. + # - Check for interface names in the input configuration and + # perform any needed reformatting of the names. + for route_map in updated_config_list: + + # Verify the presence of a "sequence number" and "action" value + # for all states other than "deleted" + if self._module.params['state'] != 'deleted': + check_required(self._module, ['action', 'sequence_num'], route_map, ['config']) + + # Check for interface names requiring re-formatting. + if not route_map.get('match'): + continue + + if route_map['match'].get('interface'): + intf_name = route_map['match']['interface'] + updated_intf_name = get_normalize_interface_name(intf_name, self._module) + route_map['match']['interface'] = updated_intf_name + + if route_map['match'].get('peer') and route_map['match']['peer'].get('interface'): + intf_name = route_map['match']['peer']['interface'] + updated_intf_name = get_normalize_interface_name(intf_name, self._module) + route_map['match']['peer']['interface'] = updated_intf_name + + return updated_config_list diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/static_routes/static_routes.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/static_routes/static_routes.py index 047357470..c3d62d852 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/static_routes/static_routes.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/static_routes/static_routes.py @@ -28,6 +28,12 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( update_states, get_diff, + get_replaced_config, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff ) network_instance_path = '/data/openconfig-network-instance:network-instances/network-instance' @@ -41,6 +47,61 @@ TEST_KEYS = [ {'next_hops': {'index': ''}}, ] +is_delete_all = False + + +def __derive_static_route_next_hop_config_key_match_op(key_set, command, exist_conf): + bh = command['index'].get('blackhole', None) + itf = command['index'].get('interface', None) + nv = command['index'].get('nexthop_vrf', None) + nh = command['index'].get('next_hop', None) + conf_bh = exist_conf['index'].get('blackhole', None) + conf_itf = exist_conf['index'].get('interface', None) + conf_nv = exist_conf['index'].get('nexthop_vrf', None) + conf_nh = exist_conf['index'].get('next_hop', None) + + if bh == conf_bh and itf == conf_itf and nv == conf_nv and nh == conf_nh: + return True + else: + return False + + +def __derive_static_route_next_hop_config_delete_op(key_set, command, exist_conf): + new_conf = [] + + if is_delete_all: + return True, new_conf + + metric = command.get('metric', None) + tag = command.get('tag', None) + track = command.get('track', None) + + if metric is None and tag is None and track is None: + return True, new_conf + + new_conf = exist_conf + + conf_metric = new_conf.get('metric', None) + conf_tag = new_conf.get('tag', None) + conf_track = new_conf.get('track', None) + + if metric == conf_metric: + new_conf['metric'] = None + if tag == conf_tag: + new_conf['tag'] = None + if track == conf_track: + new_conf['track'] = None + + return True, new_conf + + +TEST_KEYS_formatted_diff = [ + {'config': {'vrf_name': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, + {'static_list': {'prefix': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, + {'next_hops': {'index': '', '__delete_op': __derive_static_route_next_hop_config_delete_op, + '__key_match_op': __derive_static_route_next_hop_config_key_match_op}} +] + class Static_routes(ConfigBase): """ @@ -97,6 +158,21 @@ class Static_routes(ConfigBase): if result['changed']: result['after'] = changed_static_routes_facts + new_config = changed_static_routes_facts + old_config = existing_static_routes_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_static_routes_facts, + TEST_KEYS_formatted_diff) + self.post_process_generated_config(new_config) + result['after(generated)'] = new_config + + if self._module._diff: + self.sort_lists_in_config(new_config) + self.sort_lists_in_config(old_config) + result['diff'] = get_formatted_config_diff(old_config, + new_config, + self._module._verbosity) result['warnings'] = warnings return result @@ -132,10 +208,14 @@ class Static_routes(ConfigBase): if state == 'deleted': commands, requests = self._state_deleted(want, have, diff) elif state == 'merged': - commands, requests = self._state_merged(want, have, diff) + commands, requests = self._state_merged(diff) + elif state == 'overridden': + commands, requests = self._state_overridden(want, have) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have, diff) return commands, requests - def _state_merged(self, want, have, diff): + def _state_merged(self, diff): """ The command generator when state is merged :rtype: A list @@ -143,7 +223,7 @@ class Static_routes(ConfigBase): the current configuration """ commands = diff - requests = self.get_modify_static_routes_requests(commands, have) + requests = self.get_modify_static_routes_requests(commands) if commands and len(requests) > 0: commands = update_states(commands, "merged") @@ -159,6 +239,7 @@ class Static_routes(ConfigBase): :returns: the commands necessary to remove the current configuration of the provided objects """ + global is_delete_all is_delete_all = False # if want is none, then delete ALL if not want: @@ -176,7 +257,73 @@ class Static_routes(ConfigBase): return commands, requests - def get_modify_static_routes_requests(self, commands, have): + def _state_overridden(self, want, have): + """ The command generator when state is overridden + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + global is_delete_all + + commands = [] + requests = [] + self.sort_lists_in_config(want) + self.sort_lists_in_config(have) + + if have and have != want: + is_delete_all = True + del_requests = self.get_delete_static_routes_requests(have, None, is_delete_all) + requests.extend(del_requests) + commands.extend(update_states(have, "deleted")) + have = [] + + if not have and want: + mod_commands = want + mod_requests = self.get_modify_static_routes_requests(mod_commands) + + if len(mod_requests) > 0: + requests.extend(mod_requests) + commands.extend(update_states(mod_commands, "overridden")) + + return commands, requests + + def _state_replaced(self, want, have, diff): + """ The command generator when state is replaced + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + global is_delete_all + + commands = [] + requests = [] + replaced_config = get_replaced_config(want, have, TEST_KEYS) + + mod_commands = [] + if replaced_config: + self.sort_lists_in_config(replaced_config) + self.sort_lists_in_config(have) + is_delete_all = (replaced_config == have) + del_requests = self.get_delete_static_routes_requests(replaced_config, have, is_delete_all) + requests.extend(del_requests) + commands.extend(update_states(replaced_config, "deleted")) + mod_commands = want + else: + mod_commands = diff + + if mod_commands: + mod_requests = self.get_modify_static_routes_requests(mod_commands) + + if len(mod_requests) > 0: + requests.extend(mod_requests) + commands.extend(update_states(mod_commands, "replaced")) + + return commands, requests + + def get_modify_static_routes_requests(self, commands): requests = [] if not commands: @@ -208,7 +355,7 @@ class Static_routes(ConfigBase): idx = self.generate_index(index) if idx: next_hop_cfg['index'] = idx - if blackhole: + if blackhole is not None: next_hop_cfg['blackhole'] = blackhole if nexthop_vrf: next_hop_cfg['network-instance'] = nexthop_vrf @@ -342,3 +489,33 @@ class Static_routes(ConfigBase): request = {'path': url, 'method': DELETE} return request + + def sort_lists_in_config(self, config): + if config: + config.sort(key=self.get_vrf_name) + for cfg in config: + if 'static_list' in cfg and cfg['static_list']: + cfg['static_list'].sort(key=self.get_prefix) + for rt in cfg['static_list']: + if 'next_hops' in rt and rt['next_hops']: + rt['next_hops'].sort(key=lambda x: (x['index'].get('blackhole', None) is not None, + x['index'].get('interface', None) is not None, + x['index'].get('nexthop_vrf', None) is not None, + x['index'].get('next_hop', None) is not None)) + + def get_vrf_name(self, vrf_name): + return vrf_name.get('vrf_name') + + def get_prefix(self, prefix): + return prefix.get('prefix') + + def post_process_generated_config(self, configs): + for conf in configs[:]: + sls = conf.get('static_list', []) + if sls: + for sl in sls[:]: + if not sl.get('next_hops', []): + sls.remove(sl) + + if not conf.get('static_list', []): + configs.remove(conf) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/stp/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/stp/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/stp/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/stp/stp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/stp/stp.py new file mode 100644 index 000000000..031c794ae --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/stp/stp.py @@ -0,0 +1,1404 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_stp class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import re +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + update_states, + get_ranges_in_list, + get_diff, + remove_empties, +) +from ansible.module_utils.connection import ConnectionError + + +PATCH = 'patch' +DELETE = 'delete' +TEST_KEYS = [ + {'interfaces': {'intf_name': ''}}, + {'mst_instances': {'mst_id': ''}}, + {'pvst': {'vlan_id': ''}}, + {'rapid_pvst': {'vlan_id': ''}}, +] +STP_PATH = 'data/openconfig-spanning-tree:stp' +stp_map = { + True: 'EDGE_ENABLE', + False: 'EDGE_DISABLE', + 'mst': 'MSTP', + 'pvst': 'PVST', + 'rapid_pvst': 'RAPID_PVST', + 'point-to-point': 'P2P', + 'shared': 'SHARED', + 'loop': 'LOOP', + 'root': 'ROOT', + 'none': 'NONE' +} + + +class Stp(ConfigBase): + """ + The sonic_stp class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'stp', + ] + + def __init__(self, module): + super(Stp, self).__init__(module) + + def get_stp_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + stp_facts = facts['ansible_network_resources'].get('stp') + if not stp_facts: + return [] + return stp_facts + + def execute_module(self): + """ Execute the module + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = [] + commands = [] + + existing_stp_facts = self.get_stp_facts() + commands, requests = self.set_config(existing_stp_facts) + if commands and len(requests) > 0: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + result['changed'] = True + result['commands'] = commands + + changed_stp_facts = self.get_stp_facts() + + result['before'] = existing_stp_facts + if result['changed']: + result['after'] = changed_stp_facts + + result['warnings'] = warnings + return result + + def set_config(self, existing_stp_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + have = existing_stp_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + state = self._module.params['state'] + diff = get_diff(want, have, TEST_KEYS) + + if state == 'overridden': + commands, requests = self._state_overridden(want, have) + elif state == 'deleted': + commands, requests = self._state_deleted(want, have) + elif state == 'merged': + commands, requests = self._state_merged(diff, have) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have, diff) + return commands, requests + + def _state_replaced(self, want, have, diff): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + mod_commands = [] + replaced_config, requests = self.get_replaced_config(want, have) + + if replaced_config: + commands.extend(update_states(replaced_config, "deleted")) + mod_commands = want + else: + mod_commands = diff + + if mod_commands: + mod_requests = self.get_modify_stp_requests(mod_commands, have) + if len(mod_requests) > 0: + requests.extend(mod_requests) + commands.extend(update_states(mod_commands, "replaced")) + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + del_commands = get_diff(have, want, TEST_KEYS) + self.remove_default_entries(del_commands) + del_commands = remove_empties(del_commands) + + if del_commands: + is_delete_all = True + del_requests = self.get_delete_stp_requests(del_commands, have, is_delete_all) + requests.extend(del_requests) + commands.extend(update_states(have, "deleted")) + have = {} + + if not have and want: + mod_commands = want + mod_requests = self.get_modify_stp_requests(mod_commands, have) + + if len(mod_requests) > 0: + requests.extend(mod_requests) + commands.extend(update_states(mod_commands, "overridden")) + return commands, requests + + def _state_merged(self, diff, have): + """ The command generator when state is merged + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = diff + requests = self.get_modify_stp_requests(commands, have) + commands = remove_empties(commands) + + if commands and len(requests) > 0: + commands = update_states(commands, "merged") + else: + commands = [] + return commands, requests + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + is_delete_all = False + want = remove_empties(want) + + if not want: + commands = deepcopy(have) + is_delete_all = True + else: + commands = deepcopy(want) + + self.remove_default_entries(commands) + commands = remove_empties(commands) + requests = self.get_delete_stp_requests(commands, have, is_delete_all) + + if commands and len(requests) > 0: + commands = update_states(commands, "deleted") + else: + commands = [] + return commands, requests + + def get_modify_stp_requests(self, commands, have): + requests = [] + + if not commands: + return requests + + global_request = self.get_modify_stp_global_request(commands, have) + interfaces_request = self.get_modify_stp_interfaces_request(commands) + mstp_requests = self.get_modify_stp_mstp_request(commands, have) + pvst_request = self.get_modify_stp_pvst_request(commands) + rapid_pvst_request = self.get_modify_stp_rapid_pvst_request(commands) + + if global_request: + requests.append(global_request) + if interfaces_request: + requests.append(interfaces_request) + if mstp_requests: + requests.append(mstp_requests) + if pvst_request: + requests.append(pvst_request) + if rapid_pvst_request: + requests.append(rapid_pvst_request) + + return requests + + def get_modify_stp_global_request(self, commands, have): + request = None + + if not commands: + return request + + stp_global = commands.get('global', None) + if stp_global: + global_dict = {} + config_dict = {} + enabled_protocol = stp_global.get('enabled_protocol', None) + loop_guard = stp_global.get('loop_guard', None) + bpdu_filter = stp_global.get('bpdu_filter', None) + disabled_vlans = stp_global.get('disabled_vlans', None) + root_guard_timeout = stp_global.get('root_guard_timeout', None) + portfast = stp_global.get('portfast', None) + hello_time = stp_global.get('hello_time', None) + max_age = stp_global.get('max_age', None) + fwd_delay = stp_global.get('fwd_delay', None) + bridge_priority = stp_global.get('bridge_priority', None) + + if enabled_protocol: + config_dict['enabled-protocol'] = [stp_map[enabled_protocol]] + if loop_guard is not None: + config_dict['loop-guard'] = loop_guard + if bpdu_filter is not None: + config_dict['bpdu-filter'] = bpdu_filter + if disabled_vlans: + if have: + cfg_stp_global = have.get('global', None) + if cfg_stp_global: + cfg_disabled_vlans = cfg_stp_global.get('disabled_vlans', None) + if cfg_disabled_vlans: + disabled_vlans = self.get_vlans_diff(disabled_vlans, cfg_disabled_vlans) + if not disabled_vlans: + commands['global'].pop('disabled_vlans') + if disabled_vlans: + config_dict['openconfig-spanning-tree-ext:disabled-vlans'] = self.convert_vlans_list(disabled_vlans) + if root_guard_timeout: + config_dict['openconfig-spanning-tree-ext:rootguard-timeout'] = root_guard_timeout + if portfast is not None and enabled_protocol == 'pvst': + config_dict['openconfig-spanning-tree-ext:portfast'] = portfast + elif portfast: + self._module.fail_json(msg='Portfast only configurable for pvst protocol.') + if hello_time: + config_dict['openconfig-spanning-tree-ext:hello-time'] = hello_time + if max_age: + config_dict['openconfig-spanning-tree-ext:max-age'] = max_age + if fwd_delay: + config_dict['openconfig-spanning-tree-ext:forwarding-delay'] = fwd_delay + if bridge_priority: + config_dict['openconfig-spanning-tree-ext:bridge-priority'] = bridge_priority + if config_dict: + global_dict['config'] = config_dict + url = '%s/global' % (STP_PATH) + payload = {'openconfig-spanning-tree:global': global_dict} + request = {'path': url, 'method': PATCH, 'data': payload} + + return request + + def get_modify_stp_interfaces_request(self, commands): + request = None + interfaces = commands.get('interfaces', None) + + if interfaces: + intf_list = [] + for intf in interfaces: + intf_dict = {} + config_dict = {} + intf_name = intf.get('intf_name', None) + edge_port = intf.get('edge_port', None) + link_type = intf.get('link_type', None) + guard = intf.get('guard', None) + bpdu_guard = intf.get('bpdu_guard', None) + bpdu_filter = intf.get('bpdu_filter', None) + portfast = intf.get('portfast', None) + uplink_fast = intf.get('uplink_fast', None) + shutdown = intf.get('shutdown', None) + cost = intf.get('cost', None) + port_priority = intf.get('port_priority', None) + stp_enable = intf.get('stp_enable', None) + + if intf_name: + config_dict['name'] = intf_name + if edge_port is not None: + config_dict['edge-port'] = stp_map[edge_port] + if link_type: + config_dict['link-type'] = stp_map[link_type] + if guard: + config_dict['guard'] = stp_map[guard] + if bpdu_guard is not None: + config_dict['bpdu-guard'] = bpdu_guard + if bpdu_filter is not None: + config_dict['bpdu-filter'] = bpdu_filter + if portfast is not None: + config_dict['openconfig-spanning-tree-ext:portfast'] = portfast + if uplink_fast is not None: + config_dict['openconfig-spanning-tree-ext:uplink-fast'] = uplink_fast + if shutdown is not None: + config_dict['openconfig-spanning-tree-ext:bpdu-guard-port-shutdown'] = shutdown + if cost: + config_dict['openconfig-spanning-tree-ext:cost'] = cost + if port_priority: + config_dict['openconfig-spanning-tree-ext:port-priority'] = port_priority + if stp_enable is not None: + config_dict['openconfig-spanning-tree-ext:spanning-tree-enable'] = stp_enable + if config_dict: + intf_dict['name'] = intf_name + intf_dict['config'] = config_dict + intf_list.append(intf_dict) + if intf_list: + url = '%s/interfaces' % (STP_PATH) + payload = {'openconfig-spanning-tree:interfaces': {'interface': intf_list}} + request = {'path': url, 'method': PATCH, 'data': payload} + + return request + + def get_modify_stp_mstp_request(self, commands, have): + request = None + + if not commands: + return request + + mstp = commands.get('mstp', None) + + if mstp: + mstp_dict = {} + config_dict = {} + mst_name = mstp.get('mst_name', None) + revision = mstp.get('revision', None) + max_hop = mstp.get('max_hop', None) + hello_time = mstp.get('hello_time', None) + max_age = mstp.get('max_age', None) + fwd_delay = mstp.get('fwd_delay', None) + mst_instances = mstp.get('mst_instances', None) + + if mst_name: + config_dict['name'] = mst_name + if revision: + config_dict['revision'] = revision + if max_hop: + config_dict['max-hop'] = max_hop + if hello_time: + config_dict['hello-time'] = hello_time + if max_age: + config_dict['max-age'] = max_age + if fwd_delay: + config_dict['forwarding-delay'] = fwd_delay + if mst_instances: + mst_inst_list = [] + pop_list = [] + for mst in mst_instances: + mst_inst_dict = {} + mst_cfg_dict = {} + mst_index = mst_instances.index(mst) + mst_id = mst.get('mst_id', None) + bridge_priority = mst.get('bridge_priority', None) + interfaces = mst.get('interfaces', None) + vlans = mst.get('vlans', None) + + if mst_id: + mst_cfg_dict['mst-id'] = mst_id + if bridge_priority: + mst_cfg_dict['bridge-priority'] = bridge_priority + if interfaces: + intf_list = self.get_interfaces_list(interfaces) + if intf_list: + mst_inst_dict['interfaces'] = {'interface': intf_list} + if vlans: + if have: + cfg_mstp = have.get('mstp', None) + if cfg_mstp: + cfg_mst_instances = cfg_mstp.get('mst_instances', None) + if cfg_mst_instances: + for cfg_mst in cfg_mst_instances: + cfg_mst_id = cfg_mst.get('mst_id', None) + cfg_vlans = cfg_mst.get('vlans', None) + + if mst_id == cfg_mst_id and cfg_vlans: + vlans = self.get_vlans_diff(vlans, cfg_vlans) + if not vlans: + pop_list.insert(0, mst_index) + if vlans: + mst_cfg_dict['vlan'] = self.convert_vlans_list(vlans) + if mst_cfg_dict: + mst_inst_dict['mst-id'] = mst_id + mst_inst_dict['config'] = mst_cfg_dict + if mst_inst_dict: + mst_inst_list.append(mst_inst_dict) + if pop_list: + for i in pop_list: + commands['mstp']['mst_instances'][i].pop('vlans') + if mst_inst_list: + mstp_dict['mst-instances'] = {'mst-instance': mst_inst_list} + + if config_dict: + mstp_dict['config'] = config_dict + + if mstp_dict: + url = '%s/mstp' % (STP_PATH) + payload = {'openconfig-spanning-tree:mstp': mstp_dict} + request = {'path': url, 'method': PATCH, 'data': payload} + + return request + + def get_modify_stp_pvst_request(self, commands): + request = None + pvst = commands.get('pvst', None) + + if pvst: + vlans_list = self.get_vlans_list(pvst) + if vlans_list: + url = '%s/openconfig-spanning-tree-ext:pvst' % (STP_PATH) + payload = {'openconfig-spanning-tree-ext:pvst': {'vlans': vlans_list}} + request = {'path': url, 'method': PATCH, 'data': payload} + + return request + + def get_modify_stp_rapid_pvst_request(self, commands): + request = None + rapid_pvst = commands.get('rapid_pvst', None) + + if rapid_pvst: + vlans_list = self.get_vlans_list(rapid_pvst) + if vlans_list: + url = '%s/rapid-pvst' % (STP_PATH) + payload = {'openconfig-spanning-tree:rapid-pvst': {'vlan': vlans_list}} + request = {'path': url, 'method': PATCH, 'data': payload} + + return request + + def get_vlans_list(self, data): + vlans_list = [] + + for vlan in data: + vlans_dict = {} + config_dict = {} + vlan_id = vlan.get('vlan_id', None) + hello_time = vlan.get('hello_time', None) + max_age = vlan.get('max_age', None) + fwd_delay = vlan.get('fwd_delay', None) + bridge_priority = vlan.get('bridge_priority', None) + interfaces = vlan.get('interfaces', None) + + if vlan_id: + config_dict['vlan-id'] = vlan_id + if hello_time: + config_dict['hello-time'] = hello_time + if max_age: + config_dict['max-age'] = max_age + if fwd_delay: + config_dict['forwarding-delay'] = fwd_delay + if bridge_priority: + config_dict['bridge-priority'] = bridge_priority + if interfaces: + intf_list = self.get_interfaces_list(interfaces) + if intf_list: + vlans_dict['interfaces'] = {'interface': intf_list} + if config_dict: + vlans_dict['vlan-id'] = vlan_id + vlans_dict['config'] = config_dict + if vlans_dict: + vlans_list.append(vlans_dict) + + return vlans_list + + def get_interfaces_list(self, interfaces): + intf_list = [] + for intf in interfaces: + intf_dict = {} + intf_cfg_dict = {} + intf_name = intf.get('intf_name', None) + cost = intf.get('cost', None) + port_priority = intf.get('port_priority', None) + + if intf_name: + intf_cfg_dict['name'] = intf_name + if cost: + intf_cfg_dict['cost'] = cost + if port_priority: + intf_cfg_dict['port-priority'] = port_priority + if intf_cfg_dict: + intf_dict['name'] = intf_name + intf_dict['config'] = intf_cfg_dict + intf_list.append(intf_dict) + + return intf_list + + def get_vlans_common(self, vlans, cfg_vlans): + """Returns the vlan ranges that are common in the want and have + vlans lists + """ + vlans = self.get_vlan_id_list(vlans) + cfg_vlans = self.get_vlan_id_list(cfg_vlans) + return self.get_vlan_range_list(list(set(vlans).intersection(set(cfg_vlans)))) + + def get_vlans_diff(self, vlans, cfg_vlans): + """Returns the vlan ranges present only in the want vlans list + and not in the have vlans list + """ + vlans = self.get_vlan_id_list(vlans) + cfg_vlans = self.get_vlan_id_list(cfg_vlans) + return self.get_vlan_range_list(list(set(vlans) - set(cfg_vlans))) + + @staticmethod + def get_vlan_id_list(vlans): + """Returns a list of all VLAN IDs specified in a vlans list""" + vlan_id_list = [] + + if vlans: + for vlan_val in vlans: + if '-' in vlan_val or '..' in vlan_val: + start, end = re.split(r'-|\.\.', vlan_val) + vlan_id_list.extend(range(int(start), int(end) + 1)) + else: + # Single VLAN ID + vlan_id_list.append(int(vlan_val)) + + return vlan_id_list + + @staticmethod + def get_vlan_range_list(vlan_id_list): + """Returns the vlans list for a given list of VLAN IDs""" + vlan_range_list = [] + + if vlan_id_list: + vlan_id_list.sort() + for vlan_range in get_ranges_in_list(vlan_id_list): + vlan_range_list.append('-'.join(map(str, (vlan_range[0], vlan_range[-1])[:len(vlan_range)]))) + + return vlan_range_list + + def convert_vlans_list(self, vlans): + converted_vlans = [] + + for vlan in vlans: + if len(vlan) == 1: + converted_vlans.append(int(vlan)) + else: + converted_vlans.append(vlan.replace('-', '..')) + + return converted_vlans + + def get_delete_stp_requests(self, commands, have, is_delete_all): + requests = [] + + if not commands: + return requests + + if is_delete_all: + requests.append(self.get_delete_all_stp_request()) + else: + requests.extend(self.get_delete_stp_mstp_requests(commands, have)) + requests.extend(self.get_delete_stp_pvst_requests(commands, have)) + requests.extend(self.get_delete_stp_rapid_pvst_requests(commands, have)) + requests.extend(self.get_delete_stp_interfaces_requests(commands, have)) + requests.extend(self.get_delete_stp_global_requests(commands, have)) + + return requests + + def get_delete_stp_global_requests(self, commands, have): + requests = [] + + stp_global = commands.get('global', None) + if stp_global: + enabled_protocol = stp_global.get('enabled_protocol', None) + loop_guard = stp_global.get('loop_guard', None) + bpdu_filter = stp_global.get('bpdu_filter', None) + disabled_vlans = stp_global.get('disabled_vlans', None) + root_guard_timeout = stp_global.get('root_guard_timeout', None) + portfast = stp_global.get('portfast', None) + hello_time = stp_global.get('hello_time', None) + max_age = stp_global.get('max_age', None) + fwd_delay = stp_global.get('fwd_delay', None) + bridge_priority = stp_global.get('bridge_priority', None) + + cfg_stp_global = have.get('global', None) + if cfg_stp_global: + cfg_enabled_protocol = cfg_stp_global.get('enabled_protocol', None) + cfg_loop_guard = cfg_stp_global.get('loop_guard', None) + cfg_bpdu_filter = cfg_stp_global.get('bpdu_filter', None) + cfg_disabled_vlans = cfg_stp_global.get('disabled_vlans', None) + cfg_root_guard_timeout = cfg_stp_global.get('root_guard_timeout', None) + cfg_portfast = cfg_stp_global.get('portfast', None) + cfg_hello_time = cfg_stp_global.get('hello_time', None) + cfg_max_age = cfg_stp_global.get('max_age', None) + cfg_fwd_delay = cfg_stp_global.get('fwd_delay', None) + cfg_bridge_priority = cfg_stp_global.get('bridge_priority', None) + + # Default loop_guard is false, don't delete if false + if loop_guard and loop_guard == cfg_loop_guard: + requests.append(self.get_delete_stp_global_attr('loop-guard')) + # Default bpdu_filter is false, don't delete if false + if bpdu_filter and bpdu_filter == cfg_bpdu_filter: + requests.append(self.get_delete_stp_global_attr('bpdu-filter')) + if disabled_vlans and cfg_disabled_vlans: + disabled_vlans_to_delete = self.get_vlans_common(disabled_vlans, cfg_disabled_vlans) + for i, vlan in enumerate(disabled_vlans_to_delete): + if '-' in vlan: + disabled_vlans_to_delete[i] = vlan.replace('-', '..') + if disabled_vlans_to_delete: + encoded_vlans = '%2C'.join(disabled_vlans_to_delete) + attr = 'openconfig-spanning-tree-ext:disabled-vlans=%s' % (encoded_vlans) + requests.append(self.get_delete_stp_global_attr(attr)) + else: + commands['global'].pop('disabled_vlans') + if root_guard_timeout: + if root_guard_timeout == cfg_root_guard_timeout: + requests.append(self.get_delete_stp_global_attr('openconfig-spanning-tree-ext:rootguard-timeout')) + else: + commands['global'].pop('root_guard_timeout') + # Default portfast is false, don't delete if false + if portfast and portfast == cfg_portfast: + requests.append(self.get_delete_stp_global_attr('openconfig-spanning-tree-ext:portfast')) + if hello_time and hello_time == cfg_hello_time: + requests.append(self.get_delete_stp_global_attr('openconfig-spanning-tree-ext:hello-time')) + if max_age and max_age == cfg_max_age: + requests.append(self.get_delete_stp_global_attr('openconfig-spanning-tree-ext:max-age')) + if fwd_delay and fwd_delay == cfg_fwd_delay: + requests.append(self.get_delete_stp_global_attr('openconfig-spanning-tree-ext:forwarding-delay')) + if bridge_priority and bridge_priority == cfg_bridge_priority: + requests.append(self.get_delete_stp_global_attr('openconfig-spanning-tree-ext:bridge-priority')) + if enabled_protocol: + if enabled_protocol == cfg_enabled_protocol: + requests.append(self.get_delete_stp_global_attr('enabled-protocol')) + else: + commands['global'].pop('enabled_protocol') + + return requests + + def get_delete_stp_interfaces_requests(self, commands, have): + requests = [] + + interfaces = commands.get('interfaces', None) + if interfaces: + intf_list = [] + for intf in interfaces: + intf_dict = {} + intf_name = intf.get('intf_name', None) + edge_port = intf.get('edge_port', None) + link_type = intf.get('link_type', None) + guard = intf.get('guard', None) + bpdu_guard = intf.get('bpdu_guard', None) + bpdu_filter = intf.get('bpdu_filter', None) + portfast = intf.get('portfast', None) + uplink_fast = intf.get('uplink_fast', None) + shutdown = intf.get('shutdown', None) + cost = intf.get('cost', None) + port_priority = intf.get('port_priority', None) + stp_enable = intf.get('stp_enable', None) + + cfg_interfaces = have.get('interfaces', None) + if cfg_interfaces: + for cfg_intf in cfg_interfaces: + cfg_intf_name = cfg_intf.get('intf_name', None) + cfg_edge_port = cfg_intf.get('edge_port', None) + cfg_link_type = cfg_intf.get('link_type', None) + cfg_guard = cfg_intf.get('guard', None) + cfg_bpdu_guard = cfg_intf.get('bpdu_guard', None) + cfg_bpdu_filter = cfg_intf.get('bpdu_filter', None) + cfg_portfast = cfg_intf.get('portfast', None) + cfg_uplink_fast = cfg_intf.get('uplink_fast', None) + cfg_shutdown = cfg_intf.get('shutdown', None) + cfg_cost = cfg_intf.get('cost', None) + cfg_port_priority = cfg_intf.get('port_priority', None) + cfg_stp_enable = cfg_intf.get('stp_enable', None) + + if intf_name and intf_name == cfg_intf_name: + # Default edge_port is false, don't delete if false + if edge_port and edge_port == cfg_edge_port: + requests.append(self.get_delete_stp_interface_attr(intf_name, 'edge-port')) + intf_dict.update({'intf_name': intf_name, 'edge_port': edge_port}) + if link_type and link_type == cfg_link_type: + requests.append(self.get_delete_stp_interface_attr(intf_name, 'link-type')) + intf_dict.update({'intf_name': intf_name, 'link_type': link_type}) + if guard and guard == cfg_guard: + requests.append(self.get_delete_stp_interface_attr(intf_name, 'guard')) + intf_dict.update({'intf_name': intf_name, 'guard': guard}) + # Default bpdu_guard is false, don't delete if false + if bpdu_guard and bpdu_guard == cfg_bpdu_guard: + url = '%s/interfaces/interface=%s/config/bpdu-guard' % (STP_PATH, intf_name) + payload = {'openconfig-spanning-tree:bpdu-guard': False} + request = {'path': url, 'method': PATCH, 'data': payload} + requests.append(request) + intf_dict.update({'intf_name': intf_name, 'bpdu_guard': bpdu_guard}) + # Default bpdu_filter is false, don't delete if false + if bpdu_filter and bpdu_filter == cfg_bpdu_filter: + requests.append(self.get_delete_stp_interface_attr(intf_name, 'bpdu-filter')) + intf_dict.update({'intf_name': intf_name, 'bpdu_filter': bpdu_filter}) + # Default portfast is false, don't delete if false + if portfast and portfast == cfg_portfast: + requests.append(self.get_delete_stp_interface_attr(intf_name, 'openconfig-spanning-tree-ext:portfast')) + intf_dict.update({'intf_name': intf_name, 'portfast': portfast}) + # Default uplink_fast is false, don't delete if false + if uplink_fast and uplink_fast == cfg_uplink_fast: + url = '%s/interfaces/interface=%s/config/openconfig-spanning-tree-ext:uplink-fast' % (STP_PATH, intf_name) + payload = {'openconfig-spanning-tree-ext:uplink-fast': False} + request = {'path': url, 'method': PATCH, 'data': payload} + requests.append(request) + intf_dict.update({'intf_name': intf_name, 'uplink_fast': uplink_fast}) + # Default shutdown is false, don't delete if false + if shutdown and shutdown == cfg_shutdown: + url = '%s/interfaces/interface=%s/config/openconfig-spanning-tree-ext:bpdu-guard-port-shutdown' % (STP_PATH, intf_name) + payload = {'openconfig-spanning-tree-ext:bpdu-guard-port-shutdown': False} + request = {'path': url, 'method': PATCH, 'data': payload} + requests.append(request) + intf_dict.update({'intf_name': intf_name, 'shutdown': shutdown}) + if cost and cost == cfg_cost: + requests.append(self.get_delete_stp_interface_attr(intf_name, 'openconfig-spanning-tree-ext:cost')) + intf_dict.update({'intf_name': intf_name, 'cost': cost}) + if port_priority and port_priority == cfg_port_priority: + requests.append(self.get_delete_stp_interface_attr(intf_name, 'openconfig-spanning-tree-ext:port-priority')) + intf_dict.update({'intf_name': intf_name, 'port_priority': port_priority}) + # Default stp_enable is true, don't delete if true + if stp_enable is False and stp_enable == cfg_stp_enable: + url = '%s/interfaces/interface=%s/config/openconfig-spanning-tree-ext:spanning-tree-enable' % (STP_PATH, intf_name) + payload = {'openconfig-spanning-tree-ext:spanning-tree-enable': True} + request = {'path': url, 'method': PATCH, 'data': payload} + requests.append(request) + intf_dict.update({'intf_name': intf_name, 'stp_enable': stp_enable}) + if (edge_port is None and not link_type and not guard and bpdu_guard is None and bpdu_filter is None and portfast is None and + uplink_fast is None and shutdown is None and not cost and not port_priority and stp_enable is None): + requests.append(self.get_delete_stp_interface(intf_name)) + intf_dict.update({'intf_name': intf_name}) + if intf_dict: + intf_list.append(intf_dict) + if intf_list: + commands['interfaces'] = intf_list + else: + commands.pop('interfaces') + + return requests + + def get_delete_stp_mstp_requests(self, commands, have): + requests = [] + + mstp = commands.get('mstp', None) + if mstp: + mst_name = mstp.get('mst_name', None) + revision = mstp.get('revision', None) + max_hop = mstp.get('max_hop', None) + hello_time = mstp.get('hello_time', None) + max_age = mstp.get('max_age', None) + fwd_delay = mstp.get('fwd_delay', None) + mst_instances = mstp.get('mst_instances', None) + + cfg_mstp = have.get('mstp', None) + if cfg_mstp: + cfg_mst_name = cfg_mstp.get('mst_name', None) + cfg_revision = cfg_mstp.get('revision', None) + cfg_max_hop = cfg_mstp.get('max_hop', None) + cfg_hello_time = cfg_mstp.get('hello_time', None) + cfg_max_age = cfg_mstp.get('max_age', None) + cfg_fwd_delay = cfg_mstp.get('fwd_delay', None) + cfg_mst_instances = cfg_mstp.get('mst_instances', None) + + if mst_name: + if mst_name == cfg_mst_name: + requests.append(self.get_delete_stp_mstp_cfg_attr('name')) + else: + commands['mstp'].pop('mst_name') + if revision: + if revision == cfg_revision: + requests.append(self.get_delete_stp_mstp_cfg_attr('revision')) + else: + commands['mstp'].pop('revision') + if max_hop: + if max_hop == cfg_max_hop: + requests.append(self.get_delete_stp_mstp_cfg_attr('max-hop')) + else: + commands['mstp'].pop('max_hop') + if hello_time: + if hello_time == cfg_hello_time: + requests.append(self.get_delete_stp_mstp_cfg_attr('hello-time')) + else: + commands['mstp'].pop('hello_time') + if max_age: + if max_age == cfg_max_age: + requests.append(self.get_delete_stp_mstp_cfg_attr('max-age')) + else: + commands['mstp'].pop('max_age') + if fwd_delay: + if fwd_delay == cfg_fwd_delay: + requests.append(self.get_delete_stp_mstp_cfg_attr('forwarding-delay')) + else: + commands['mstp'].pop('fwd_delay') + if mst_instances: + mst_inst_list = [] + for mst in mst_instances: + mst_inst_dict = {} + mst_id = mst.get('mst_id', None) + bridge_priority = mst.get('bridge_priority', None) + interfaces = mst.get('interfaces', None) + vlans = mst.get('vlans', None) + if cfg_mst_instances: + for cfg_mst in cfg_mst_instances: + cfg_mst_id = cfg_mst.get('mst_id', None) + cfg_bridge_priority = cfg_mst.get('bridge_priority', None) + cfg_interfaces = cfg_mst.get('interfaces', None) + cfg_vlans = cfg_mst.get('vlans', None) + + if mst_id == cfg_mst_id: + if bridge_priority and bridge_priority == cfg_bridge_priority: + requests.append(self.get_delete_mst_inst_cfg_attr(mst_id, 'bridge-priority')) + mst_inst_dict.update({'mst_id': mst_id, 'bridge_priority': bridge_priority}) + if interfaces: + intf_list = [] + for intf in interfaces: + intf_dict = {} + intf_name = intf.get('intf_name', None) + cost = intf.get('cost', None) + port_priority = intf.get('port_priority', None) + + if cfg_interfaces: + for cfg_intf in cfg_interfaces: + cfg_intf_name = cfg_intf.get('intf_name', None) + cfg_cost = cfg_intf.get('cost', None) + cfg_port_priority = cfg_intf.get('port_priority', None) + + if intf_name == cfg_intf_name: + if cost and cost == cfg_cost: + requests.append(self.get_delete_mst_intf_cfg_attr(mst_id, intf_name, 'cost')) + intf_dict.update({'intf_name': intf_name, 'cost': cost}) + if port_priority and port_priority == cfg_port_priority: + requests.append(self.get_delete_mst_intf_cfg_attr(mst_id, intf_name, 'port-priority')) + intf_dict.update({'intf_name': intf_name, 'port_priority': port_priority}) + if not cost and not port_priority: + requests.append(self.get_delete_mst_intf(mst_id, intf_name)) + intf_dict.update({'intf_name': intf_name}) + if intf_dict: + intf_list.append(intf_dict) + if intf_list: + mst_inst_dict.update({'mst_id': mst_id, 'interfaces': intf_list}) + + if vlans and cfg_vlans: + vlans_to_delete = self.get_vlans_common(vlans, cfg_vlans) + cmd_vlans = deepcopy(vlans_to_delete) + for i, vlan in enumerate(vlans_to_delete): + if '-' in vlan: + vlans_to_delete[i] = vlan.replace('-', '..') + if vlans_to_delete: + encoded_vlans = '%2C'.join(vlans_to_delete) + attr = 'vlan=%s' % (encoded_vlans) + requests.append(self.get_delete_mst_inst_cfg_attr(mst_id, attr)) + mst_inst_dict.update({'mst_id': mst_id, 'vlans': cmd_vlans}) + if not bridge_priority and not vlans and not interfaces: + requests.append(self.get_delete_mst_inst(mst_id)) + mst_inst_dict.update({'mst_id': mst_id}) + if mst_inst_dict: + mst_inst_list.append(mst_inst_dict) + if mst_inst_list: + commands['mstp']['mst_instances'] = mst_inst_list + else: + commands['mstp'].pop('mst_instances') + if not commands['mstp']: + commands.pop('mstp') + + return requests + + def get_delete_stp_pvst_requests(self, commands, have): + requests = [] + + pvst = commands.get('pvst', None) + if pvst: + vlans_list = [] + for vlan in pvst: + vlans_dict = {} + vlan_id = vlan.get('vlan_id', None) + hello_time = vlan.get('hello_time', None) + max_age = vlan.get('max_age', None) + fwd_delay = vlan.get('fwd_delay', None) + bridge_priority = vlan.get('bridge_priority', None) + interfaces = vlan.get('interfaces', []) + + cfg_pvst = have.get('pvst', None) + if cfg_pvst: + for cfg_vlan in cfg_pvst: + cfg_vlan_id = cfg_vlan.get('vlan_id', None) + cfg_hello_time = cfg_vlan.get('hello_time', None) + cfg_max_age = cfg_vlan.get('max_age', None) + cfg_fwd_delay = cfg_vlan.get('fwd_delay', None) + cfg_bridge_priority = cfg_vlan.get('bridge_priority', None) + cfg_interfaces = cfg_vlan.get('interfaces', []) + + if vlan_id == cfg_vlan_id: + if hello_time and hello_time == cfg_hello_time: + requests.append(self.get_delete_pvst_vlan_cfg_attr(vlan_id, 'hello-time')) + vlans_dict.update({'vlan_id': vlan_id, 'hello_time': hello_time}) + if max_age and max_age == cfg_max_age: + requests.append(self.get_delete_pvst_vlan_cfg_attr(vlan_id, 'max-age')) + vlans_dict.update({'vlan_id': vlan_id, 'max_age': max_age}) + if fwd_delay and fwd_delay == cfg_fwd_delay: + requests.append(self.get_delete_pvst_vlan_cfg_attr(vlan_id, 'forwarding-delay')) + vlans_dict.update({'vlan_id': vlan_id, 'fwd_delay': fwd_delay}) + if bridge_priority and bridge_priority == cfg_bridge_priority: + requests.append(self.get_delete_pvst_vlan_cfg_attr(vlan_id, 'bridge-priority')) + vlans_dict.update({'vlan_id': vlan_id, 'bridge_priority': bridge_priority}) + + if interfaces: + intf_list = [] + for intf in interfaces: + intf_dict = {} + intf_name = intf.get('intf_name', None) + cost = intf.get('cost', None) + port_priority = intf.get('port_priority', None) + + if cfg_interfaces: + for cfg_intf in cfg_interfaces: + cfg_intf_name = cfg_intf.get('intf_name', None) + cfg_cost = cfg_intf.get('cost', None) + cfg_port_priority = cfg_intf.get('port_priority', None) + + if intf_name == cfg_intf_name: + if cost and cost == cfg_cost: + requests.append(self.get_delete_pvst_intf_cfg_attr(vlan_id, intf_name, 'cost')) + intf_dict.update({'intf_name': intf_name, 'cost': cost}) + if port_priority and port_priority == cfg_port_priority: + requests.append(self.get_delete_pvst_intf_cfg_attr(vlan_id, intf_name, 'port-priority')) + intf_dict.update({'intf_name': intf_name, 'port_priority': port_priority}) + if not cost and not port_priority: + requests.append(self.get_delete_pvst_intf(vlan_id, intf_name)) + intf_dict.update({'intf_name': intf_name}) + if intf_dict: + intf_list.append(intf_dict) + if intf_list: + vlans_dict.update({'vlan_id': vlan_id, 'interfaces': intf_list}) + if vlans_dict: + vlans_list.append(vlans_dict) + if vlans_list: + commands['pvst'] = vlans_list + else: + commands.pop('pvst') + + return requests + + def get_delete_stp_rapid_pvst_requests(self, commands, have): + requests = [] + + rapid_pvst = commands.get('rapid_pvst', None) + if rapid_pvst: + vlans_list = [] + for vlan in rapid_pvst: + vlans_dict = {} + vlan_id = vlan.get('vlan_id', None) + hello_time = vlan.get('hello_time', None) + max_age = vlan.get('max_age', None) + fwd_delay = vlan.get('fwd_delay', None) + bridge_priority = vlan.get('bridge_priority', None) + interfaces = vlan.get('interfaces', []) + + cfg_rapid_pvst = have.get('rapid_pvst', None) + if cfg_rapid_pvst: + for cfg_vlan in cfg_rapid_pvst: + cfg_vlan_id = cfg_vlan.get('vlan_id', None) + cfg_hello_time = cfg_vlan.get('hello_time', None) + cfg_max_age = cfg_vlan.get('max_age', None) + cfg_fwd_delay = cfg_vlan.get('fwd_delay', None) + cfg_bridge_priority = cfg_vlan.get('bridge_priority', None) + cfg_interfaces = cfg_vlan.get('interfaces', []) + + if vlan_id == cfg_vlan_id: + if hello_time and hello_time == cfg_hello_time: + requests.append(self.get_delete_rapid_pvst_vlan_cfg_attr(vlan_id, 'hello-time')) + vlans_dict.update({'vlan_id': vlan_id, 'hello_time': hello_time}) + if max_age and max_age == cfg_max_age: + requests.append(self.get_delete_rapid_pvst_vlan_cfg_attr(vlan_id, 'max-age')) + vlans_dict.update({'vlan_id': vlan_id, 'max_age': max_age}) + if fwd_delay and fwd_delay == cfg_fwd_delay: + requests.append(self.get_delete_rapid_pvst_vlan_cfg_attr(vlan_id, 'forwarding-delay')) + vlans_dict.update({'vlan_id': vlan_id, 'fwd_delay': fwd_delay}) + if bridge_priority and bridge_priority == cfg_bridge_priority: + requests.append(self.get_delete_rapid_pvst_vlan_cfg_attr(vlan_id, 'bridge-priority')) + vlans_dict.update({'vlan_id': vlan_id, 'bridge_priority': bridge_priority}) + + if interfaces: + intf_list = [] + for intf in interfaces: + intf_dict = {} + intf_name = intf.get('intf_name', None) + cost = intf.get('cost', None) + port_priority = intf.get('port_priority', None) + + if cfg_interfaces: + for cfg_intf in cfg_interfaces: + cfg_intf_name = cfg_intf.get('intf_name', None) + cfg_cost = cfg_intf.get('cost', None) + cfg_port_priority = cfg_intf.get('port_priority', None) + + if intf_name == cfg_intf_name: + if cost and cost == cfg_cost: + requests.append(self.get_delete_rapid_pvst_intf_cfg_attr(vlan_id, intf_name, 'cost')) + intf_dict.update({'intf_name': intf_name, 'cost': cost}) + if port_priority and port_priority == cfg_port_priority: + requests.append(self.get_delete_rapid_pvst_intf_cfg_attr(vlan_id, intf_name, 'port-priority')) + intf_dict.update({'intf_name': intf_name, 'port_priority': port_priority}) + if not cost and not port_priority: + requests.append(self.get_delete_rapid_pvst_intf(vlan_id, intf_name)) + intf_dict.update({'intf_name': intf_name}) + if intf_dict: + intf_list.append(intf_dict) + if intf_list: + vlans_dict.update({'vlan_id': vlan_id, 'interfaces': intf_list}) + if vlans_dict: + vlans_list.append(vlans_dict) + if vlans_list: + commands['rapid_pvst'] = vlans_list + else: + commands.pop('rapid_pvst') + + return requests + + def get_delete_all_stp_request(self): + request = {'path': STP_PATH, 'method': DELETE} + + return request + + def get_delete_stp_global_attr(self, attr): + url = '%s/global/config/%s' % (STP_PATH, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_stp_interface(self, intf_name): + url = '%s/interfaces/interface=%s' % (STP_PATH, intf_name) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_stp_interface_attr(self, intf_name, attr): + url = '%s/interfaces/interface=%s/config/%s' % (STP_PATH, intf_name, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_stp_mstp_cfg_attr(self, attr): + url = '%s/mstp/config/%s' % (STP_PATH, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_mst_inst(self, mst_id): + url = '%s/mstp/mst-instances/mst-instance=%s' % (STP_PATH, mst_id) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_mst_inst_cfg_attr(self, mst_id, attr): + url = '%s/mstp/mst-instances/mst-instance=%s/config/%s' % (STP_PATH, mst_id, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_mst_intf(self, mst_id, intf_name): + url = '%s/mstp/mst-instances/mst-instance=%s/interfaces/interface=%s' % (STP_PATH, mst_id, intf_name) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_mst_intf_cfg_attr(self, mst_id, intf_name, attr): + url = '%s/mstp/mst-instances/mst-instance=%s/interfaces/interface=%s/config/%s' % (STP_PATH, mst_id, intf_name, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_pvst_vlan_cfg_attr(self, vlan_id, attr): + url = '%s/openconfig-spanning-tree-ext:pvst/vlans=%s/config/%s' % (STP_PATH, vlan_id, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_pvst_intf(self, vlan_id, intf_name): + url = '%s/openconfig-spanning-tree-ext:pvst/vlans=%s/interfaces/interface=%s' % (STP_PATH, vlan_id, intf_name) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_pvst_intf_cfg_attr(self, vlan_id, intf_name, attr): + url = '%s/openconfig-spanning-tree-ext:pvst/vlans=%s/interfaces/interface=%s/config/%s' % (STP_PATH, vlan_id, intf_name, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_rapid_pvst_vlan_cfg_attr(self, vlan_id, attr): + url = '%s/rapid-pvst/vlan=%s/config/%s' % (STP_PATH, vlan_id, attr) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_rapid_pvst_intf(self, vlan_id, intf_name): + url = '%s/rapid-pvst/vlan=%s/interfaces/interface=%s' % (STP_PATH, vlan_id, intf_name) + request = {'path': url, 'method': DELETE} + + return request + + def get_delete_rapid_pvst_intf_cfg_attr(self, vlan_id, intf_name, attr): + url = '%s/rapid-pvst/vlan=%s/interfaces/interface=%s/config/%s' % (STP_PATH, vlan_id, intf_name, attr) + request = {'path': url, 'method': DELETE} + + return request + + def remove_default_entries(self, data): + stp_global = data.get('global', None) + interfaces = data.get('interfaces', None) + + if stp_global: + loop_guard = stp_global.get('loop_guard', None) + bpdu_filter = stp_global.get('bpdu_filter', None) + portfast = stp_global.get('portfast', None) + hello_time = stp_global.get('hello_time', None) + max_age = stp_global.get('max_age', None) + fwd_delay = stp_global.get('fwd_delay', None) + bridge_priority = stp_global.get('bridge_priority', None) + + if loop_guard is False: + stp_global.pop('loop_guard') + if bpdu_filter is False: + stp_global.pop('bpdu_filter') + if portfast is False: + stp_global.pop('portfast') + if hello_time == 2: + stp_global.pop('hello_time') + if max_age == 20: + stp_global.pop('max_age') + if fwd_delay == 15: + stp_global.pop('fwd_delay') + if bridge_priority == 32768: + stp_global.pop('bridge_priority') + if not stp_global: + data.pop('global') + + if interfaces: + for intf in interfaces: + edge_port = intf.get('edge_port', None) + bpdu_guard = intf.get('bpdu_guard', None) + bpdu_filter = intf.get('bpdu_filter', None) + portfast = intf.get('portfast', None) + uplink_fast = intf.get('uplink_fast', None) + shutdown = intf.get('shutdown', None) + stp_enable = intf.get('stp_enable', None) + + if edge_port is False: + intf.pop('edge_port') + if bpdu_guard is False: + intf.pop('bpdu_guard') + if bpdu_filter is False: + intf.pop('bpdu_filter') + if portfast is False: + intf.pop('portfast') + if uplink_fast is False: + intf.pop('uplink_fast') + if shutdown is False: + intf.pop('shutdown') + if stp_enable: + intf.pop('stp_enable') + + def get_replaced_config(self, want, have): + config_dict = {} + requests = [] + stp_global = want.get('global', None) + new_have = self.remove_default_entries(deepcopy(have)) + new_have = remove_empties(new_have) + cfg_stp_global = new_have.get('global', None) + + if stp_global and cfg_stp_global and stp_global != cfg_stp_global: + requests.append(self.get_delete_all_stp_request()) + return have, requests + + interfaces = want.get('interfaces', None) + cfg_interfaces = have.get('interfaces', None) + if interfaces and cfg_interfaces: + intf_list = [] + for intf in interfaces: + intf_name = intf.get('intf_name', None) + for cfg_intf in cfg_interfaces: + cfg_intf_name = cfg_intf.get('intf_name', None) + if intf_name == cfg_intf_name: + if intf != cfg_intf: + intf_list.append(cfg_intf) + requests.append(self.get_delete_stp_interface(cfg_intf_name)) + if intf_list: + config_dict['interfaces'] = intf_list + + mstp = want.get('mstp', None) + cfg_mstp = have.get('mstp', None) + if mstp and cfg_mstp: + mst_name = mstp.get('mst_name', None) + revision = mstp.get('revision', None) + max_hop = mstp.get('max_hop', None) + hello_time = mstp.get('hello_time', None) + max_age = mstp.get('max_age', None) + fwd_delay = mstp.get('fwd_delay', None) + mst_instances = mstp.get('mst_instances', None) + + cfg_mst_name = cfg_mstp.get('mst_name', None) + cfg_revision = cfg_mstp.get('revision', None) + cfg_max_hop = cfg_mstp.get('max_hop', None) + cfg_hello_time = cfg_mstp.get('hello_time', None) + cfg_max_age = cfg_mstp.get('max_age', None) + cfg_fwd_delay = cfg_mstp.get('fwd_delay', None) + cfg_mst_instances = cfg_mstp.get('mst_instances', None) + + if ((mst_name and mst_name != cfg_mst_name) or (revision and revision != cfg_revision) or (max_hop and max_hop != cfg_max_hop) or + (hello_time and hello_time != cfg_hello_time) or (max_age and max_age != cfg_max_age) or + (fwd_delay and fwd_delay != cfg_fwd_delay)): + config_dict['mstp'] = cfg_mstp + requests.append({'path': '%s/mstp/config' % STP_PATH, 'method': DELETE}) + requests.append({'path': '%s/mstp/mst-instances' % STP_PATH, 'method': DELETE}) + else: + if mst_instances and cfg_mst_instances: + mst_inst_list = [] + for mst in mst_instances: + mst_id = mst.get('mst_id', None) + bridge_priority = mst.get('bridge_priority', None) + vlans = mst.get('vlans', None) + if vlans: + vlans.sort() + interfaces = mst.get('interfaces', None) + for cfg_mst in cfg_mst_instances: + cfg_mst_id = cfg_mst.get('mst_id', None) + cfg_bridge_priority = cfg_mst.get('bridge_priority', None) + cfg_vlans = cfg_mst.get('vlans', None) + if cfg_vlans: + cfg_vlans.sort() + cfg_interfaces = cfg_mst.get('interfaces', None) + + if mst_id == cfg_mst_id: + if ((bridge_priority and bridge_priority != cfg_bridge_priority) or (vlans and vlans != cfg_vlans)): + mst_inst_list.append(cfg_mst) + requests.append(self.get_delete_mst_inst(cfg_mst_id)) + else: + if interfaces and cfg_interfaces: + intf_list = [] + for intf in interfaces: + intf_name = intf.get('intf_name', None) + for cfg_intf in cfg_interfaces: + cfg_intf_name = cfg_intf.get('intf_name', None) + if intf_name == cfg_intf_name: + if intf != cfg_intf: + intf_list.append(cfg_intf) + mst_inst_list.append({'mst_id': cfg_mst_id, 'interfaces': intf_list}) + requests.append(self.get_delete_mst_intf(cfg_mst_id, cfg_intf_name)) + if mst_inst_list: + config_dict['mstp'] = {'mst_instances': mst_inst_list} + + pvst = want.get('pvst', None) + cfg_pvst = have.get('pvst', None) + if pvst and cfg_pvst: + vlans_list, vlans_requests = self.get_replaced_vlans_list(pvst, cfg_pvst, 'pvst') + if vlans_list: + config_dict['pvst'] = vlans_list + requests.extend(vlans_requests) + + rapid_pvst = want.get('rapid_pvst', None) + cfg_rapid_pvst = have.get('rapid_pvst', None) + if rapid_pvst and cfg_rapid_pvst: + vlans_list, vlans_requests = self.get_replaced_vlans_list(rapid_pvst, cfg_rapid_pvst, 'rapid_pvst') + if vlans_list: + config_dict['rapid_pvst'] = vlans_list + requests.extend(vlans_requests) + + return config_dict, requests + + def get_replaced_vlans_list(self, want_data, have_data, protocol): + vlans_list = [] + requests = [] + for vlan in want_data: + vlan_id = vlan.get('vlan_id', None) + hello_time = vlan.get('hello_time', None) + max_age = vlan.get('max_age', None) + fwd_delay = vlan.get('fwd_delay', None) + bridge_priority = vlan.get('bridge_priority', None) + interfaces = vlan.get('interfaces', None) + + for cfg_vlan in have_data: + cfg_vlan_id = cfg_vlan.get('vlan_id', None) + cfg_hello_time = cfg_vlan.get('hello_time', None) + cfg_max_age = cfg_vlan.get('max_age', None) + cfg_fwd_delay = cfg_vlan.get('fwd_delay', None) + cfg_bridge_priority = cfg_vlan.get('bridge_priority', None) + cfg_interfaces = cfg_vlan.get('interfaces', None) + + if vlan_id == cfg_vlan_id: + if ((hello_time and hello_time != cfg_hello_time) or (max_age and max_age != cfg_max_age) or + (fwd_delay and fwd_delay != cfg_fwd_delay) or (bridge_priority and bridge_priority != cfg_bridge_priority)): + vlans_list.append(cfg_vlan) + + if cfg_hello_time: + if protocol == 'pvst': + requests.append(self.get_delete_pvst_vlan_cfg_attr(cfg_vlan_id, 'hello-time')) + elif protocol == 'rapid_pvst': + requests.append(self.get_delete_rapid_pvst_vlan_cfg_attr(cfg_vlan_id, 'hello-time')) + if cfg_max_age: + if protocol == 'pvst': + requests.append(self.get_delete_pvst_vlan_cfg_attr(cfg_vlan_id, 'max-age')) + elif protocol == 'rapid_pvst': + requests.append(self.get_delete_rapid_pvst_vlan_cfg_attr(cfg_vlan_id, 'max-age')) + if cfg_fwd_delay: + if protocol == 'pvst': + requests.append(self.get_delete_pvst_vlan_cfg_attr(cfg_vlan_id, 'forwarding-delay')) + elif protocol == 'rapid_pvst': + requests.append(self.get_delete_rapid_pvst_vlan_cfg_attr(cfg_vlan_id, 'forwarding-delay')) + if cfg_bridge_priority: + if protocol == 'pvst': + requests.append(self.get_delete_pvst_vlan_cfg_attr(cfg_vlan_id, 'bridge-priority')) + elif protocol == 'rapid_pvst': + requests.append(self.get_delete_rapid_pvst_vlan_cfg_attr(cfg_vlan_id, 'bridge-priority')) + if cfg_interfaces: + for cfg_intf in cfg_interfaces: + cfg_intf_name = cfg_intf.get('intf_name', None) + if protocol == 'pvst': + requests.append(self.get_delete_pvst_intf(cfg_vlan_id, cfg_intf_name)) + elif protocol == 'rapid_pvst': + requests.append(self.get_delete_rapid_pvst_intf(cfg_vlan_id, cfg_intf_name)) + + else: + if interfaces and cfg_interfaces: + intf_list = [] + for intf in interfaces: + intf_name = intf.get('intf_name', None) + for cfg_intf in cfg_interfaces: + cfg_intf_name = cfg_intf.get('intf_name', None) + if intf_name == cfg_intf_name: + if intf != cfg_intf: + intf_list.append(cfg_intf) + vlans_list.append({'vlan_id': cfg_vlan_id, 'interfaces': intf_list}) + if protocol == 'pvst': + requests.append(self.get_delete_pvst_intf(cfg_vlan_id, cfg_intf_name)) + elif protocol == 'rapid_pvst': + requests.append(self.get_delete_rapid_pvst_intf(cfg_vlan_id, cfg_intf_name)) + + return vlans_list, requests diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/system/system.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/system/system.py index 21d575a1f..50225718b 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/system/system.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/system/system.py @@ -26,17 +26,45 @@ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common i from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( update_states, + send_requests, get_diff, ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( to_request, edit_config ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + get_new_config, + get_formatted_config_diff +) PATCH = 'patch' DELETE = 'delete' +def __derive_system_config_delete_op(key_set, command, exist_conf): + new_conf = exist_conf + + if 'hostname' in command: + new_conf['hostname'] = 'sonic' + if 'interface_naming' in command: + new_conf['interface_naming'] = 'native' + if 'anycast_address' in command and 'anycast_address' in new_conf: + if 'ipv4' in command['anycast_address']: + new_conf['anycast_address']['ipv4'] = True + if 'ipv6' in command['anycast_address']: + new_conf['anycast_address']['ipv6'] = True + if 'mac_address' in command['anycast_address']: + new_conf['anycast_address']['mac_address'] = None + + return True, new_conf + + +TEST_KEYS_formatted_diff = [ + {'__default_ops': {'__delete_op': __derive_system_config_delete_op}}, +] + + class System(ConfigBase): """ The sonic_system class @@ -90,6 +118,17 @@ class System(ConfigBase): if result['changed']: result['after'] = changed_system_facts + new_config = changed_system_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_system_facts, + TEST_KEYS_formatted_diff) + result['after(generated)'] = new_config + + if self._module._diff: + result['diff'] = get_formatted_config_diff(existing_system_facts, + new_config, + self._module._verbosity) result['warnings'] = warnings return result @@ -127,6 +166,11 @@ class System(ConfigBase): elif state == 'merged': diff = get_diff(want, have) commands = self._state_merged(want, have, diff) + elif state == 'overridden': + commands = self._state_overridden(want, have) + elif state == 'replaced': + commands = self._state_replaced(want, have) + return commands def _state_merged(self, want, have, diff): @@ -142,6 +186,7 @@ class System(ConfigBase): requests = self.get_create_system_request(want, diff) if len(requests) > 0: commands = update_states(diff, "merged") + return commands, requests def _state_deleted(self, want, have): @@ -167,6 +212,70 @@ class System(ConfigBase): requests = self.get_delete_all_system_request(diff_want) if len(requests) > 0: commands = update_states(diff_want, "deleted") + + return commands, requests + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + new_want = self.patch_want_with_default(want, ac_address_only=True) + replaced_config = self.get_replaced_config(have, new_want) + if replaced_config: + requests = self.get_delete_all_system_request(replaced_config) + send_requests(self._module, requests) + commands = new_want + else: + diff = get_diff(new_want, have) + commands = diff + if not commands: + commands = [] + + requests = [] + + if commands: + requests = self.get_create_system_request(have, commands) + + if len(requests) > 0: + commands = update_states(commands, "replaced") + else: + commands = [] + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + new_want = self.patch_want_with_default(want) + if have and have != new_want: + requests = self.get_delete_all_system_request(have) + send_requests(self._module, requests) + have = [] + + commands = [] + requests = [] + + if not have and new_want: + commands = new_want + requests = self.get_create_system_request(have, commands) + if len(requests) > 0: + commands = update_states(commands, "overridden") + else: + commands = [] + return commands, requests def get_create_system_request(self, want, commands): @@ -190,8 +299,9 @@ class System(ConfigBase): return requests def build_create_hostname_payload(self, commands): - payload = {"openconfig-system:config": {}} + payload = {} if "hostname" in commands and commands["hostname"]: + payload = {"openconfig-system:config": {}} payload['openconfig-system:config'].update({"hostname": commands["hostname"]}) return payload @@ -221,6 +331,63 @@ class System(ConfigBase): payload["sonic-sag:SAG_GLOBAL_LIST"].append(temp) return payload + def patch_want_with_default(self, want, ac_address_only=False): + new_want = {} + if want is None: + if ac_address_only: + new_want = {'anycast_address': {'ipv4': True, 'ipv6': True, 'mac_address': None}} + else: + new_want = {'hostname': 'sonic', 'interface_naming': 'native', + 'anycast_address': {'ipv4': True, 'ipv6': True, 'mac_address': None}} + else: + new_want = want.copy() + new_anycast = {} + anycast = want.get('anycast_address', None) + if not anycast: + new_anycast = {'ipv4': True, 'ipv6': True, 'mac_address': None} + else: + new_anycast = anycast.copy() + ipv4 = anycast.get("ipv4", None) + if ipv4 is None: + new_anycast["ipv4"] = True + ipv6 = anycast.get("ipv6", None) + if ipv6 is None: + new_anycast["ipv6"] = True + mac = anycast.get("mac_address", None) + if mac is None: + new_anycast["mac_address"] = None + new_want["anycast_address"] = new_anycast + + if not ac_address_only: + hostname = want.get('hostname', None) + if hostname is None: + new_want["hostname"] = 'sonic' + intf_name = want.get('interface_naming', None) + if intf_name is None: + new_want["interface_naming"] = 'native' + return new_want + + def get_replaced_config(self, have, want): + + replaced_config = dict() + + h_hostname = have.get('hostname', None) + w_hostname = want.get('hostname', None) + if (h_hostname != w_hostname) and w_hostname: + replaced_config = have.copy() + return replaced_config + h_intf_name = have.get('interface_naming', None) + w_intf_name = want.get('interface_naming', None) + if (h_intf_name != w_intf_name) and w_intf_name: + replaced_config = have.copy() + return replaced_config + h_ac_addr = have.get('anycast_address', None) + w_ac_addr = want.get('anycast_address', None) + if (h_ac_addr != w_ac_addr) and w_ac_addr: + replaced_config['anycast_address'] = h_ac_addr + return replaced_config + return replaced_config + def remove_default_entries(self, data): new_data = {} if not data: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/tacacs_server/tacacs_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/tacacs_server/tacacs_server.py index 498fcbe28..e376fc82a 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/tacacs_server/tacacs_server.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/tacacs_server/tacacs_server.py @@ -27,14 +27,25 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( update_states, get_diff, + get_replaced_config, get_normalize_interface_name, ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + __DELETE_LEAFS_OR_CONFIG_IF_NO_NON_KEY_LEAF, + get_new_config, + get_formatted_config_diff +) PATCH = 'patch' DELETE = 'delete' TEST_KEYS = [ {'host': {'name': ''}}, ] +TEST_KEYS_formatted_diff = [ + {'__default_ops': {'__delete_op': __DELETE_LEAFS_OR_CONFIG_IF_NO_NON_KEY_LEAF}}, + {'host': {'name': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, +] class Tacacs_server(ConfigBase): @@ -91,6 +102,17 @@ class Tacacs_server(ConfigBase): if result['changed']: result['after'] = changed_tacacs_server_facts + new_config = changed_tacacs_server_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_tacacs_server_facts, + TEST_KEYS_formatted_diff) + result['after(generated)'] = new_config + + if self._module._diff: + result['diff'] = get_formatted_config_diff(existing_tacacs_server_facts, + new_config, + self._module._verbosity) result['warnings'] = warnings return result @@ -180,6 +202,67 @@ class Tacacs_server(ConfigBase): return commands, requests + def _state_replaced(self, want, have, diff): + """ The command generator when state is replaced + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + replaced_config = get_replaced_config(want, have, TEST_KEYS) + + add_commands = [] + if replaced_config: + del_requests = self.get_delete_tacacs_server_requests(replaced_config, have) + requests.extend(del_requests) + commands.extend(update_states(replaced_config, "deleted")) + add_commands = want + else: + add_commands = diff + + if add_commands: + add_requests = self.get_modify_tacacs_server_requests(add_commands, have) + if len(add_requests) > 0: + requests.extend(add_requests) + commands.extend(update_states(add_commands, "replaced")) + + return commands, requests + + def _state_overridden(self, want, have, diff): + """ The command generator when state is overridden + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + + r_diff = get_diff(have, want, TEST_KEYS) + if have and (diff or r_diff): + del_requests = self.get_delete_tacacs_server_requests(have, have) + requests.extend(del_requests) + commands.extend(update_states(have, "deleted")) + have = [] + + if not have and want: + want_commands = want + want_requests = self.get_modify_tacacs_server_requests(want_commands, have) + + if len(want_requests) > 0: + requests.extend(want_requests) + commands.extend(update_states(want_commands, "overridden")) + + return commands, requests + def get_tacacs_global_payload(self, conf): payload = {} global_cfg = {} diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/users/users.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/users/users.py index 73398cf74..9c79cd0e4 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/users/users.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/users/users.py @@ -26,14 +26,21 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s edit_config ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( - dict_to_set, update_states, get_diff, ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff +) from ansible.module_utils.connection import ConnectionError PATCH = 'patch' DELETE = 'delete' +TEST_KEYS_formatted_diff = [ + {'config': {'name': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, +] class Users(ConfigBase): @@ -83,7 +90,7 @@ class Users(ConfigBase): except ConnectionError as exc: try: json_obj = json.loads(str(exc).replace("'", '"')) - if json_obj and type(json_obj) is dict and 401 == json_obj['code']: + if json_obj and isinstance(json_obj, dict) and 401 == json_obj['code']: auth_error = True warnings.append("Unable to get after configs as password got changed for current user") else: @@ -101,6 +108,19 @@ class Users(ConfigBase): if result['changed']: result['after'] = changed_users_facts + new_config = changed_users_facts + old_config = existing_users_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_users_facts, + TEST_KEYS_formatted_diff) + result['after(generated)'] = new_config + if self._module._diff: + self.sort_lists_in_config(new_config) + self.sort_lists_in_config(old_config) + result['diff'] = get_formatted_config_diff(old_config, + new_config, + self._module._verbosity) result['warnings'] = warnings return result @@ -133,8 +153,7 @@ class Users(ConfigBase): want = [] new_want = [{'name': conf['name'], 'role': conf['role']} for conf in want] - new_have = [{'name': conf['name'], 'role': conf['role']} for conf in have] - new_diff = get_diff(new_want, new_have) + new_diff = get_diff(new_want, have) diff = [] for cfg in new_diff: @@ -187,7 +206,7 @@ class Users(ConfigBase): :returns: the commands necessary to remove the current configuration of the provided objects """ - # if want is none, then delete all the usersi except admin + # if want is none, then delete all the users except admin if not want: commands = have else: @@ -202,6 +221,65 @@ class Users(ConfigBase): return commands, requests + def _state_replaced(self, want, have, diff): + """ The command generator when state is merged + + :param want: the additive configuration as a dictionary + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to replace the current configuration + wit the provided configuration + """ + self.validate_new_users(want, have) + + commands = diff + requests = self.get_modify_users_requests(commands, have) + if commands and len(requests) > 0: + commands = update_states(commands, "replaced") + else: + commands = [] + + return commands, requests + + def _state_overridden(self, want, have, diff): + """ The command generator when state is overridden + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + self.sort_lists_in_config(want) + self.sort_lists_in_config(have) + new_want = [{'name': conf['name'], 'role': conf['role']} for conf in want] + new_have = [] + for conf in have: + # Exclude admin user from new_have if it isn't present in new_want + if conf['name'] == 'admin' and not any(cfg['name'] == 'admin' for cfg in new_want): + continue + else: + new_have.append({'name': conf['name'], 'role': conf['role']}) + + if diff or new_want != new_have: + # Delete all users except admin + del_requests = self.get_delete_users_requests(have, have) + requests.extend(del_requests) + commands.extend(update_states(have, "deleted")) + have = [] + + # Merge want configuration + mod_commands = want + mod_requests = self.get_modify_users_requests(mod_commands, have) + + if mod_commands and len(mod_requests) > 0: + requests.extend(mod_requests) + commands.extend(update_states(mod_commands, "overridden")) + + return commands, requests + def get_pwd(self, pw): clear_pwd = hashed_pwd = "" pwd = pw.replace("\\", "") @@ -281,7 +359,7 @@ class Users(ConfigBase): if not commands: return requests - # Skip the asmin user in 'deleted' state. we cannot delete all users + # Skip the admin user in 'deleted' state. we cannot delete all users admin_usr = None for conf in commands: @@ -297,3 +375,7 @@ class Users(ConfigBase): if admin_usr: commands.remove(admin_usr) return requests + + def sort_lists_in_config(self, config): + if config: + config.sort(key=lambda x: x['name']) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlan_mapping/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlan_mapping/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlan_mapping/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlan_mapping/vlan_mapping.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlan_mapping/vlan_mapping.py new file mode 100644 index 000000000..acb72db17 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlan_mapping/vlan_mapping.py @@ -0,0 +1,517 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic_vlan_mapping class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + get_diff, + update_states, + remove_empties_from_list, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible.module_utils.connection import ConnectionError + + +TEST_KEYS = [ + {'config': {'name': ''}}, + {'mapping': {'service_vlan': '', 'dot1q_tunnel': ''}}, +] + + +class Vlan_mapping(ConfigBase): + """ + The sonic_vlan_mapping class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'vlan_mapping', + ] + + def __init__(self, module): + super(Vlan_mapping, self).__init__(module) + + def get_vlan_mapping_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) + vlan_mapping_facts = facts['ansible_network_resources'].get('vlan_mapping') + if not vlan_mapping_facts: + return [] + return vlan_mapping_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = list() + + existing_vlan_mapping_facts = self.get_vlan_mapping_facts() + commands, requests = self.set_config(existing_vlan_mapping_facts) + if commands: + if not self._module.check_mode: + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + result['changed'] = True + result['commands'] = commands + + changed_vlan_mapping_facts = self.get_vlan_mapping_facts() + + result['before'] = existing_vlan_mapping_facts + if result['changed']: + result['after'] = changed_vlan_mapping_facts + + result['warnings'] = warnings + return result + + def set_config(self, existing_vlan_mapping_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = remove_empties_from_list(self._module.params['config']) + have = existing_vlan_mapping_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + state = self._module.params['state'] + have = self.convert_vlan_ids_range(have) + want = self.convert_vlan_ids_range(want) + diff = get_diff(want, have, TEST_KEYS) + + if state == 'overridden': + commands, requests = self._state_overridden(want, have, diff) + elif state == 'deleted': + commands, requests = self._state_deleted(want, have) + elif state == 'merged': + commands, requests = self._state_merged(want, have, diff) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have, diff) + + ret_commands = remove_empties_from_list(commands) + return ret_commands, requests + + def _state_replaced(self, want, have, diff): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + requests = [] + commands = [] + commands_del = [] + + commands_del = self.get_replaced_delete_list(want, have) + + if commands_del: + commands.extend(update_states(commands_del, "deleted")) + + requests_del = self.get_delete_vlan_mapping_requests(commands_del, have, is_delete_all=True) + if requests_del: + requests.extend(requests_del) + + if diff or commands_del: + requests_rep = self.get_create_vlan_mapping_requests(want, have) + if len(requests_rep): + requests.extend(requests_rep) + commands = update_states(want, "replaced") + else: + commands = [] + + return commands, requests + + def _state_overridden(self, want, have, diff): + """ The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + + commands_del = get_diff(have, want, TEST_KEYS) + if commands_del: + requests_del = self.get_delete_vlan_mapping_requests(commands_del, have, is_delete_all=True) + requests.extend(requests_del) + commands_del = update_states(commands_del, "deleted") + commands.extend(commands_del) + + commands_over = diff + if diff: + requests_over = self.get_create_vlan_mapping_requests(commands_over, have) + requests.extend(requests_over) + commands_over = update_states(commands_over, "overridden") + commands.extend(commands_over) + + return commands, requests + + def _state_merged(self, want, have, diff): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = diff + requests = self.get_create_vlan_mapping_requests(commands, have) + + if commands and len(requests): + commands = update_states(commands, 'merged') + else: + commands = [] + + return commands, requests + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + requests = [] + is_delete_all = False + + if not want: + commands = have + is_delete_all = True + else: + commands = want + + requests.extend(self.get_delete_vlan_mapping_requests(commands, have, is_delete_all)) + + if len(requests) == 0: + commands = [] + + if commands: + commands = update_states(commands, 'deleted') + + return commands, requests + + def get_replaced_delete_list(self, commands, have): + matched = [] + + for cmd in commands: + name = cmd.get('name', None) + interface_name = name.replace('/', '%2f') + mapping_list = cmd.get('mapping', []) + + matched_interface_name = None + matched_mapping_list = [] + for existing in have: + have_name = existing.get('name', None) + have_interface_name = have_name.replace('/', '%2f') + have_mapping_list = existing.get('mapping', []) + if interface_name == have_interface_name: + matched_interface_name = have_interface_name + matched_mapping_list = have_mapping_list + + if mapping_list and matched_mapping_list: + returned_mapping_list = [] + for mapping in mapping_list: + service_vlan = mapping.get('service_vlan', None) + + for matched_mapping in matched_mapping_list: + matched_service_vlan = matched_mapping.get('service_vlan', None) + + if matched_service_vlan and service_vlan: + if matched_service_vlan == service_vlan: + priority = mapping.get('priority', None) + have_priority = matched_mapping.get('priority', None) + inner_vlan = mapping.get('inner_vlan', None) + have_inner_vlan = matched_mapping.get('inner_vlan', None) + dot1q_tunnel = mapping.get('dot1q_tunnel', False) + have_dot1q_tunnel = matched_mapping.get('dot1q_tunnel', False) + vlan_ids = mapping.get('vlan_ids', []) + have_vlan_ids = matched_mapping.get('vlan_ids', []) + + if priority != have_priority: + returned_mapping_list.append(mapping) + elif inner_vlan != have_inner_vlan: + returned_mapping_list.append(mapping) + elif dot1q_tunnel != have_dot1q_tunnel: + returned_mapping_list.append(mapping) + elif sorted(vlan_ids) != sorted(have_vlan_ids): + returned_mapping_list.append(mapping) + + if returned_mapping_list: + matched.append({'name': interface_name, 'mapping': returned_mapping_list}) + + return matched + + def get_delete_vlan_mapping_requests(self, commands, have, is_delete_all): + """ Get list of requests to delete vlan mapping configurations + for all interfaces specified by the commands + """ + url = "data/openconfig-interfaces:interfaces/interface={}/openconfig-interfaces-ext:mapped-vlans/mapped-vlan={}" + priority_url = "/ingress-mapping/config/mapped-vlan-priority" + vlan_ids_url = "/match/single-tagged/config/vlan-ids={}" + method = "DELETE" + requests = [] + + # Delete all vlan mappings + if is_delete_all: + for cmd in commands: + name = cmd.get('name', None) + interface_name = name.replace('/', '%2f') + mapping_list = cmd.get('mapping', []) + + if mapping_list: + for mapping in mapping_list: + service_vlan = mapping.get('service_vlan', None) + path = url.format(interface_name, service_vlan) + request = {"path": path, "method": method} + requests.append(request) + + return requests + + else: + for cmd in commands: + name = cmd.get('name', None) + interface_name = name.replace('/', '%2f') + mapping_list = cmd.get('mapping', []) + + # Checks if there is a interface matching the delete command + have_interface_name = None + have_mapping_list = [] + for tmp in have: + tmp_name = tmp.get('name', None) + tmp_interface_name = tmp_name.replace('/', '%2f') + tmp_mapping_list = tmp.get('mapping', []) + if interface_name == tmp_interface_name: + have_interface_name = tmp_interface_name + have_mapping_list = tmp_mapping_list + + # Delete part or all of single mapping + if mapping_list: + for mapping in mapping_list: + service_vlan = mapping.get('service_vlan', None) + vlan_ids = mapping.get('vlan_ids', None) + priority = mapping.get('priority', None) + + # Checks if there is a vlan mapping matching the delete command + have_service_vlan = None + have_vlan_ids = None + have_priority = None + for have_mapping in have_mapping_list: + if have_mapping.get('service_vlan', None) == service_vlan: + have_service_vlan = have_mapping.get('service_vlan', None) + have_vlan_ids = have_mapping.get('vlan_ids', None) + have_priority = have_mapping.get('priority', None) + + if service_vlan and have_service_vlan: + if vlan_ids or priority: + # Delete priority + if priority and have_priority: + path = url.format(interface_name, service_vlan) + priority_url + request = {"path": path, "method": method} + requests.append(request) + # Delete vlan ids + if vlan_ids and have_vlan_ids: + vlan_ids_str = "" + same_vlan_ids_list = self.get_vlan_ids_diff(vlan_ids, have_vlan_ids, same=True) + if same_vlan_ids_list: + for vlan in same_vlan_ids_list: + if vlan_ids_str: + vlan_ids_str = vlan_ids_str + "%2C" + vlan.replace("-", "..") + else: + vlan_ids_str = vlan.replace("-", "..") + path = url.format(interface_name, service_vlan) + vlan_ids_url.format(vlan_ids_str) + request = {"path": path, "method": method} + requests.append(request) + # Delete entire mapping + else: + path = url.format(interface_name, service_vlan) + request = {"path": path, "method": method} + requests.append(request) + # Delete all mappings in an interface + else: + if have_mapping_list: + for mapping in have_mapping_list: + service_vlan = mapping.get('service_vlan', None) + path = url.format(interface_name, service_vlan) + request = {"path": path, "method": method} + requests.append(request) + + return requests + + def get_create_vlan_mapping_requests(self, commands, have): + """ Get list of requests to create/modify vlan mapping configurations + for all interfaces specified by the commands + """ + requests = [] + if not commands: + return requests + + for cmd in commands: + name = cmd.get('name', None) + interface_name = name.replace('/', '%2f') + mapping_list = cmd.get('mapping', []) + + if mapping_list: + for mapping in mapping_list: + requests.append(self.get_create_vlan_mapping_request(interface_name, mapping)) + return requests + + def get_create_vlan_mapping_request(self, interface_name, mapping): + url = "data/openconfig-interfaces:interfaces/interface={}/openconfig-interfaces-ext:mapped-vlans" + body = {} + method = "PATCH" + match_data = None + + service_vlan = mapping.get('service_vlan', None) + priority = mapping.get('priority', None) + vlan_ids = mapping.get('vlan_ids', []) + dot1q_tunnel = mapping.get('dot1q_tunnel', None) + inner_vlan = mapping.get('inner_vlan', None) + + if not dot1q_tunnel: + if len(vlan_ids) > 1: + raise Exception("When dot1q-tunnel is false only one VLAN ID can be passed to the vlan_ids list") + if not vlan_ids and priority: + match_data = None + elif vlan_ids: + if inner_vlan: + match_data = {'double-tagged': {'config': {'inner-vlan-id': inner_vlan, 'outer-vlan-id': int(vlan_ids[0])}}} + else: + match_data = {'single-tagged': {'config': {'vlan-ids': [int(vlan_ids[0])]}}} + if priority: + ing_data = {'config': {'vlan-stack-action': 'SWAP', 'mapped-vlan-priority': priority}} + egr_data = {'config': {'vlan-stack-action': 'SWAP', 'mapped-vlan-priority': priority}} + else: + ing_data = {'config': {'vlan-stack-action': 'SWAP'}} + egr_data = {'config': {'vlan-stack-action': 'SWAP'}} + else: + if inner_vlan: + raise Exception("Inner vlan can only be passed when dot1q_tunnel is false") + if not vlan_ids and priority: + match_data = None + elif vlan_ids: + vlan_ids_list = [] + for vlan in vlan_ids: + vlan_ids_list.append(int(vlan)) + match_data = {'single-tagged': {'config': {'vlan-ids': vlan_ids_list}}} + if priority: + ing_data = {'config': {'vlan-stack-action': 'PUSH', 'mapped-vlan-priority': priority}} + egr_data = {'config': {'vlan-stack-action': 'POP', 'mapped-vlan-priority': priority}} + else: + ing_data = {'config': {'vlan-stack-action': 'PUSH'}} + egr_data = {'config': {'vlan-stack-action': 'POP'}} + if match_data: + body = {'openconfig-interfaces-ext:mapped-vlans': {'mapped-vlan': [ + {'vlan-id': service_vlan, + 'config': {'vlan-id': service_vlan}, + 'match': match_data, + 'ingress-mapping': ing_data, + 'egress-mapping': egr_data} + ]}} + else: + body = {'openconfig-interfaces-ext:mapped-vlans': {'mapped-vlan': [ + {'vlan-id': service_vlan, + 'config': {'vlan-id': service_vlan}, + 'ingress-mapping': ing_data, + 'egress-mapping': egr_data} + ]}} + + request = {"path": url.format(interface_name), "method": method, "data": body} + return request + + def get_vlan_ids_diff(self, vlan_ids, have_vlan_ids, same): + """ Takes two vlan id lists and finds the difference. + :param vlan_ids: list of vlan ids that is looking for diffs + :param have_vlan_ids: list of vlan ids that is being compared to + :param same: if true will instead return list of shared values + :rtype: list(str) + """ + results = [] + + for vlan_id in vlan_ids: + if same: + if vlan_id in have_vlan_ids: + results.append(vlan_id) + else: + if vlan_id not in have_vlan_ids: + results.append(vlan_id) + + return results + + def vlanIdsRangeStr(self, vlanList): + rangeList = [] + for vid in vlanList: + if "-" in vid: + vidList = vid.split("-") + lower = int(vidList[0]) + upper = int(vidList[1]) + for i in range(lower, upper + 1): + rangeList.append(str(i)) + else: + rangeList.append(vid) + return rangeList + + def convert_vlan_ids_range(self, config): + + interface_index = 0 + for conf in config: + name = conf.get('name', None) + interface_name = name.replace('/', '%2f') + mapping_list = conf.get('mapping', []) + + mapping_index = 0 + if mapping_list: + for mapping in mapping_list: + vlan_ids = mapping.get('vlan_ids', None) + + if vlan_ids: + config[interface_index]['mapping'][mapping_index]['vlan_ids'] = self.vlanIdsRangeStr(vlan_ids) + mapping_index = mapping_index + 1 + interface_index = interface_index + 1 + + return config diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlans/vlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlans/vlans.py index 404051074..0a0b105a7 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlans/vlans.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlans/vlans.py @@ -14,17 +14,17 @@ created from __future__ import absolute_import, division, print_function __metaclass__ = type -import json - from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( to_list, + search_obj_in_list, ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( get_diff, + get_replaced_config, update_states, remove_empties_from_list, ) @@ -35,23 +35,20 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s to_request, edit_config ) -from ansible.module_utils._text import to_native +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff +) from ansible.module_utils.connection import ConnectionError -import traceback - -LIB_IMP_ERR = None -ERR_MSG = None -try: - import jinja2 - HAS_LIB = True -except Exception as e: - HAS_LIB = False - ERR_MSG = to_native(e) - LIB_IMP_ERR = traceback.format_exc() + TEST_KEYS = [ {'config': {'vlan_id': ''}}, ] +TEST_KEYS_formatted_diff = [ + {'config': {'vlan_id': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, +] class Vlans(ConfigBase): @@ -109,6 +106,18 @@ class Vlans(ConfigBase): if result['changed']: result['after'] = changed_vlans_facts + new_config = changed_vlans_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_vlans_facts, + TEST_KEYS_formatted_diff) + new_config.sort(key=lambda x: x['vlan_id']) + result['after(generated)'] = new_config + + if self._module._diff: + result['diff'] = get_formatted_config_diff(existing_vlans_facts, + new_config, + self._module._verbosity) result['warnings'] = warnings return result @@ -121,7 +130,7 @@ class Vlans(ConfigBase): to the desired configuration """ want = remove_empties_from_list(self._module.params['config']) - have = existing_vlans_facts + have = remove_empties_from_list(existing_vlans_facts) resp = self.set_state(want, have) return to_list(resp) @@ -157,7 +166,29 @@ class Vlans(ConfigBase): :returns: the commands necessary to migrate the current configuration to the desired configuration """ - return self._state_merged(want, have, diff) + commands = [] + requests = [] + + replaced_config = get_replaced_config(want, have, TEST_KEYS) + replaced_vlans = [] + for config in replaced_config: + vlan_obj = search_obj_in_list(config['vlan_id'], want, 'vlan_id') + if vlan_obj and vlan_obj.get('description', None) is None: + replaced_vlans.append(config) + + if replaced_vlans: + del_requests = self.get_delete_vlans_requests(replaced_vlans, False) + requests.extend(del_requests) + commands.extend(update_states(replaced_config, "deleted")) + + if diff: + rep_commands = diff + rep_requests = self.get_create_vlans_requests(rep_commands) + if len(rep_requests) > 0: + requests.extend(rep_requests) + commands.extend(update_states(rep_commands, "replaced")) + + return commands, requests def _state_overridden(self, want, have, diff): """ The command generator when state is overridden @@ -166,20 +197,41 @@ class Vlans(ConfigBase): :returns: the commands necessary to migrate the current configuration to the desired configuration """ - ret_requests = list() - commands = list() - vlans_to_delete = get_diff(have, want, TEST_KEYS) - if vlans_to_delete: - delete_vlans_requests = self.get_delete_vlans_requests(vlans_to_delete) - ret_requests.extend(delete_vlans_requests) - commands.extend(update_states(vlans_to_delete, "deleted")) + commands = [] + requests = [] + + r_diff = get_diff(have, want, TEST_KEYS) + if not diff and not r_diff: + return commands, requests + + del_vlans = [] + del_descr_vlans = [] + for config in r_diff: + vlan_obj = search_obj_in_list(config['vlan_id'], want, 'vlan_id') + if vlan_obj: + if vlan_obj.get('description', None) is None: + del_descr_vlans.append(config) + else: + del_vlans.append(config) + + if del_vlans: + del_requests = self.get_delete_vlans_requests(del_vlans, True) + requests.extend(del_requests) + commands.extend(update_states(del_vlans, "deleted")) + + if del_descr_vlans: + del_requests = self.get_delete_vlans_requests(del_descr_vlans, False) + requests.extend(del_requests) + commands.extend(update_states(del_descr_vlans, "deleted")) if diff: - vlans_to_create_requests = self.get_create_vlans_requests(diff) - ret_requests.extend(vlans_to_create_requests) - commands.extend(update_states(diff, "merged")) + ovr_commands = diff + ovr_requests = self.get_create_vlans_requests(ovr_commands) + if len(ovr_requests) > 0: + requests.extend(ovr_requests) + commands.extend(update_states(ovr_commands, "overridden")) - return commands, ret_requests + return commands, requests def _state_merged(self, want, have, diff): """ The command generator when state is merged @@ -204,16 +256,18 @@ class Vlans(ConfigBase): """ commands = list() # if want is none, then delete all the vlans + delete_vlan = False if not want: commands = have + delete_vlan = True else: # delete specific vlans commands = get_diff(want, diff, TEST_KEYS) - requests = self.get_delete_vlans_requests(commands) + requests = self.get_delete_vlans_requests(commands, delete_vlan) commands = update_states(commands, "deleted") return commands, requests - def get_delete_vlans_requests(self, configs): + def get_delete_vlans_requests(self, configs, delete_vlan=False): requests = [] if not configs: return requests @@ -223,7 +277,7 @@ class Vlans(ConfigBase): for vlan in configs: vlan_id = vlan.get("vlan_id") description = vlan.get("description") - if description: + if description and not delete_vlan: path = self.get_delete_vlan_config_attr(vlan_id, "description") else: path = url.format(vlan_id) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vrfs/vrfs.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vrfs/vrfs.py index 83deb0ecb..2a07e6456 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vrfs/vrfs.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vrfs/vrfs.py @@ -14,6 +14,7 @@ created from __future__ import absolute_import, division, print_function __metaclass__ = type +from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -30,12 +31,22 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s update_states, normalize_interface_name ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff +) from ansible.module_utils.connection import ConnectionError PATCH = 'patch' DELETE = 'DELETE' +MGMT_VRF_NAME = 'mgmt' TEST_KEYS = [ - {'interfaces': {'name': ''}}, + {'interfaces': {'name': ''}} +] +TEST_KEYS_formatted_diff = [ + {'config': {'name': ''}}, + {'interfaces': {'name': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}} ] @@ -97,6 +108,17 @@ class Vrfs(ConfigBase): if result['changed']: result['after'] = changed_vrf_interfaces_facts + new_config = changed_vrf_interfaces_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_vrf_interfaces_facts, + TEST_KEYS_formatted_diff) + result['after(generated)'] = new_config + + if self._module._diff: + result['diff'] = get_formatted_config_diff(existing_vrf_interfaces_facts, + new_config, + self._module._verbosity) result['warnings'] = warnings return result @@ -137,6 +159,10 @@ class Vrfs(ConfigBase): commands, requests = self._state_deleted(want, have) elif state == 'merged': commands, requests = self._state_merged(want, have, diff) + elif state == 'overridden': + commands, requests = self._state_overridden(want, have) + elif state == 'replaced': + commands, requests = self._state_replaced(want, have) return commands, requests @@ -172,7 +198,7 @@ class Vrfs(ConfigBase): """ # if want is none, then delete all the vrfs if not want: - commands = have + commands = self.preprocess_mgmt_vrf_for_deleted(have) self.delete_all_flag = True else: commands = want @@ -180,7 +206,7 @@ class Vrfs(ConfigBase): requests = [] if commands: - requests = self.get_delete_vrf_interface_requests(commands, have, want) + requests = self.get_delete_vrf_interface_requests(commands, have) if len(requests) > 0: commands = update_states(commands, "deleted") @@ -189,7 +215,76 @@ class Vrfs(ConfigBase): return commands, requests - def get_delete_vrf_interface_requests(self, configs, have, want): + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + requests = [] + + replaced_config = self.get_replaced_config(have, want) + self.sort_config(replaced_config) + self.sort_config(want) + + if replaced_config and replaced_config != want: + self.delete_all_flag = False + del_requests = self.get_delete_vrf_interface_requests(replaced_config, have, 'replaced') + requests.extend(del_requests) + commands.extend(update_states(replaced_config, "deleted")) + replaced_config = [] + + if not replaced_config and want: + add_commands = want + add_requests = self.get_create_requests(add_commands, have) + + if len(add_requests) > 0: + requests.extend(add_requests) + commands.extend(update_states(add_commands, "replaced")) + + return commands, requests + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param diff: the difference between want and have + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + self.sort_config(have) + self.sort_config(want) + + commands = [] + requests = [] + + if have and have != want: + want, have = self.preprocess_mgmt_vrf_for_overridden(want, have) + + self.delete_all_flag = True + del_requests = self.get_delete_vrf_interface_requests(have, have) + requests.extend(del_requests) + commands.extend(update_states(have, "deleted")) + have = [] + + if not have and want: + add_commands = want + add_requests = self.get_create_requests(add_commands, have) + + if len(add_requests) > 0: + requests.extend(add_requests) + commands.extend(update_states(add_commands, "overridden")) + + return commands, requests + + def get_delete_vrf_interface_requests(self, configs, have, state=None): requests = [] if not configs: return requests @@ -211,21 +306,29 @@ class Vrfs(ConfigBase): continue # if members are not mentioned delet the vrf name - if (self._module.params['state'] == 'deleted' and self.delete_all_flag) or empty_flag: + adjusted_delete_all_flag = name != MGMT_VRF_NAME and self.delete_all_flag + adjusted_empty_flag = empty_flag + if state == 'replaced': + adjusted_empty_flag = empty_flag and name != MGMT_VRF_NAME + + if adjusted_delete_all_flag or adjusted_empty_flag: url = 'data/openconfig-network-instance:network-instances/network-instance={0}'.format(name) request = {"path": url, "method": method} requests.append(request) else: - matched_members = matched.get('members', None) - - if matched_members: - matched_intf = matched_members.get('interfaces', None) - if matched_intf: - for del_mem in matched_intf: - url = 'data/openconfig-network-instance:network-instances/' - url = url + 'network-instance={0}/interfaces/interface={1}'.format(name, del_mem['name']) - request = {"path": url, "method": method} - requests.append(request) + have_members = matched.get('members', None) + conf_members = conf.get('members', None) + + if have_members: + have_intf = have_members.get('interfaces', None) + conf_intf = conf_members.get('interfaces', None) + if conf_intf: + for del_mem in conf_intf: + if del_mem in have_intf: + url = 'data/openconfig-network-instance:network-instances/' + url = url + 'network-instance={0}/interfaces/interface={1}'.format(name, del_mem['name']) + request = {"path": url, "method": method} + requests.append(request) return requests @@ -301,3 +404,62 @@ class Vrfs(ConfigBase): network_inst_payload["openconfig-network-instance:interface"].append(member_payload) return network_inst_payload + + def get_vrf_name(self, vrf): + return vrf.get('name') + + def get_interface_name(self, intf): + return intf.get('name') + + def sort_config(self, conf): + if conf: + conf.sort(key=self.get_vrf_name) + for vrf in conf: + if vrf.get('members', None) and vrf['members'].get('interfaces', None): + vrf['members']['interfaces'].sort(key=self.get_interface_name) + + def get_replaced_config(self, have, want): + + replaced_vrfs = [] + for vrf in want: + vrf_name = vrf['name'] + have_vrf = next((h_vrf for h_vrf in have if h_vrf['name'] == vrf_name), None) + if have_vrf: + replaced_vrfs.append(have_vrf) + + return replaced_vrfs + + def preprocess_mgmt_vrf_for_deleted(self, have): + new_have = have + conf = next((vrf for vrf in new_have if vrf['name'] == MGMT_VRF_NAME), None) + if conf: + new_have = deepcopy(have) + new_have.remove(conf) + return new_have + + def preprocess_mgmt_vrf_for_overridden(self, want, have): + new_want = deepcopy(want) + new_have = deepcopy(have) + h_conf = next((vrf for vrf in new_have if vrf['name'] == MGMT_VRF_NAME), None) + if h_conf: + conf = next((vrf for vrf in new_want if vrf['name'] == MGMT_VRF_NAME), None) + if conf: + mv_intfs = [] + if conf.get('members', None) and conf['members'].get('interfaces', None): + mv_intfs = conf['members'].get('interfaces', []) + + h_mv_intfs = [] + if h_conf.get('members', None) and h_conf['members'].get('interfaces', None): + h_mv_intfs = h_conf['members'].get('interfaces', []) + + mv_intfs.sort(key=lambda x: x['name']) + h_mv_intfs.sort(key=lambda x: x['name']) + if mv_intfs == h_mv_intfs: + new_want.remove(conf) + new_have.remove(h_conf) + elif not h_mv_intfs: + new_have.remove(h_conf) + else: + new_have.remove(h_conf) + + return new_want, new_have diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vxlans/vxlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vxlans/vxlans.py index d44adcedf..0a87c98c8 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vxlans/vxlans.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vxlans/vxlans.py @@ -26,7 +26,9 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( get_diff, - update_states + update_states, + get_replaced_config, + send_requests ) from ansible.module_utils.connection import ConnectionError @@ -124,7 +126,7 @@ class Vxlans(ConfigBase): diff = get_diff(want, have, test_keys) if state == 'overridden': - commands, requests = self._state_overridden(want, have, diff) + commands, requests = self._state_overridden(want, have) elif state == 'deleted': commands, requests = self._state_deleted(want, have, diff) elif state == 'merged': @@ -141,57 +143,63 @@ class Vxlans(ConfigBase): :returns: the commands necessary to migrate the current configuration to the desired configuration """ + requests = [] + replaced_config = get_replaced_config(want, have, test_keys) + + if replaced_config: + self.sort_lists_in_config(replaced_config) + self.sort_lists_in_config(have) + is_delete_all = (replaced_config == have) + if is_delete_all: + requests = self.get_delete_all_vxlan_request(have) + else: + requests = self.get_delete_vxlan_request(replaced_config, have) + + send_requests(self._module, requests) + commands = want + else: + commands = diff requests = [] - commands = [] - commands_del = get_diff(have, want, test_keys) - requests_del = [] - if commands_del: - requests_del = self.get_delete_vxlan_request(commands_del, have) - if requests_del: - requests.extend(requests_del) - commands_del = update_states(commands_del, "deleted") - commands.extend(commands_del) - - commands_rep = diff - requests_rep = [] - if commands_rep: - requests_rep = self.get_create_vxlans_request(commands_rep, have) - if requests_rep: - requests.extend(requests_rep) - commands_rep = update_states(commands_rep, "replaced") - commands.extend(commands_rep) + if commands: + requests = self.get_create_vxlans_request(commands, have) + if len(requests) > 0: + commands = update_states(commands, "replaced") + else: + commands = [] + else: + commands = [] return commands, requests - def _state_overridden(self, want, have, diff): + def _state_overridden(self, want, have): """ The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ - requests = [] + self.sort_lists_in_config(want) + self.sort_lists_in_config(have) + + if have and have != want: + requests = self.get_delete_all_vxlan_request(have) + send_requests(self._module, requests) + + have = [] + commands = [] + requests = [] + + if not have and want: + commands = want + requests = self.get_create_vxlans_request(commands, have) - commands_del = get_diff(have, want) - requests_del = [] - if commands_del: - requests_del = self.get_delete_vxlan_request(commands_del, have) - if requests_del: - requests.extend(requests_del) - commands_del = update_states(commands_del, "deleted") - commands.extend(commands_del) - - commands_over = diff - requests_over = [] - if commands_over: - requests_over = self.get_create_vxlans_request(commands_over, have) - if requests_over: - requests.extend(requests_over) - commands_over = update_states(commands_over, "overridden") - commands.extend(commands_over) + if len(requests) > 0: + commands = update_states(commands, "overridden") + else: + commands = [] return commands, requests @@ -271,6 +279,7 @@ class Vxlans(ConfigBase): vlan_map_requests = [] src_ip_requests = [] primary_ip_requests = [] + evpn_nvo_requests = [] tunnel_requests = [] # Need to delete in reverse order of creation. @@ -282,6 +291,7 @@ class Vxlans(ConfigBase): vrf_map_list = conf.get('vrf_map', []) src_ip = conf.get('source_ip', None) primary_ip = conf.get('primary_ip', None) + evpn_nvo = conf.get('evpn_nvo', None) if vrf_map_list: vrf_map_requests.extend(self.get_delete_vrf_map_request(conf, conf, name, vrf_map_list)) @@ -291,6 +301,8 @@ class Vxlans(ConfigBase): src_ip_requests.extend(self.get_delete_src_ip_request(conf, conf, name, src_ip)) if primary_ip: primary_ip_requests.extend(self.get_delete_primary_ip_request(conf, conf, name, primary_ip)) + if evpn_nvo: + evpn_nvo_requests.extend(self.get_delete_evpn_request(conf, conf, evpn_nvo)) tunnel_requests.extend(self.get_delete_tunnel_request(conf, conf, name)) if vrf_map_requests: @@ -301,6 +313,8 @@ class Vxlans(ConfigBase): requests.extend(src_ip_requests) if primary_ip_requests: requests.extend(primary_ip_requests) + if evpn_nvo_requests: + requests.extend(evpn_nvo_requests) if tunnel_requests: requests.extend(tunnel_requests) @@ -315,6 +329,7 @@ class Vxlans(ConfigBase): vrf_map_requests = [] vlan_map_requests = [] src_ip_requests = [] + evpn_nvo_requests = [] primary_ip_requests = [] tunnel_requests = [] @@ -325,6 +340,7 @@ class Vxlans(ConfigBase): name = conf['name'] src_ip = conf.get('source_ip', None) + evpn_nvo = conf.get('evpn_nvo', None) primary_ip = conf.get('primary_ip', None) vlan_map_list = conf.get('vlan_map', None) vrf_map_list = conf.get('vrf_map', None) @@ -342,7 +358,7 @@ class Vxlans(ConfigBase): is_delete_full = False if (name and vlan_map_list is None and vrf_map_list is None and - src_ip is None and primary_ip is None): + src_ip is None and evpn_nvo is None and primary_ip is None): is_delete_full = True vrf_map_list = matched.get("vrf_map", []) vlan_map_list = matched.get("vlan_map", []) @@ -364,7 +380,8 @@ class Vxlans(ConfigBase): have_vlan_map_count -= len(temp_vlan_map_requests) if src_ip: src_ip_requests.extend(self.get_delete_src_ip_request(conf, matched, name, src_ip)) - + if evpn_nvo: + evpn_nvo_requests.extend(self.get_delete_evpn_request(conf, matched, evpn_nvo)) if primary_ip: primary_ip_requests.extend(self.get_delete_primary_ip_request(conf, matched, name, primary_ip)) if is_delete_full: @@ -376,6 +393,8 @@ class Vxlans(ConfigBase): requests.extend(vlan_map_requests) if src_ip_requests: requests.extend(src_ip_requests) + if evpn_nvo_requests: + requests.extend(evpn_nvo_requests) if primary_ip_requests: requests.extend(primary_ip_requests) if tunnel_requests: @@ -399,7 +418,7 @@ class Vxlans(ConfigBase): payload = self.build_create_tunnel_payload(conf) request = {"path": url, "method": PATCH, "data": payload} requests.append(request) - if conf.get('source_ip', None): + if conf.get('evpn_nvo', None): requests.append(self.get_create_evpn_request(conf)) return requests @@ -502,12 +521,23 @@ class Vxlans(ConfigBase): payload_url = dict({"sonic-vrf:vni": vrf_map['vni']}) return payload_url - def get_delete_evpn_request(self, conf): + def get_delete_evpn_request(self, conf, matched, del_evpn_nvo): # Create URL and payload - url = "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST={evpn_nvo}".format(evpn_nvo=conf['evpn_nvo']) - request = {"path": url, "method": DELETE} + requests = [] - return request + url = "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST={evpn_nvo}" + + is_change_needed = False + if matched: + matched_evpn_nvo = matched.get('evpn_nvo', None) + if matched_evpn_nvo and matched_evpn_nvo == del_evpn_nvo: + is_change_needed = True + + if is_change_needed: + request = {"path": url.format(evpn_nvo=conf['evpn_nvo']), "method": DELETE} + requests.append(request) + + return requests def get_delete_tunnel_request(self, conf, matched, name): # Create URL and payload @@ -530,9 +560,7 @@ class Vxlans(ConfigBase): if matched_source_ip and matched_source_ip == del_source_ip: is_change_needed = True - # Delete the EVPN NVO if the source_ip address is being deleted. if is_change_needed: - requests.append(self.get_delete_evpn_request(conf)) request = {"path": url.format(name=name), "method": DELETE} requests.append(request) @@ -604,3 +632,18 @@ class Vxlans(ConfigBase): requests.append(request) return requests + + def sort_lists_in_config(self, config): + if config: + config.sort(key=self.get_name) + for cfg in config: + if 'vlan_map' in cfg and cfg['vlan_map']: + cfg['vlan_map'].sort(key=self.get_vni) + if 'vrf_map' in cfg and cfg['vrf_map']: + cfg['vrf_map'].sort(key=self.get_vni) + + def get_name(self, name): + return name.get('name') + + def get_vni(self, vni): + return vni.get('vni') diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/aaa/aaa.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/aaa/aaa.py index 5a7bd05c9..541a5805e 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/aaa/aaa.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/aaa/aaa.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/acl_interfaces/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/acl_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/acl_interfaces/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/acl_interfaces/acl_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/acl_interfaces/acl_interfaces.py new file mode 100644 index 000000000..ec2973d84 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/acl_interfaces/acl_interfaces.py @@ -0,0 +1,148 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic acl_interfaces fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils.connection import ConnectionError +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.acl_interfaces.acl_interfaces import Acl_interfacesArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) + + +class Acl_interfacesFacts(object): + """ The sonic acl_interfaces fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = Acl_interfacesArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for acl_interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if connection: # just for linting purposes, remove + pass + + if not data: + acl_interfaces_configs = self.get_acl_interfaces() + + objs = [] + for interface_config in acl_interfaces_configs.items(): + obj = self.render_config(self.generated_spec, interface_config) + if obj: + objs.append(obj) + + ansible_facts['ansible_network_resources'].pop('acl_interfaces', None) + facts = {} + if objs: + params = utils.validate_config(self.argument_spec, {'config': objs}) + facts['acl_interfaces'] = params['config'] + + ansible_facts['ansible_network_resources'].update(facts) + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + config['name'] = conf[0] + config['access_groups'] = [] + + acls = {'mac': [], 'ipv4': [], 'ipv6': []} + for acl in conf[1]: + acl_type = acl.pop('type') + if acl_type in ('ACL_L2', 'openconfig-acl:ACL_L2'): + acls['mac'].append(acl) + elif acl_type in ('ACL_IPV4', 'openconfig-acl:ACL_IPV4'): + acls['ipv4'].append(acl) + elif acl_type in ('ACL_IPV6', 'openconfig-acl:ACL_IPV6'): + acls['ipv6'].append(acl) + + for acl_type, acl_list in acls.items(): + if acl_list: + config['access_groups'].append({ + 'type': acl_type, + 'acls': acl_list + }) + + return config + + def get_acl_interfaces(self): + """Get all interface access-group configurations available in chassis""" + acl_interfaces_path = 'data/openconfig-acl:acl/interfaces' + method = 'GET' + request = [{'path': acl_interfaces_path, 'method': method}] + + try: + response = edit_config(self._module, to_request(self._module, request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + acl_interfaces = [] + if response[0][1].get('openconfig-acl:interfaces'): + acl_interfaces = response[0][1]['openconfig-acl:interfaces'].get('interface', []) + + acl_interfaces_configs = {} + for interface in acl_interfaces: + acls_list = [] + + ingress_acls = interface.get('ingress-acl-sets', {}).get('ingress-acl-set', []) + for acl in ingress_acls: + if acl.get('config'): + acls_list.append({ + 'name': acl['config']['set-name'], + 'type': acl['config']['type'], + 'direction': 'in' + }) + + egress_acls = interface.get('egress-acl-sets', {}).get('egress-acl-set', []) + for acl in egress_acls: + if acl.get('config'): + acls_list.append({ + 'name': acl['config']['set-name'], + 'type': acl['config']['type'], + 'direction': 'out' + }) + + acl_interfaces_configs[interface['id']] = acls_list + + return acl_interfaces_configs diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bfd/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bfd/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bfd/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bfd/bfd.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bfd/bfd.py new file mode 100644 index 000000000..b8786947d --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bfd/bfd.py @@ -0,0 +1,236 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic bfd fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bfd.bfd import BfdArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) + + +class BfdFacts(object): + """ The sonic bfd fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = BfdArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for bfd + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + objs = [] + + if not data: + bfd_cfg = self.get_bfd_config(self._module) + data = self.update_bfd(bfd_cfg) + objs = self.render_config(self.generated_spec, data) + facts = {} + if objs: + params = utils.validate_config(self.argument_spec, {'config': objs}) + facts['bfd'] = params['config'] + ansible_facts['ansible_network_resources'].update(facts) + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + return conf + + def update_bfd(self, data): + bfd_dict = {} + if data: + bfd_dict['profiles'] = self.update_profiles(data) + bfd_dict['single_hops'] = self.update_single_hops(data) + bfd_dict['multi_hops'] = self.update_multi_hops(data) + + return bfd_dict + + def update_profiles(self, data): + all_profiles = [] + bfd_profile = data.get('openconfig-bfd-ext:bfd-profile', None) + if bfd_profile: + profile_list = bfd_profile.get('profile', None) + if profile_list: + for profile in profile_list: + profile_dict = {} + profile_name = profile['profile-name'] + config = profile['config'] + enabled = config.get('enabled', None) + transmit_interval = config.get('desired-minimum-tx-interval', None) + receive_interval = config.get('required-minimum-receive', None) + detect_multiplier = config.get('detection-multiplier', None) + passive_mode = config.get('passive-mode', None) + min_ttl = config.get('minimum-ttl', None) + echo_interval = config.get('desired-minimum-echo-receive', None) + echo_mode = config.get('echo-active', None) + + if profile_name: + profile_dict['profile_name'] = profile_name + if enabled is not None: + profile_dict['enabled'] = enabled + if transmit_interval: + profile_dict['transmit_interval'] = transmit_interval + if receive_interval: + profile_dict['receive_interval'] = receive_interval + if detect_multiplier: + profile_dict['detect_multiplier'] = detect_multiplier + if passive_mode is not None: + profile_dict['passive_mode'] = passive_mode + if min_ttl: + profile_dict['min_ttl'] = min_ttl + if echo_interval: + profile_dict['echo_interval'] = echo_interval + if echo_mode is not None: + profile_dict['echo_mode'] = echo_mode + if profile_dict: + all_profiles.append(profile_dict) + + return all_profiles + + def update_single_hops(self, data): + all_single_hops = [] + bfd_single_hop = data.get('openconfig-bfd-ext:bfd-shop-sessions', None) + if bfd_single_hop: + single_hop_list = bfd_single_hop.get('single-hop', None) + if single_hop_list: + for hop in single_hop_list: + single_hop_dict = {} + remote_address = hop['remote-address'] + vrf = hop['vrf'] + interface = hop['interface'] + local_address = hop['local-address'] + config = hop['config'] + enabled = config.get('enabled', None) + transmit_interval = config.get('desired-minimum-tx-interval', None) + receive_interval = config.get('required-minimum-receive', None) + detect_multiplier = config.get('detection-multiplier', None) + passive_mode = config.get('passive-mode', None) + echo_interval = config.get('desired-minimum-echo-receive', None) + echo_mode = config.get('echo-active', None) + profile_name = config.get('profile-name', None) + + if remote_address: + single_hop_dict['remote_address'] = remote_address + if vrf: + single_hop_dict['vrf'] = vrf + if interface: + single_hop_dict['interface'] = interface + if local_address: + single_hop_dict['local_address'] = local_address + if enabled is not None: + single_hop_dict['enabled'] = enabled + if transmit_interval: + single_hop_dict['transmit_interval'] = transmit_interval + if receive_interval: + single_hop_dict['receive_interval'] = receive_interval + if detect_multiplier: + single_hop_dict['detect_multiplier'] = detect_multiplier + if passive_mode is not None: + single_hop_dict['passive_mode'] = passive_mode + if echo_interval: + single_hop_dict['echo_interval'] = echo_interval + if echo_mode is not None: + single_hop_dict['echo_mode'] = echo_mode + if profile_name: + single_hop_dict['profile_name'] = profile_name + if single_hop_dict: + all_single_hops.append(single_hop_dict) + + return all_single_hops + + def update_multi_hops(self, data): + all_multi_hops = [] + bfd_multi_hop = data.get('openconfig-bfd-ext:bfd-mhop-sessions', None) + if bfd_multi_hop: + multi_hop_list = bfd_multi_hop.get('multi-hop', None) + if multi_hop_list: + for hop in multi_hop_list: + multi_hop_dict = {} + remote_address = hop['remote-address'] + vrf = hop['vrf'] + local_address = hop['local-address'] + config = hop['config'] + enabled = config.get('enabled', None) + transmit_interval = config.get('desired-minimum-tx-interval', None) + receive_interval = config.get('required-minimum-receive', None) + detect_multiplier = config.get('detection-multiplier', None) + passive_mode = config.get('passive-mode', None) + min_ttl = config.get('minimum-ttl', None) + profile_name = config.get('profile-name', None) + + if remote_address: + multi_hop_dict['remote_address'] = remote_address + if vrf: + multi_hop_dict['vrf'] = vrf + if local_address: + multi_hop_dict['local_address'] = local_address + if enabled is not None: + multi_hop_dict['enabled'] = enabled + if transmit_interval: + multi_hop_dict['transmit_interval'] = transmit_interval + if receive_interval: + multi_hop_dict['receive_interval'] = receive_interval + if detect_multiplier: + multi_hop_dict['detect_multiplier'] = detect_multiplier + if passive_mode is not None: + multi_hop_dict['passive_mode'] = passive_mode + if min_ttl: + multi_hop_dict['min_ttl'] = min_ttl + if profile_name: + multi_hop_dict['profile_name'] = profile_name + if multi_hop_dict: + all_multi_hops.append(multi_hop_dict) + + return all_multi_hops + + def get_bfd_config(self, module): + bfd_cfg = None + get_bfd_path = '/data/openconfig-bfd:bfd' + request = {'path': get_bfd_path, 'method': 'get'} + + try: + response = edit_config(module, to_request(module, request)) + if 'openconfig-bfd:bfd' in response[0][1]: + bfd_cfg = response[0][1].get('openconfig-bfd:bfd', None) + except ConnectionError as exc: + module.fail_json(msg=str(exc), code=exc.code) + return bfd_cfg diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp/bgp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp/bgp.py index c86b53c2a..7ecf253e7 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp/bgp.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp/bgp.py @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -49,6 +48,7 @@ class BgpFacts(object): 'admin_max_med': ['max-med', 'admin-max-med-val'], 'max_med_on_startup_timer': ['max-med', 'time'], 'max_med_on_startup_med_val': ['max-med', 'max-med-val'], + 'rt_delay': 'route-map-process-delay' } def __init__(self, module, subspec='config', options='options'): @@ -92,8 +92,8 @@ class BgpFacts(object): ansible_facts['ansible_network_resources'].pop('bgp', None) facts = {} if objs: - params = utils.validate_config(self.argument_spec, {'config': remove_empties_from_list(objs)}) - facts['bgp'] = params['config'] + params = utils.validate_config(self.argument_spec, {'config': objs}) + facts['bgp'] = remove_empties_from_list(params['config']) ansible_facts['ansible_network_resources'].update(facts) return ansible_facts diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_af/bgp_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_af/bgp_af.py index fd37533e4..511fb024a 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_af/bgp_af.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_af/bgp_af.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2019 Red Hat +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -64,6 +63,10 @@ class Bgp_afFacts(object): 'network': ['network-config', 'network'], 'dampening': ['route-flap-damping', 'config', 'enabled'], 'route_advertise_list': ['l2vpn-evpn', 'openconfig-bgp-evpn-ext:route-advertise', 'route-advertise-list'], + 'rd': ['l2vpn-evpn', 'openconfig-bgp-evpn-ext:config', 'route-distinguisher'], + 'rt_in': ['l2vpn-evpn', 'openconfig-bgp-evpn-ext:config', 'import-rts'], + 'rt_out': ['l2vpn-evpn', 'openconfig-bgp-evpn-ext:config', 'export-rts'], + 'vnis': ['l2vpn-evpn', 'openconfig-bgp-evpn-ext:vnis', 'vni'] } af_redis_params_map = { @@ -104,6 +107,7 @@ class Bgp_afFacts(object): self.update_max_paths(data) self.update_network(data) self.update_route_advertise_list(data) + self.update_vnis(data) bgp_redis_data = get_all_bgp_af_redistribute(self._module, vrf_list, self.af_redis_params_map) self.update_redis_data(data, bgp_redis_data) self.update_afis(data) @@ -119,8 +123,8 @@ class Bgp_afFacts(object): ansible_facts['ansible_network_resources'].pop('bgp_af', None) facts = {} if objs: - params = utils.validate_config(self.argument_spec, {'config': remove_empties_from_list(objs)}) - facts['bgp_af'] = params['config'] + params = utils.validate_config(self.argument_spec, {'config': objs}) + facts['bgp_af'] = remove_empties_from_list(params['config']) ansible_facts['ansible_network_resources'].update(facts) return ansible_facts @@ -241,6 +245,38 @@ class Bgp_afFacts(object): rt_adv_lst.append(rt_adv_dict) af['route_advertise_list'] = rt_adv_lst + def update_vnis(self, data): + for conf in data: + afs = conf.get('address_family', []) + if afs: + for af in afs: + vnis = af.get('vnis', None) + if vnis: + vnis_list = [] + for vni in vnis: + vni_dict = {} + vni_config = vni['config'] + vni_number = vni_config.get('vni-number', None) + vni_adv_gw = vni_config.get('advertise-default-gw', None) + vni_adv_svi = vni_config.get('advertise-svi-ip', None) + vni_rd = vni_config.get('route-distinguisher', None) + vni_rt_in = vni_config.get('import-rts', []) + vni_rt_out = vni_config.get('export-rts', []) + if vni_number: + vni_dict['vni_number'] = vni_number + if vni_adv_gw is not None: + vni_dict['advertise_default_gw'] = vni_adv_gw + if vni_adv_svi is not None: + vni_dict['advertise_svi_ip'] = vni_adv_svi + if vni_rd: + vni_dict['rd'] = vni_rd + if vni_rt_in: + vni_dict['rt_in'] = vni_rt_in + if vni_rt_out: + vni_dict['rt_out'] = vni_rt_out + vnis_list.append(vni_dict) + af['vnis'] = vnis_list + def normalize_af_redis_params(self, af): norm_af = list() for e_af in af: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_as_paths/bgp_as_paths.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_as_paths/bgp_as_paths.py index 822db22a4..31cada350 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_as_paths/bgp_as_paths.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_as_paths/bgp_as_paths.py @@ -11,7 +11,6 @@ based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -73,8 +72,6 @@ class Bgp_as_pathsFacts(object): else: result['permit'] = False as_path_list_configs.append(result) - # with open('/root/ansible_log.log', 'a+') as fp: - # fp.write('as_path_list: ' + str(as_path_list_configs) + '\n') return as_path_list_configs def populate_facts(self, connection, ansible_facts, data=None): diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py index ffa294221..ff4827e61 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py @@ -11,7 +11,6 @@ based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -67,26 +66,37 @@ class Bgp_communitiesFacts(object): match = member_config['match-set-options'] permit_str = member_config.get('openconfig-bgp-policy-ext:action', None) members = member_config.get("community-member", []) - result['name'] = name + result['name'] = str(name) result['match'] = match + result['members'] = None + result['permit'] = False if permit_str and permit_str == 'PERMIT': result['permit'] = True - else: - result['permit'] = False if members: result['type'] = 'expanded' if 'REGEX' in members[0] else 'standard' + if result['type'] == 'expanded': + members = [':'.join(i.split(':')[1:]) for i in members] + members.sort() + result['members'] = {'regex': members} else: - result['type'] = '' - if result['type'] == 'expanded': - members = [':'.join(i.split(':')[1:]) for i in members] - result['local_as'] = True if "NO_EXPORT_SUBCONFED" in members else False - result['no_advertise'] = True if "NO_ADVERTISE" in members else False - result['no_export'] = True if "NO_EXPORT" in members else False - result['no_peer'] = True if "NOPEER" in members else False - result['members'] = {'regex': members} + result['type'] = 'standard' + + if result['type'] == 'standard': + result['local_as'] = None + result['no_advertise'] = None + result['no_export'] = None + result['no_peer'] = None + for i in members: + if "NO_EXPORT_SUBCONFED" in i: + result['local_as'] = True + elif "NO_ADVERTISE" in i: + result['no_advertise'] = True + elif "NO_EXPORT" in i: + result['no_export'] = True + elif "NOPEER" in i: + result['no_peer'] = True + bgp_communities_configs.append(result) - # with open('/root/ansible_log.log', 'a+') as fp: - # fp.write('bgp_communities: ' + str(bgp_communities_configs) + '\n') return bgp_communities_configs def populate_facts(self, connection, ansible_facts, data=None): @@ -129,17 +139,5 @@ class Bgp_communitiesFacts(object): :rtype: dictionary :returns: The generated config """ - config = deepcopy(spec) - try: - config['name'] = str(conf['name']) - config['members'] = conf['members'] - config['match'] = conf['match'] - config['type'] = conf['type'] - config['permit'] = conf['permit'] - except TypeError: - config['name'] = None - config['members'] = None - config['match'] = None - config['type'] = None - config['permit'] = None - return utils.remove_empties(config) + + return conf diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py index b1d7c4ad0..814a25d11 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py @@ -11,7 +11,6 @@ based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -69,34 +68,38 @@ class Bgp_ext_communitiesFacts(object): match = member_config['match-set-options'] permit_str = member_config.get('openconfig-bgp-policy-ext:action', None) members = member_config.get("ext-community-member", []) - result['name'] = name + result['name'] = str(name) result['match'] = match.lower() - + result['members'] = dict() + result['type'] = 'standard' + result['permit'] = False if permit_str and permit_str == 'PERMIT': result['permit'] = True + if members: + result['type'] = 'expanded' if 'REGEX' in members[0] else 'standard' + if result['type'] == 'expanded': + members = [':'.join(i.split(':')[1:]) for i in members] + members_list = list(map(str, members)) + members_list.sort() + result['members'] = {'regex': members_list} else: - result['permit'] = False - - result['members'] = dict() - rt = list() - soo = list() - regex = list() - for member in members: - if member.startswith('route-target'): - rt.append(':'.join(member.split(':')[1:])) - elif member.startswith('route-origin'): - soo.append(':'.join(member.split(':')[1:])) - elif member.startswith('REGEX'): - regex.append(':'.join(member.split(':')[1:])) - - result['type'] = 'standard' - if regex and len(regex) > 0: - result['type'] = 'expanded' - result['members']['regex'] = regex - if rt and len(rt) > 0: - result['members']['route_target'] = rt - if soo and len(soo) > 0: - result['members']['route_origin'] = soo + rt = list() + soo = list() + for member in members: + if member.startswith('route-origin'): + soo.append(':'.join(member.split(':')[1:])) + else: + rt.append(':'.join(member.split(':')[1:])) + route_target_list = list(map(str, rt)) + route_origin_list = list(map(str, soo)) + route_target_list.sort() + route_origin_list.sort() + + if route_target_list and len(route_target_list) > 0: + result['members']['route_target'] = route_target_list + + if route_origin_list and len(route_origin_list) > 0: + result['members']['route_origin'] = route_origin_list bgp_extcommunities_configs.append(result) @@ -142,17 +145,5 @@ class Bgp_ext_communitiesFacts(object): :rtype: dictionary :returns: The generated config """ - config = deepcopy(spec) - try: - config['name'] = str(conf['name']) - config['members'] = conf['members'] - config['match'] = conf['match'] - config['type'] = conf['type'] - config['permit'] = conf['permit'] - except TypeError: - config['name'] = None - config['members'] = None - config['match'] = None - config['type'] = None - config['permit'] = None - return utils.remove_empties(config) + + return conf diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors/bgp_neighbors.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors/bgp_neighbors.py index 903b93de1..687420991 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors/bgp_neighbors.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors/bgp_neighbors.py @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -110,8 +109,8 @@ class Bgp_neighborsFacts(object): ansible_facts['ansible_network_resources'].pop('bgp_neighbors', None) facts = {} if objs: - params = utils.validate_config(self.argument_spec, {'config': remove_empties_from_list(objs)}) - facts['bgp_neighbors'] = params['config'] + params = utils.validate_config(self.argument_spec, {'config': objs}) + facts['bgp_neighbors'] = remove_empties_from_list(params['config']) ansible_facts['ansible_network_resources'].update(facts) return ansible_facts diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors_af/bgp_neighbors_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors_af/bgp_neighbors_af.py index 26119b61c..8f034bb2a 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors_af/bgp_neighbors_af.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors_af/bgp_neighbors_af.py @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -121,7 +120,6 @@ class Bgp_neighbors_afFacts(object): ipv4_unicast = norm_nei_af.get('ipv4_unicast', None) ipv6_unicast = norm_nei_af.get('ipv6_unicast', None) - l2vpn_evpn = norm_nei_af.get('l2vpn_evpn', None) if ipv4_unicast: if 'config' in ipv4_unicast: ip_afi = update_bgp_nbr_pg_ip_afi_dict(ipv4_unicast['config']) @@ -142,12 +140,6 @@ class Bgp_neighbors_afFacts(object): if prefix_limit: norm_nei_af['prefix_limit'] = prefix_limit norm_nei_af.pop('ipv6_unicast') - elif l2vpn_evpn: - if 'config' in l2vpn_evpn: - prefix_limit = update_bgp_nbr_pg_prefix_limit_dict(l2vpn_evpn['config']) - if prefix_limit: - norm_nei_af['prefix_limit'] = prefix_limit - norm_nei_af.pop('l2vpn_evpn') norm_neighbor_afs.append(norm_nei_af) if norm_neighbor_afs: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/copp/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/copp/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/copp/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/copp/copp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/copp/copp.py new file mode 100644 index 000000000..52a01d3d1 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/copp/copp.py @@ -0,0 +1,127 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic copp fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.copp.copp import CoppArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) + + +class CoppFacts(object): + """ The sonic copp fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = CoppArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for bfd + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + objs = [] + + if not data: + copp_cfg = self.get_copp_config(self._module) + data = self.update_copp_groups(copp_cfg) + objs = self.render_config(self.generated_spec, data) + facts = {} + if objs: + params = utils.validate_config(self.argument_spec, {'config': objs}) + facts['copp'] = params['config'] + ansible_facts['ansible_network_resources'].update(facts) + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + return conf + + def update_copp_groups(self, data): + config_dict = {} + all_copp_groups = [] + if data: + copp_groups = data.get('copp-groups', None) + if copp_groups: + copp_group_list = copp_groups.get('copp-group', None) + if copp_group_list: + for group in copp_group_list: + group_dict = {} + copp_name = group['name'] + config = group['config'] + trap_priority = config.get('trap-priority', None) + trap_action = config.get('trap-action', None) + queue = config.get('queue', None) + cir = config.get('cir', None) + cbs = config.get('cbs', None) + + if copp_name: + group_dict['copp_name'] = copp_name + if trap_priority: + group_dict['trap_priority'] = trap_priority + if trap_action: + group_dict['trap_action'] = trap_action + if queue: + group_dict['queue'] = queue + if cir: + group_dict['cir'] = cir + if cbs: + group_dict['cbs'] = cbs + if group_dict: + all_copp_groups.append(group_dict) + if all_copp_groups: + config_dict['copp_groups'] = all_copp_groups + + return config_dict + + def get_copp_config(self, module): + copp_cfg = None + get_copp_path = '/data/openconfig-copp-ext:copp' + request = {'path': get_copp_path, 'method': 'get'} + + try: + response = edit_config(module, to_request(module, request)) + if 'openconfig-copp-ext:copp' in response[0][1]: + copp_cfg = response[0][1].get('openconfig-copp-ext:copp', None) + except ConnectionError as exc: + module.fail_json(msg=str(exc), code=exc.code) + return copp_cfg diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/dhcp_relay/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/dhcp_relay/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/dhcp_relay/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/dhcp_relay/dhcp_relay.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/dhcp_relay/dhcp_relay.py new file mode 100644 index 000000000..70f78dc24 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/dhcp_relay/dhcp_relay.py @@ -0,0 +1,208 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic dhcp_relay fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.dhcp_relay.dhcp_relay import Dhcp_relayArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible.module_utils.connection import ConnectionError + +SELECT_VALUE_TO_BOOL = { + 'ENABLE': True, + 'DISABLE': False +} + + +class Dhcp_relayFacts(object): + """ The sonic dhcp_relay fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = Dhcp_relayArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for dhcp_relay + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if connection: # just for linting purposes, remove + pass + + if not data: + dhcp_relay_configs = self.get_dhcp_relay() + dhcpv6_relay_configs = self.get_dhcpv6_relay() + + all_relay_configs = {} + for intf_name, dhcp_relay_config in dhcp_relay_configs.items(): + all_relay_configs[intf_name] = {} + all_relay_configs[intf_name]['ipv4'] = dhcp_relay_config + + for intf_name, dhcpv6_relay_config in dhcpv6_relay_configs.items(): + if all_relay_configs.get(intf_name): + all_relay_configs[intf_name]['ipv6'] = dhcpv6_relay_config + else: + all_relay_configs[intf_name] = {} + all_relay_configs[intf_name]['ipv6'] = dhcpv6_relay_config + + objs = [] + for relay_config in all_relay_configs.items(): + obj = self.render_config(self.generated_spec, relay_config) + if obj: + objs.append(obj) + + ansible_facts['ansible_network_resources'].pop('dhcp_relay', None) + facts = {} + if objs: + params = utils.validate_config(self.argument_spec, {'config': objs}) + facts['dhcp_relay'] = utils.remove_empties({'config': params['config']})['config'] + + ansible_facts['ansible_network_resources'].update(facts) + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + config['name'] = conf[0] + + if conf[1].get('ipv4'): + ipv4_dict = conf[1]['ipv4'] + if ipv4_dict.get('policy_action'): + ipv4_dict['policy_action'] = ipv4_dict['policy_action'].lower() + + ipv4_dict['link_select'] = SELECT_VALUE_TO_BOOL.get(ipv4_dict['link_select']) + ipv4_dict['vrf_select'] = SELECT_VALUE_TO_BOOL.get(ipv4_dict['vrf_select']) + + config['ipv4'] = ipv4_dict + else: + config.pop('ipv4') + + if conf[1].get('ipv6'): + ipv6_dict = conf[1]['ipv6'] + ipv6_dict['vrf_select'] = SELECT_VALUE_TO_BOOL.get(ipv6_dict['vrf_select']) + + config['ipv6'] = ipv6_dict + else: + config.pop('ipv6') + + return config + + def get_dhcp_relay(self): + """Get all DHCP relay configurations available in chassis""" + dhcp_relay_interfaces_path = 'data/openconfig-relay-agent:relay-agent/dhcp' + method = 'GET' + request = [{'path': dhcp_relay_interfaces_path, 'method': method}] + + try: + response = edit_config(self._module, to_request(self._module, request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + dhcp_relay_interfaces = [] + if (response[0][1].get('openconfig-relay-agent:dhcp') + and response[0][1]['openconfig-relay-agent:dhcp'].get('interfaces')): + dhcp_relay_interfaces = response[0][1]['openconfig-relay-agent:dhcp']['interfaces'].get('interface', []) + + dhcp_relay_configs = {} + for interface in dhcp_relay_interfaces: + ipv4_dict = {} + server_addresses = [] + + config = interface.get('config', {}) + for address in config.get('helper-address', []): + temp = {} + temp['address'] = address + server_addresses.append(temp) + ipv4_dict['server_addresses'] = server_addresses + + ipv4_dict['max_hop_count'] = config.get('openconfig-relay-agent-ext:max-hop-count') + ipv4_dict['policy_action'] = config.get('openconfig-relay-agent-ext:policy-action') + ipv4_dict['source_interface'] = config.get('openconfig-relay-agent-ext:src-intf') + ipv4_dict['vrf_name'] = config.get('openconfig-relay-agent-ext:vrf') + + opt_config = interface.get('agent-information-option', {}).get('config', {}) + ipv4_dict['circuit_id'] = opt_config.get('circuit-id') + ipv4_dict['link_select'] = opt_config.get('openconfig-relay-agent-ext:link-select') + ipv4_dict['vrf_select'] = opt_config.get('openconfig-relay-agent-ext:vrf-select') + + dhcp_relay_configs[interface['id']] = ipv4_dict + + return dhcp_relay_configs + + def get_dhcpv6_relay(self): + """Get all DHCPv6 relay configurations available in chassis""" + dhcpv6_relay_interfaces_path = 'data/openconfig-relay-agent:relay-agent/dhcpv6' + method = 'GET' + request = [{'path': dhcpv6_relay_interfaces_path, 'method': method}] + + try: + response = edit_config(self._module, to_request(self._module, request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + dhcpv6_relay_interfaces = [] + if (response[0][1].get('openconfig-relay-agent:dhcpv6') + and response[0][1]['openconfig-relay-agent:dhcpv6'].get('interfaces')): + dhcpv6_relay_interfaces = response[0][1]['openconfig-relay-agent:dhcpv6']['interfaces'].get('interface', []) + + dhcpv6_relay_configs = {} + for interface in dhcpv6_relay_interfaces: + ipv6_dict = {} + server_addresses = [] + + config = interface.get('config', {}) + for address in config.get('helper-address', []): + temp = {} + temp['address'] = address + server_addresses.append(temp) + ipv6_dict['server_addresses'] = server_addresses + + ipv6_dict['max_hop_count'] = config.get('openconfig-relay-agent-ext:max-hop-count') + ipv6_dict['source_interface'] = config.get('openconfig-relay-agent-ext:src-intf') + ipv6_dict['vrf_name'] = config.get('openconfig-relay-agent-ext:vrf') + + opt_config = interface.get('options', {}).get('config', {}) + ipv6_dict['vrf_select'] = opt_config.get('openconfig-relay-agent-ext:vrf-select') + + dhcpv6_relay_configs[interface['id']] = ipv6_dict + + return dhcpv6_relay_configs diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/dhcp_snooping/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/dhcp_snooping/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/dhcp_snooping/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/dhcp_snooping/dhcp_snooping.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/dhcp_snooping/dhcp_snooping.py new file mode 100644 index 000000000..c0464c8f1 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/dhcp_snooping/dhcp_snooping.py @@ -0,0 +1,213 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic dhcp_snooping fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.dhcp_snooping.dhcp_snooping import Dhcp_snoopingArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible.module_utils.connection import ConnectionError + + +class Dhcp_snoopingFacts(object): + """ The sonic dhcp_snooping fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = Dhcp_snoopingArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for dhcp_snooping + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if connection: # just for linting purposes, remove + pass + + if not data: + data = self.get_dhcp_snooping() + + obj = self.render_config(self.generated_spec, data) + + ansible_facts['ansible_network_resources'].pop('dhcp_snooping', None) + facts = {} + if obj: + params = utils.validate_config(self.argument_spec, {'config': obj}) + params_cleaned = {'config': utils.remove_empties(params['config'])} + facts['dhcp_snooping'] = params_cleaned['config'] + + ansible_facts['ansible_network_resources'].update(facts) + return ansible_facts + + def get_dhcp_snooping(self): + config = {} + + config['top_level'] = self.get_dhcp_snooping_top_level() + config['binding'] = self.get_dhcp_snooping_binding() + + return config + + def get_dhcp_snooping_top_level(self): + """Get all DHCP snooping configurations available in chassis""" + dhcp_snooping_path = 'data/openconfig-dhcp-snooping:dhcp-snooping' + method = 'GET' + request = [{'path': dhcp_snooping_path, 'method': method}] + + try: + response = edit_config(self._module, to_request(self._module, request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + config = {} + if (response[0][1].get('openconfig-dhcp-snooping:dhcp-snooping')): + config = response[0][1].get('openconfig-dhcp-snooping:dhcp-snooping') + + return config + + def get_dhcp_snooping_binding(self): + dhcp_binding_snooping_path = 'data/openconfig-dhcp-snooping:dhcp-snooping-binding' + method = 'GET' + request = [{'path': dhcp_binding_snooping_path, 'method': method}] + + try: + response = edit_config(self._module, to_request(self._module, request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + config = {} + if (response[0][1].get('openconfig-dhcp-snooping:dhcp-snooping-binding')): + config = response[0][1].get('openconfig-dhcp-snooping:dhcp-snooping-binding') + + return config + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + + v4 = {'afi': 'ipv4'} + v6 = {'afi': 'ipv6'} + config['afis'] = [v4, v6] + + # Start with the top-level config from the device. + top_level = conf.get('top_level', {}) + + # Transform the "config" dict from the top-level device config. + deviceConfig = top_level.get('config', {}) + + v4_enabled = deviceConfig.get('dhcpv4-admin-enable', None) + if v4_enabled: + v4['enabled'] = True + else: + v4['enabled'] = False + v6_enabled = deviceConfig.get('dhcpv6-admin-enable', None) + if v6_enabled: + v6['enabled'] = True + else: + v6['enabled'] = False + + v4_verify_mac = deviceConfig.get('dhcpv4-verify-mac-address', None) + if v4_verify_mac is False: + v4['verify_mac'] = False + else: + v4['verify_mac'] = True + v6_verify_mac = deviceConfig.get('dhcpv6-verify-mac-address', None) + if v6_verify_mac is False: + v6['verify_mac'] = False + else: + v6['verify_mac'] = True + + # Transform the "state" dict from the top-level device config. + state = top_level.get('state', {}) + + v4_vlans = state.get('dhcpv4-snooping-vlan', []) + if len(v4_vlans) > 0: + v4['vlans'] = v4_vlans + v6_vlans = state.get('dhcpv6-snooping-vlan', []) + if len(v6_vlans) > 0: + v6['vlans'] = v6_vlans + + STANDARD_ETH = "Eth" + PC = 'PortChannel' + v4_trusted_intf = state.get('dhcpv4-trusted-intf', []) + if len(v4_trusted_intf) > 0: + v4['trusted'] = [] + for intfName in v4_trusted_intf: + intf = {} + if intfName.startswith(STANDARD_ETH) or intfName.startswith(PC): + intf['intf_name'] = intfName + else: + continue + v4['trusted'].append(intf) + v6_trusted_intf = state.get('dhcpv6-trusted-intf', []) + if len(v6_trusted_intf) > 0: + v6['trusted'] = [] + for intfName in v6_trusted_intf: + intf = {} + if intfName.startswith(STANDARD_ETH) or intfName.startswith(PC): + intf['intf_name'] = intfName + else: + continue + v6['trusted'].append(intf) + + # Transform the binding config from the device. + binding = conf.get('binding', {}) + binding_list_container = binding.get('dhcp-snooping-binding-entry-list', {}) + binding_list = binding_list_container.get('dhcp-snooping-binding-list', []) + if len(binding_list) > 0: + v4_entries = [] + v6_entries = [] + for entry in binding_list: + binding = {} + binding['mac_addr'] = entry['mac'] + binding['ip_addr'] = entry['state']['ipaddress'] + binding['intf_name'] = entry['state']['intf'] + binding['vlan_id'] = entry['state']['vlan'] + if entry['iptype'] == 'ipv4': + v4_entries.append(binding) + elif entry['iptype'] == 'ipv6': + v6_entries.append(binding) + if len(v4_entries) > 0: + v4['source_bindings'] = v4_entries + if len(v6_entries) > 0: + v6['source_bindings'] = v6_entries + + return config diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/facts.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/facts.py index 75622632a..dbe597448 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/facts.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/facts.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -32,6 +32,7 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.mclag.mclag import MclagFacts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.prefix_lists.prefix_lists import Prefix_listsFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.vlan_mapping.vlan_mapping import Vlan_mappingFacts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.vrfs.vrfs import VrfsFacts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.vxlans.vxlans import VxlansFacts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.users.users import UsersFacts @@ -42,6 +43,21 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.radius_server.radius_server import Radius_serverFacts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.static_routes.static_routes import Static_routesFacts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.ntp.ntp import NtpFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.logging.logging import LoggingFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.pki.pki import PkiFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.ip_neighbor.ip_neighbor import Ip_neighborFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.port_group.port_group import Port_groupFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.dhcp_relay.dhcp_relay import Dhcp_relayFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.dhcp_snooping.dhcp_snooping import Dhcp_snoopingFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.acl_interfaces.acl_interfaces import Acl_interfacesFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.l2_acls.l2_acls import L2_aclsFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.l3_acls.l3_acls import L3_aclsFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.lldp_global.lldp_global import Lldp_globalFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.mac.mac import MacFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.bfd.bfd import BfdFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.copp.copp import CoppFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.route_maps.route_maps import Route_mapsFacts +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.stp.stp import StpFacts FACT_LEGACY_SUBSETS = {} FACT_RESOURCE_SUBSETS = dict( @@ -59,6 +75,7 @@ FACT_RESOURCE_SUBSETS = dict( bgp_ext_communities=Bgp_ext_communitiesFacts, mclag=MclagFacts, prefix_lists=Prefix_listsFacts, + vlan_mapping=Vlan_mappingFacts, vrfs=VrfsFacts, vxlans=VxlansFacts, users=UsersFacts, @@ -69,6 +86,21 @@ FACT_RESOURCE_SUBSETS = dict( radius_server=Radius_serverFacts, static_routes=Static_routesFacts, ntp=NtpFacts, + logging=LoggingFacts, + pki=PkiFacts, + ip_neighbor=Ip_neighborFacts, + port_group=Port_groupFacts, + dhcp_relay=Dhcp_relayFacts, + dhcp_snooping=Dhcp_snoopingFacts, + acl_interfaces=Acl_interfacesFacts, + l2_acls=L2_aclsFacts, + l3_acls=L3_aclsFacts, + lldp_global=Lldp_globalFacts, + mac=MacFacts, + bfd=BfdFacts, + copp=CoppFacts, + route_maps=Route_mapsFacts, + stp=StpFacts ) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/interfaces/interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/interfaces/interfaces.py index a36b5d3c0..7ce15fe1b 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/interfaces/interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/interfaces/interfaces.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# © Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -59,6 +58,7 @@ class InterfacesFacts(object): if "openconfig-interfaces:interfaces" in response[0][1]: all_interfaces = response[0][1].get("openconfig-interfaces:interfaces", {}) + return all_interfaces['interface'] def populate_facts(self, connection, ansible_facts, data=None): @@ -94,8 +94,8 @@ class InterfacesFacts(object): if objs: facts['interfaces'] = [] params = utils.validate_config(self.argument_spec, {'config': objs}) - if params: - facts['interfaces'].extend(params['config']) + for cfg in params['config']: + facts['interfaces'].append(utils.remove_empties(cfg)) ansible_facts['ansible_network_resources'].update(facts) return ansible_facts @@ -115,7 +115,7 @@ class InterfacesFacts(object): def transform_config(self, conf): exist_cfg = conf['config'] - trans_cfg = None + trans_cfg = dict() is_loop_back = False name = conf['name'] @@ -125,16 +125,29 @@ class InterfacesFacts(object): if pos > 0: name = name[0:pos] - if not (is_loop_back and self.is_loop_back_already_esist(name)) and (name != "eth0"): - trans_cfg = dict() + if not (is_loop_back and self.is_loop_back_already_exist(name)) and (name != "eth0") and (name != "Management0"): trans_cfg['name'] = name if is_loop_back: self.update_loop_backs(name) else: trans_cfg['enabled'] = exist_cfg['enabled'] if exist_cfg.get('enabled') is not None else True - trans_cfg['description'] = exist_cfg['description'] if exist_cfg.get('description') else "" + trans_cfg['description'] = exist_cfg.get('description') trans_cfg['mtu'] = exist_cfg['mtu'] if exist_cfg.get('mtu') else 9100 + if name.startswith('Eth') and 'openconfig-if-ethernet:ethernet' in conf: + if conf['openconfig-if-ethernet:ethernet'].get('config', None): + eth_conf = conf['openconfig-if-ethernet:ethernet']['config'] + if 'auto-negotiate' in eth_conf: + trans_cfg['auto_negotiate'] = eth_conf['auto-negotiate'] + trans_cfg['speed'] = eth_conf['port-speed'].split(':', 1)[-1] + if 'openconfig-if-ethernet-ext2:advertised-speed' in eth_conf: + adv_speed_str = eth_conf['openconfig-if-ethernet-ext2:advertised-speed'] + if adv_speed_str != '': + trans_cfg['advertised_speed'] = adv_speed_str.split(",") + trans_cfg['advertised_speed'].sort() + if 'openconfig-if-ethernet-ext2:port-fec' in eth_conf: + trans_cfg['fec'] = eth_conf['openconfig-if-ethernet-ext2:port-fec'].split(':', 1)[-1] + return trans_cfg def reset_loop_backs(self): @@ -143,5 +156,5 @@ class InterfacesFacts(object): def update_loop_backs(self, loop_back): self.loop_backs += "{0},".format(loop_back) - def is_loop_back_already_esist(self, loop_back): + def is_loop_back_already_exist(self, loop_back): return (",{0},".format(loop_back) in self.loop_backs) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ip_neighbor/ip_neighbor.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ip_neighbor/ip_neighbor.py new file mode 100644 index 000000000..4c077c43f --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ip_neighbor/ip_neighbor.py @@ -0,0 +1,126 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic ip_neighbor fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import re +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.ip_neighbor.ip_neighbor import Ip_neighborArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible.module_utils.connection import ConnectionError + +GET = "get" + + +class Ip_neighborFacts(object): + """ The sonic ip_neighbor fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = Ip_neighborArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for ip_neighbor + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + # typically data is populated from the current device configuration + # data = connection.get('show running-config | section neighbor') + # using mock data instead + data = self.get_ip_neighbor_global() + + objs = self.render_config(self.generated_spec, data) + + ansible_facts['ansible_network_resources'].pop('ip_neighbor', None) + + facts = {} + if objs: + params = utils.validate_config(self.argument_spec, {'config': objs}) + facts['ip_neighbor'] = params['config'] + + ansible_facts['ansible_network_resources'].update(facts) + + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + return conf + + def get_ip_neighbor_global(self): + """Get IP neighbor global configurations""" + + config_path = "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config" + config_request = [{"path": config_path, "method": GET}] + config_response = [] + + ip_neigh_glb_conf = dict() + + try: + config_response = edit_config(self._module, to_request(self._module, config_request)) + except ConnectionError as exc: + if re.search("code.*404", str(exc)): + # 'code': 404, 'error-message': 'Resource not found' + return ip_neigh_glb_conf + else: + self._module.fail_json(msg=str(exc), code=exc.code) + + config = dict() + if 'openconfig-neighbor:config' in config_response[0][1]: + config = config_response[0][1].get('openconfig-neighbor:config', {}) + + if "ipv4-arp-timeout" in config: + ip_neigh_glb_conf["ipv4_arp_timeout"] = config["ipv4-arp-timeout"] + + if "ipv4-drop-neighbor-aging-time" in config: + ip_neigh_glb_conf["ipv4_drop_neighbor_aging_time"] = config["ipv4-drop-neighbor-aging-time"] + + if "ipv6-drop-neighbor-aging-time" in config: + ip_neigh_glb_conf["ipv6_drop_neighbor_aging_time"] = config["ipv6-drop-neighbor-aging-time"] + + if "ipv6-nd-cache-expiry" in config: + ip_neigh_glb_conf["ipv6_nd_cache_expiry"] = config["ipv6-nd-cache-expiry"] + + if "num-local-neigh" in config: + ip_neigh_glb_conf["num_local_neigh"] = config["num-local-neigh"] + + return ip_neigh_glb_conf diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_acls/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_acls/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_acls/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_acls/l2_acls.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_acls/l2_acls.py new file mode 100644 index 000000000..5644cf876 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_acls/l2_acls.py @@ -0,0 +1,236 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic l2_acls fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils.connection import ConnectionError +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.l2_acls.l2_acls import L2_aclsArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) + +ETHERTYPE_FORMAT = '0x{:04x}' + +action_payload_to_value_map = { + 'ACCEPT': 'permit', + 'DISCARD': 'discard', + 'DO_NOT_NAT': 'do-not-nat', + 'DROP': 'deny', + 'TRANSIT': 'transit', +} +ethertype_payload_to_protocol_map = { + '0x0800': 'ipv4', + '0x0806': 'arp', + '0x86dd': 'ipv6', + 'ETHERTYPE_ARP': 'arp', + 'ETHERTYPE_IPV4': 'ipv4', + 'ETHERTYPE_IPV6': 'ipv6' +} +ethertype_payload_to_value_map = { + 'ETHERTYPE_LLDP': '0x88cc', + 'ETHERTYPE_MPLS': '0x8847', + 'ETHERTYPE_ROCE': '0x8915' +} +pcp_value_to_traffic_map = { + 0: 'be', + 1: 'bk', + 2: 'ee', + 3: 'ca', + 4: 'vi', + 5: 'vo', + 6: 'ic', + 7: 'nc' +} + + +class L2_aclsFacts(object): + """ The sonic l2_acls fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = L2_aclsArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for l2_acls + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if connection: # just for linting purposes, remove + pass + + if not data: + l2_acls_configs = self.get_l2_acls() + + objs = [] + for l2_acl_config in l2_acls_configs: + obj = self.render_config(self.generated_spec, l2_acl_config) + if obj: + objs.append(obj) + + ansible_facts['ansible_network_resources'].pop('l2_acls', None) + facts = {} + if objs: + params = utils.validate_config(self.argument_spec, {'config': objs}) + facts['l2_acls'] = utils.remove_empties({'config': params['config']})['config'] + + ansible_facts['ansible_network_resources'].update(facts) + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + config['name'] = conf['name'] + config['remark'] = conf['remark'] + config['rules'] = conf['rules'] + + for rule in config['rules']: + if ":" in rule['action']: + rule['action'] = rule['action'].split(":")[-1] + rule['action'] = action_payload_to_value_map[rule['action']] + + rule['source'] = {} + rule['destination'] = {} + if rule.get('l2') is None: + rule['source']['any'] = True + rule['destination']['any'] = True + continue + + l2_config = rule.pop('l2') + if l2_config.get('source-mac') and l2_config.get('source-mac-mask'): + if l2_config['source-mac-mask'].lower() == 'ff:ff:ff:ff:ff:ff': + rule['source']['host'] = l2_config['source-mac'].lower() + else: + rule['source']['address'] = l2_config['source-mac'].lower() + rule['source']['address_mask'] = l2_config['source-mac-mask'].lower() + elif l2_config.get('source-mac'): + rule['source']['host'] = l2_config['source-mac'].lower() + else: + rule['source']['any'] = True + + if l2_config.get('destination-mac') and l2_config.get('destination-mac-mask'): + if l2_config['destination-mac-mask'].lower() == 'ff:ff:ff:ff:ff:ff': + rule['destination']['host'] = l2_config['destination-mac'].lower() + else: + rule['destination']['address'] = l2_config['destination-mac'].lower() + rule['destination']['address_mask'] = l2_config['destination-mac-mask'].lower() + elif l2_config.get('destination-mac'): + rule['destination']['host'] = l2_config['destination-mac'].lower() + else: + rule['destination']['any'] = True + + if l2_config.get('ethertype'): + ethertype = l2_config['ethertype'] + rule['ethertype'] = {} + if isinstance(ethertype, str): + ethertype = ethertype.split(':')[-1] + if ethertype in ethertype_payload_to_protocol_map: + rule['ethertype'][ethertype_payload_to_protocol_map[ethertype]] = True + else: + rule['ethertype']['value'] = ethertype_payload_to_value_map[ethertype] + else: + ethertype = ETHERTYPE_FORMAT.format(ethertype) + if ethertype in ethertype_payload_to_protocol_map: + rule['ethertype'][ethertype_payload_to_protocol_map[ethertype]] = True + else: + rule['ethertype']['value'] = ethertype + + if l2_config.get('openconfig-acl-ext:vlanid'): + rule['vlan_id'] = l2_config['openconfig-acl-ext:vlanid'] + if l2_config.get('openconfig-acl-ext:vlan-tag-format') == 'openconfig-acl-ext:MULTI_TAGGED': + rule['vlan_tag_format'] = {'multi_tagged': True} + + if l2_config.get('openconfig-acl-ext:dei') is not None: + rule['dei'] = l2_config['openconfig-acl-ext:dei'] + + if l2_config.get('openconfig-acl-ext:pcp') is not None: + rule['pcp'] = {} + if l2_config.get('openconfig-acl-ext:pcp-mask') is not None: + rule['pcp']['value'] = l2_config['openconfig-acl-ext:pcp'] + rule['pcp']['mask'] = l2_config['openconfig-acl-ext:pcp-mask'] + else: + rule['pcp']['traffic_type'] = pcp_value_to_traffic_map[l2_config['openconfig-acl-ext:pcp']] + + return config + + def get_l2_acls(self): + """Get all l2 acl configurations available in chassis""" + acls_path = 'data/openconfig-acl:acl/acl-sets' + method = 'GET' + request = [{'path': acls_path, 'method': method}] + + try: + response = edit_config(self._module, to_request(self._module, request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + acls = [] + if response[0][1].get('openconfig-acl:acl-sets'): + acls = response[0][1]['openconfig-acl:acl-sets'].get('acl-set', []) + + l2_acls_configs = [] + for acl in acls: + acl_config = {} + acl_rules = [] + + config = acl['config'] + if config.get('type') not in ('ACL_L2', 'openconfig-acl:ACL_L2'): + continue + + acl_config['name'] = config['name'] + acl_config['remark'] = config.get('description') + acl_config['rules'] = acl_rules + + acl_entries = acl.get('acl-entries', {}).get('acl-entry', []) + for acl_entry in acl_entries: + acl_rule = {} + + acl_entry_config = acl_entry['config'] + acl_rule['sequence_num'] = acl_entry_config['sequence-id'] + acl_rule['remark'] = acl_entry_config.get('description') + + acl_rule['action'] = acl_entry['actions']['config']['forwarding-action'] + acl_rule['l2'] = acl_entry.get('l2', {}).get('config', {}) + + acl_rules.append(acl_rule) + + l2_acls_configs.append(acl_config) + + return l2_acls_configs diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_interfaces/l2_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_interfaces/l2_interfaces.py index 07d7f97dd..78d5b002e 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_interfaces/l2_interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_interfaces/l2_interfaces.py @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -47,8 +46,8 @@ class L2_interfacesFacts(object): self.generated_spec = utils.generate_dict(facts_argument_spec) - def vlan_range_to_list(self, in_range): - range_bounds = in_range.split('-') + def vlan_range_to_list(self, in_range, range_str): + range_bounds = in_range.split(range_str) range_bottom = int(range_bounds[0]) range_top = int(range_bounds[1]) + 1 vlan_list = list(range(range_bottom, range_top)) @@ -79,15 +78,22 @@ class L2_interfacesFacts(object): new_det['trunk'] = {} new_det['trunk']['allowed_vlans'] = [] - # Save trunk vlans as a list of single vlan dicts: Convert - # any ranges to lists of individual vlan dicts and merge - # each resulting "range list" onto the main list for the - # interface. + # Save trunk vlans and vlan ranges as a list of single vlan dicts: + # Convert single vlan values to strings and convert any ranges + # to the argspec range format. (This block assumes that any string + # value received is a range, using either ".." or "-" as a + # separator between the boundaries of the range. It also assumes + # that any non-string value received is an integer specifying a + # single vlan.) for vlan in open_cfg_vlan['config'].get('trunk-vlans'): - if isinstance(vlan, str) and '-' in vlan: - new_det['trunk']['allowed_vlans'].extend(self.vlan_range_to_list(vlan)) + vlan_argspec = '' + if isinstance(vlan, str): + vlan_argspec = vlan.replace('"', '') + if '..' in vlan_argspec: + vlan_argspec = vlan_argspec.replace('..', '-') else: - new_det['trunk']['allowed_vlans'].append({'vlan': vlan}) + vlan_argspec = str(vlan) + new_det['trunk']['allowed_vlans'].append({'vlan': vlan_argspec}) l2_interfaces.append(new_det) return l2_interfaces diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_acls/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_acls/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_acls/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_acls/l3_acls.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_acls/l3_acls.py new file mode 100644 index 000000000..799064c9b --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_acls/l3_acls.py @@ -0,0 +1,322 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic l3_acls fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils.connection import ConnectionError +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.l3_acls.l3_acls import L3_aclsArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) + +IPV4_HOST_MASK = '/32' +IPV6_HOST_MASK = '/128' +L4_PORT_START = 0 +L4_PORT_END = 65535 + +action_payload_to_value_map = { + 'ACCEPT': 'permit', + 'DISCARD': 'discard', + 'DO_NOT_NAT': 'do-not-nat', + 'DROP': 'deny', + 'TRANSIT': 'transit', +} +protocol_payload_to_value_map = { + 'IP_ICMP': 'icmp', + 'IP_IGMP': 2, + 'IP_TCP': 'tcp', + 'IP_UDP': 'udp', + 'IP_RSVP': 46, + 'IP_GRE': 47, + 'IP_AUTH': 51, + 'IP_PIM': 103, + 'IP_L2TP': 115 +} +protocol_number_to_name_map = { + 1: 'icmp', + 6: 'tcp', + 17: 'udp', + 58: 'icmpv6' +} +dscp_value_to_name_map = { + 0: 'default', + 8: 'cs1', + 16: 'cs2', + 24: 'cs3', + 32: 'cs4', + 40: 'cs5', + 48: 'cs6', + 56: 'cs7', + 10: 'af11', + 12: 'af12', + 14: 'af13', + 18: 'af21', + 20: 'af22', + 22: 'af23', + 26: 'af31', + 28: 'af32', + 30: 'af33', + 34: 'af41', + 36: 'af42', + 38: 'af43', + 46: 'ef', + 44: 'voice_admit' +} + + +class L3_aclsFacts(object): + """ The sonic l3_acls fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = L3_aclsArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for l3_acls + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if connection: # just for linting purposes, remove + pass + + if not data: + l3_acls_configs = self.get_l3_acls() + + objs = [] + for l3_acl_config in l3_acls_configs: + obj = self.render_config(self.generated_spec, l3_acl_config) + if obj: + objs.append(obj) + + ansible_facts['ansible_network_resources'].pop('l3_acls', None) + facts = {} + if objs: + params = utils.validate_config(self.argument_spec, {'config': objs}) + facts['l3_acls'] = utils.remove_empties({'config': params['config']})['config'] + + ansible_facts['ansible_network_resources'].update(facts) + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + config['address_family'] = conf['address_family'] + config['acls'] = conf['acls'] + is_ipv4 = bool(config['address_family'] == 'ipv4') + + for acl in config['acls']: + for rule in acl['rules']: + rule['source'] = {} + rule['destination'] = {} + rule['protocol'] = {} + rule['protocol_options'] = {} + + if ":" in rule['action']: + rule['action'] = rule['action'].split(":")[-1] + rule['action'] = action_payload_to_value_map[rule['action']] + + l2_config = rule.pop('l2', None) + l3_config = rule.pop('l3', None) + l4_config = rule.pop('l4', None) + if l3_config is None: + if is_ipv4: + rule['protocol']['name'] = 'ip' + else: + rule['protocol']['name'] = 'ipv6' + + rule['source']['any'] = True + rule['destination']['any'] = True + continue + + protocol = l3_config.get('protocol') + if protocol is not None: + if isinstance(protocol, str): + protocol = protocol.replace('openconfig-packet-match-types:', '') + protocol = protocol_payload_to_value_map[protocol] + if isinstance(protocol, str): + rule['protocol']['name'] = protocol + else: + rule['protocol']['number'] = protocol + else: + protocol = protocol_number_to_name_map.get(protocol, protocol) + if isinstance(protocol, str): + rule['protocol']['name'] = protocol + else: + rule['protocol']['number'] = protocol + else: + if is_ipv4: + rule['protocol']['name'] = 'ip' + else: + rule['protocol']['name'] = 'ipv6' + + rule['source'] = self._convert_ip_addr_to_spec_fmt(l3_config.get('source-address'), is_ipv4) + rule['destination'] = self._convert_ip_addr_to_spec_fmt(l3_config.get('destination-address'), is_ipv4) + if protocol in ('tcp', 'udp'): + rule['source']['port_number'] = self._convert_l4_port_to_spec_fmt(l4_config.get('source-port')) + rule['destination']['port_number'] = self._convert_l4_port_to_spec_fmt(l4_config.get('destination-port')) + + if protocol in ('icmp', 'icmpv6'): + rule['protocol_options'][protocol] = { + 'code': l4_config.get('openconfig-acl-ext:icmp-code'), + 'type': l4_config.get('openconfig-acl-ext:icmp-type') + } + elif protocol == 'tcp': + rule['protocol_options']['tcp'] = {} + if l4_config.get('openconfig-acl-ext:tcp-session-established'): + rule['protocol_options']['tcp']['established'] = True + else: + for flag in l4_config.get('tcp-flags', []): + flag = flag.split(':')[-1].replace('TCP_', '').lower() + rule['protocol_options']['tcp'][flag] = True + + dscp = l3_config.get('dscp') + if dscp in dscp_value_to_name_map: + rule['dscp'] = {dscp_value_to_name_map[dscp]: True} + else: + rule['dscp'] = {'value': dscp} + + rule['vlan_id'] = l2_config.get('openconfig-acl-ext:vlanid') + + return config + + def get_l3_acls(self): + """Get all l3 acl configurations available in chassis""" + acls_path = 'data/openconfig-acl:acl/acl-sets' + method = 'GET' + request = [{'path': acls_path, 'method': method}] + + try: + response = edit_config(self._module, to_request(self._module, request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + acls = [] + if response[0][1].get('openconfig-acl:acl-sets'): + acls = response[0][1]['openconfig-acl:acl-sets'].get('acl-set', []) + + ipv4_acls_configs = [] + ipv6_acls_configs = [] + for acl in acls: + is_ipv4 = False + acl_config = {} + acl_rules = [] + + config = acl['config'] + if config.get('type') in ('ACL_IPV4', 'openconfig-acl:ACL_IPV4'): + is_ipv4 = True + elif config.get('type') in ('ACL_IPV6', 'openconfig-acl:ACL_IPV6'): + is_ipv4 = False + else: + continue + + acl_config['name'] = config['name'] + acl_config['remark'] = config.get('description') + acl_config['rules'] = acl_rules + + acl_entries = acl.get('acl-entries', {}).get('acl-entry', []) + for acl_entry in acl_entries: + acl_rule = {} + + acl_entry_config = acl_entry['config'] + acl_rule['sequence_num'] = acl_entry_config['sequence-id'] + acl_rule['remark'] = acl_entry_config.get('description') + + acl_rule['action'] = acl_entry['actions']['config']['forwarding-action'] + acl_rule['l2'] = acl_entry.get('l2', {}).get('config', {}) + if is_ipv4: + acl_rule['l3'] = acl_entry.get('ipv4', {}).get('config', {}) + else: + acl_rule['l3'] = acl_entry.get('ipv6', {}).get('config', {}) + acl_rule['l4'] = acl_entry.get('transport', {}).get('config', {}) + + acl_rules.append(acl_rule) + + if is_ipv4: + ipv4_acls_configs.append(acl_config) + else: + ipv6_acls_configs.append(acl_config) + + l3_acls_configs = [] + if ipv4_acls_configs: + l3_acls_configs.append({'address_family': 'ipv4', 'acls': ipv4_acls_configs}) + if ipv6_acls_configs: + l3_acls_configs.append({'address_family': 'ipv6', 'acls': ipv6_acls_configs}) + + return l3_acls_configs + + @staticmethod + def _convert_ip_addr_to_spec_fmt(ip_addr, is_ipv4=False): + spec_fmt = {} + if ip_addr is not None: + ip_addr = ip_addr.lower() + if is_ipv4: + host_mask = IPV4_HOST_MASK + else: + host_mask = IPV6_HOST_MASK + + if ip_addr.endswith(host_mask): + spec_fmt['host'] = ip_addr.replace(host_mask, '') + else: + spec_fmt['prefix'] = ip_addr + else: + spec_fmt['any'] = True + + return spec_fmt + + @staticmethod + def _convert_l4_port_to_spec_fmt(l4_port): + spec_fmt = {} + if l4_port is not None: + if isinstance(l4_port, str) and '..' in l4_port: + l4_port = [int(i) for i in l4_port.split('..')] + if l4_port[0] == L4_PORT_START: + spec_fmt['lt'] = l4_port[1] + elif l4_port[1] == L4_PORT_END: + spec_fmt['gt'] = l4_port[0] + else: + spec_fmt['range'] = { + 'begin': l4_port[0], + 'end': l4_port[1] + } + else: + spec_fmt['eq'] = int(l4_port) + + return spec_fmt diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_interfaces/l3_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_interfaces/l3_interfaces.py index 69a6dcd44..e91a2b033 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_interfaces/l3_interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_interfaces/l3_interfaces.py @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lag_interfaces/lag_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lag_interfaces/lag_interfaces.py index 728196813..d83659d92 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lag_interfaces/lag_interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lag_interfaces/lag_interfaces.py @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lldp_global/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lldp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lldp_global/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lldp_global/lldp_global.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lldp_global/lldp_global.py new file mode 100644 index 000000000..75f3dad51 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lldp_global/lldp_global.py @@ -0,0 +1,114 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic lldp_global fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.lldp_global.lldp_global import Lldp_globalArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible.module_utils.connection import ConnectionError + + +GET = "get" + + +class Lldp_globalFacts(object): + """ The sonic lldp_global fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = Lldp_globalArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for lldp_global + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if connection: # just for linting purposes, remove + pass + + obj = self.get_all_lldp_configs() + + ansible_facts['ansible_network_resources'].pop('lldp_global', None) + facts = {} + if obj: + params = utils.validate_config(self.argument_spec, {'config': obj}) + facts['lldp_global'] = utils.remove_empties(params['config']) + + ansible_facts['ansible_network_resources'].update(facts) + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + return conf + + def get_all_lldp_configs(self): + """Get all the lldp_global configured in the device""" + request = [{"path": "data/openconfig-lldp:lldp/config", "method": GET}] + lldp_global_data = {} + try: + response = edit_config(self._module, to_request(self._module, request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + lldp_global_data['tlv_select'] = {} + lldp_global_data['tlv_select']['management_address'] = True + lldp_global_data['tlv_select']['system_capabilities'] = True + lldp_global_data['enable'] = True + if 'openconfig-lldp:config' in response[0][1]: + raw_lldp_global_data = response[0][1]['openconfig-lldp:config'] + if 'enabled' in raw_lldp_global_data: + lldp_global_data['enable'] = raw_lldp_global_data['enabled'] + if 'hello-timer' in raw_lldp_global_data: + lldp_global_data['hello_time'] = raw_lldp_global_data['hello-timer'] + if 'openconfig-lldp-ext:mode' in raw_lldp_global_data: + lldp_global_data['mode'] = raw_lldp_global_data['openconfig-lldp-ext:mode'].lower() + if 'system-description' in raw_lldp_global_data: + lldp_global_data['system_description'] = raw_lldp_global_data['system-description'] + if 'system-name' in raw_lldp_global_data: + lldp_global_data['system_name'] = raw_lldp_global_data['system-name'] + if 'openconfig-lldp-ext:multiplier' in raw_lldp_global_data: + lldp_global_data['multiplier'] = raw_lldp_global_data['openconfig-lldp-ext:multiplier'] + if 'suppress-tlv-advertisement' in raw_lldp_global_data: + for tlv_select in raw_lldp_global_data['suppress-tlv-advertisement']: + tlv_select = tlv_select.replace('openconfig-lldp-types:', '').lower() + if tlv_select in ('management_address', 'system_capabilities'): + lldp_global_data['tlv_select'][tlv_select] = False + return lldp_global_data diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/logging/logging.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/logging/logging.py new file mode 100644 index 000000000..c3c05035e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/logging/logging.py @@ -0,0 +1,128 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic logging fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.logging.logging import LoggingArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible.module_utils.connection import ConnectionError + +GET = "get" + + +class LoggingFacts(object): + """ The sonic logging fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = LoggingArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for logging + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + # typically data is populated from the current device configuration + # data = connection.get('show running-config | section ^interface') + # using mock data instead + data = self.get_logging_configuration() + + obj = self.render_config(self.generated_spec, data) + + ansible_facts['ansible_network_resources'].pop('logging', None) + facts = {} + if obj: + params = utils.validate_config(self.argument_spec, {'config': obj}) + facts['logging'] = params['config'] + + ansible_facts['ansible_network_resources'].update(facts) + + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + return conf + + def get_logging_configuration(self): + """Get all logging configuration""" + + config_request = [{"path": "data/openconfig-system:system/logging", "method": GET}] + config_response = [] + try: + config_response = edit_config(self._module, to_request(self._module, config_request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + logging_response = dict() + if 'openconfig-system:logging' in config_response[0][1]: + logging_response = config_response[0][1].get('openconfig-system:logging', {}) + + remote_servers = [] + if 'remote-servers' in logging_response: + remote_servers = logging_response['remote-servers'].get('remote-server', []) + + logging_config = dict() + + logging_servers = [] + for remote_server in remote_servers: + rs_config = remote_server.get('config', {}) + logging_server = {} + logging_server['host'] = rs_config['host'] + if 'openconfig-system-ext:message-type' in rs_config: + logging_server['message_type'] = rs_config['openconfig-system-ext:message-type'] + if 'openconfig-system-ext:source-interface' in rs_config: + logging_server['source_interface'] = rs_config['openconfig-system-ext:source-interface'] + if logging_server['source_interface'].startswith("Management") or \ + logging_server['source_interface'].startswith("Mgmt"): + logging_server['source_interface'] = 'eth0' + if 'openconfig-system-ext:vrf-name' in rs_config: + logging_server['vrf'] = rs_config['openconfig-system-ext:vrf-name'] + if 'remote-port' in rs_config: + logging_server['remote_port'] = rs_config['remote-port'] + + logging_servers.append(logging_server) + + logging_config['remote_servers'] = logging_servers + + return logging_config diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mac/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mac/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mac/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mac/mac.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mac/mac.py new file mode 100644 index 000000000..26f705040 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mac/mac.py @@ -0,0 +1,151 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic mac_address fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + remove_empties_from_list +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.mac.mac import MacArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils import ( + get_all_vrfs, +) + +NETWORK_INSTANCE_PATH = '/data/openconfig-network-instance:network-instances/network-instance' + + +class MacFacts(object): + """ The sonic mac fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = MacArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for mac_address + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + objs = [] + if connection: # just for linting purposes, remove + pass + + if not data: + data = self.update_mac(self._module) + # operate on a collection of resource x + for conf in data: + if conf: + obj = self.render_config(conf) + # split the config into instances of the resource + if obj: + objs.append(obj) + + ansible_facts['ansible_network_resources'].pop('mac', None) + facts = {} + if objs: + params = utils.validate_config(self.argument_spec, {'config': remove_empties_from_list(objs)}) + facts['mac'] = params['config'] + ansible_facts['ansible_network_resources'].update(facts) + return ansible_facts + + def render_config(self, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + return conf + + def update_mac(self, module): + mac_address_cfg_list = [] + vrfs = get_all_vrfs(module) + for vrf_name in vrfs: + aging_time = self.get_config(vrf_name, module, 'fdb/config/mac-aging-time', 'openconfig-network-instance:mac-aging-time') + dampening_cfg_dict = self.get_config(vrf_name, module, 'openconfig-mac-dampening:mac-dampening/config', 'openconfig-mac-dampening:config') + entries_dict = self.get_config(vrf_name, module, 'fdb/mac-table/entries', 'openconfig-network-instance:entries') + cfg_dict = {} + mac_dict = {} + mac_table_entries = [] + dampening_interval = dampening_cfg_dict.get('interval', None) + dampening_threshold = dampening_cfg_dict.get('threshold', None) + + if entries_dict: + entry_list = entries_dict.get('entry', []) + for entry in entry_list: + entry_dict = {} + mac_address = entry.get('mac-address', None) + vlan_id = entry.get('vlan', None) + interface = entry.get('interface', {}).get('interface-ref', {}).get('config', {}).get('interface', None) + if mac_address: + entry_dict['mac_address'] = mac_address + if vlan_id: + entry_dict['vlan_id'] = vlan_id + if interface: + entry_dict['interface'] = interface + if entry_dict: + mac_table_entries.append(entry_dict) + + if aging_time: + mac_dict['aging_time'] = aging_time + if dampening_interval: + mac_dict['dampening_interval'] = dampening_interval + if dampening_threshold: + mac_dict['dampening_threshold'] = dampening_threshold + if mac_table_entries: + mac_dict['mac_table_entries'] = mac_table_entries + if mac_dict: + cfg_dict['mac'] = mac_dict + cfg_dict['vrf_name'] = vrf_name + mac_address_cfg_list.append(cfg_dict) + + return mac_address_cfg_list + + def get_config(self, vrf_name, module, path, name): + cfg_dict = {} + get_path = '%s=%s/%s' % (NETWORK_INSTANCE_PATH, vrf_name, path) + request = {'path': get_path, 'method': 'get'} + + try: + response = edit_config(module, to_request(module, request)) + if name in response[0][1]: + cfg_dict = response[0][1].get(name, None) + except Exception as exc: + pass + + return cfg_dict diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mclag/mclag.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mclag/mclag.py index 69864cdf9..9c57f6cc0 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mclag/mclag.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mclag/mclag.py @@ -23,6 +23,9 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s to_request, edit_config ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + get_ranges_in_list +) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.mclag.mclag import MclagArgs from ansible.module_utils.connection import ConnectionError @@ -76,7 +79,7 @@ class MclagFacts(object): facts = {} if objs: params = utils.validate_config(self.argument_spec, {'config': objs}) - facts['mclag'] = params['config'] + facts['mclag'] = utils.remove_empties(params['config']) ansible_facts['ansible_network_resources'].update(facts) return ansible_facts @@ -118,6 +121,8 @@ class MclagFacts(object): config['peer_link'] = domain_config['peer-link'] if domain_config.get('mclag-system-mac', None): config['system_mac'] = domain_config['mclag-system-mac'] + if domain_config.get('delay-restore', None): + config['delay_restore'] = domain_config['delay-restore'] if conf.get('vlan-interfaces', None) and conf['vlan-interfaces'].get('vlan-interface', None): vlans_list = [] @@ -125,7 +130,15 @@ class MclagFacts(object): for vlan in vlan_data: vlans_list.append({'vlan': vlan['name']}) if vlans_list: - config['unique_ip'] = {'vlans': vlans_list} + config['unique_ip'] = {'vlans': self.get_vlan_range_list(vlans_list)} + + if conf.get('vlan-ifs', None) and conf['vlan-ifs'].get('vlan-if', None): + vlans_list = [] + vlan_data = conf['vlan-ifs']['vlan-if'] + for vlan in vlan_data: + vlans_list.append({'vlan': vlan['name']}) + if vlans_list: + config['peer_gateway'] = {'vlans': self.get_vlan_range_list(vlans_list)} if conf.get('interfaces', None) and conf['interfaces'].get('interface', None): portchannels_list = [] @@ -136,4 +149,27 @@ class MclagFacts(object): if portchannels_list: config['members'] = {'portchannels': portchannels_list} + if conf.get('mclag-gateway-macs', None) and conf['mclag-gateway-macs'].get('mclag-gateway-mac', None): + gw_mac_data = conf['mclag-gateway-macs']['mclag-gateway-mac'] + if gw_mac_data[0].get('config', None) and gw_mac_data[0]['config'].get('gateway-mac', None): + config['gateway_mac'] = gw_mac_data[0]['config']['gateway-mac'] + return config + + @staticmethod + def get_vlan_range_list(vlans_list): + """Returns list of VLAN ranges for given list of VLANs""" + vlan_range_list = [] + vlan_id_list = [] + + for vlan in vlans_list: + match = re.match(r'Vlan(\d+)', vlan['vlan']) + if match: + vlan_id_list.append(int(match.group(1))) + + if vlan_id_list: + vlan_id_list.sort() + for vlan_range in get_ranges_in_list(vlan_id_list): + vlan_range_list.append({'vlan': 'Vlan{0}'.format('-'.join(map(str, (vlan_range[0], vlan_range[-1])[:len(vlan_range)])))}) + + return vlan_range_list diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ntp/ntp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ntp/ntp.py index a47142b47..d52516705 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ntp/ntp.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ntp/ntp.py @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -113,16 +112,16 @@ class NtpFacts(object): ntp_config = dict() - if 'network-instance' in ntp_global_config: + if 'network-instance' in ntp_global_config and ntp_global_config['network-instance']: ntp_config['vrf'] = ntp_global_config['network-instance'] if 'enable-ntp-auth' in ntp_global_config: ntp_config['enable_ntp_auth'] = ntp_global_config['enable-ntp-auth'] - if 'source-interface' in ntp_global_config: + if 'source-interface' in ntp_global_config and ntp_global_config['source-interface']: ntp_config['source_interfaces'] = ntp_global_config['source-interface'] - if 'trusted-key' in ntp_global_config: + if 'trusted-key' in ntp_global_config and ntp_global_config['trusted-key']: ntp_config['trusted_keys'] = ntp_global_config['trusted-key'] servers = [] @@ -134,8 +133,10 @@ class NtpFacts(object): server['key_id'] = ntp_server['config']['key-id'] server['minpoll'] = ntp_server['config'].get('minpoll', None) server['maxpoll'] = ntp_server['config'].get('maxpoll', None) + server['prefer'] = ntp_server['config'].get('prefer', None) servers.append(server) - ntp_config['servers'] = servers + if servers: + ntp_config['servers'] = servers keys = [] for ntp_key in ntp_keys: @@ -148,6 +149,7 @@ class NtpFacts(object): key['key_type'] = key_type key['key_value'] = ntp_key['config'].get('key-value', None) keys.append(key) - ntp_config['ntp_keys'] = keys + if keys: + ntp_config['ntp_keys'] = keys return ntp_config diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/pki/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/pki/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/pki/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/pki/pki.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/pki/pki.py new file mode 100644 index 000000000..240c50335 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/pki/pki.py @@ -0,0 +1,144 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Dell EMC +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic pki fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.pki.pki import ( + PkiArgs, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config, +) + +pki_path = "data/openconfig-pki:pki/" +security_profiles_path = "data/openconfig-pki:pki/security-profiles" + + +class PkiFacts(object): + """The sonic pki fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = PkiArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for pki + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if connection: # just for linting purposes, remove + pass + resources = {} + if not data: + result = self.get_pki() + if len(result) > 0 and result[0]: + code, resources = result[0] + + objs = {} + if ( + resources.get("openconfig-pki:pki") + and resources.get("openconfig-pki:pki").get("security-profiles") + and resources.get("openconfig-pki:pki") + .get("security-profiles") + .get("security-profile") + ): + sps = ( + resources.get("openconfig-pki:pki") + .get("security-profiles") + .get("security-profile") + ) + sps_conf = [r.get("config") for r in sps] + rep_conf = [] + for c in sps_conf: + conf = {} + for k, v in c.items(): + conf[k.replace("-", "_")] = v + rep_conf.append(conf) + objs["security_profiles"] = rep_conf + if ( + resources.get("openconfig-pki:pki") + and resources.get("openconfig-pki:pki").get("trust-stores") + and resources.get("openconfig-pki:pki") + .get("trust-stores") + .get("trust-store") + ): + tsts = ( + resources.get("openconfig-pki:pki") + .get("trust-stores") + .get("trust-store") + ) + tsts_conf = [r.get("config") for r in tsts] + rep_conf = [] + for c in tsts_conf: + conf = {} + for k, v in c.items(): + conf[k.replace("-", "_")] = v + rep_conf.append(conf) + + objs["trust_stores"] = rep_conf + + ansible_facts["ansible_network_resources"].pop("pki", None) + facts = {} + if objs: + params = utils.validate_config( + self.argument_spec, {"config": objs} + ) + facts["pki"] = params["config"] + + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def get_pki(self): + request = {"path": pki_path, "method": "get"} + try: + response = edit_config( + self._module, to_request(self._module, request) + ) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + return response + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + return utils.remove_empties(config) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/port_breakout/port_breakout.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/port_breakout/port_breakout.py index 938bd6423..08b143dd5 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/port_breakout/port_breakout.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/port_breakout/port_breakout.py @@ -11,8 +11,6 @@ based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -import re -import json from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -29,7 +27,6 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s from ansible.module_utils.connection import ConnectionError GET = "get" -POST = "post" class Port_breakoutFacts(object): @@ -98,8 +95,8 @@ class Port_breakoutFacts(object): return conf def get_all_port_breakout(self): - """Get all the port_breakout configured in the device""" - request = [{"path": "operations/sonic-port-breakout:breakout_capabilities", "method": POST}] + """Get all the port_breakout configured on the device""" + request = [{"path": "data/sonic-port-breakout:sonic-port-breakout/BREAKOUT_CFG/BREAKOUT_CFG_LIST", "method": GET}] port_breakout_list = [] try: response = edit_config(self._module, to_request(self._module, request)) @@ -107,12 +104,12 @@ class Port_breakoutFacts(object): self._module.fail_json(msg=str(exc), code=exc.code) raw_port_breakout_list = [] - if "sonic-port-breakout:output" in response[0][1]: - raw_port_breakout_list = response[0][1].get("sonic-port-breakout:output", {}).get('caps', []) + if "sonic-port-breakout:BREAKOUT_CFG_LIST" in response[0][1]: + raw_port_breakout_list = response[0][1].get("sonic-port-breakout:BREAKOUT_CFG_LIST", []) for port_breakout in raw_port_breakout_list: name = port_breakout.get('port', None) - mode = port_breakout.get('defmode', None) + mode = port_breakout.get('brkout_mode', None) if name and mode: if '[' in mode: mode = mode[:mode.index('[')] diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/port_group/port_group.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/port_group/port_group.py new file mode 100644 index 000000000..c6e4816c4 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/port_group/port_group.py @@ -0,0 +1,116 @@ +# +# -*- coding: utf-8 -*- +# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic port group fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.port_group.port_group import Port_groupArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible.module_utils.connection import ConnectionError + +GET = "get" + + +class Port_groupFacts(object): + """ The sonic port group fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = Port_groupArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for port groups + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + # typically data is populated from the current device configuration + # data = connection.get('show running-config | section port-group') + # using mock data instead + data = self.get_port_groups() + + objs = [] + for conf in data: + if conf: + obj = self.render_config(self.generated_spec, conf) + if obj: + objs.append(obj) + + ansible_facts['ansible_network_resources'].pop('port_group', None) + facts = {} + if objs: + facts['port_group'] = [] + params = utils.validate_config(self.argument_spec, {'config': objs}) + if params: + facts['port_group'].extend(params['config']) + ansible_facts['ansible_network_resources'].update(facts) + + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + return conf + + def get_port_groups(self): + """Get all the port group configurations""" + + pgs_request = [{"path": "data/openconfig-port-group:port-groups/port-group", "method": GET}] + try: + pgs_response = edit_config(self._module, to_request(self._module, pgs_request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + pgs_config = [] + if "openconfig-port-group:port-group" in pgs_response[0][1]: + pgs_config = pgs_response[0][1].get("openconfig-port-group:port-group", []) + + pgs = [] + for pg_config in pgs_config: + pg = dict() + if 'config' in pg_config: + pg['id'] = pg_config['id'] + speed_str = pg_config['config'].get('speed', None) + if speed_str: + pg['speed'] = speed_str.split(":", 1)[-1] + pgs.append(pg) + + return pgs diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/radius_server/radius_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/radius_server/radius_server.py index 72593b225..33ab55a72 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/radius_server/radius_server.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/radius_server/radius_server.py @@ -11,8 +11,6 @@ based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -import re -import json from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -103,7 +101,7 @@ class Radius_serverFacts(object): if 'auth-type' in raw_radius_global_data: radius_server_data['auth_type'] = raw_radius_global_data['auth-type'] - if 'secret-key' in raw_radius_global_data: + if 'secret-key' in raw_radius_global_data and raw_radius_global_data['secret-key']: radius_server_data['key'] = raw_radius_global_data['secret-key'] if 'timeout' in raw_radius_global_data: radius_server_data['timeout'] = raw_radius_global_data['timeout'] diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/route_maps/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/route_maps/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/route_maps/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/route_maps/route_maps.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/route_maps/route_maps.py new file mode 100644 index 000000000..05e6d6188 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/route_maps/route_maps.py @@ -0,0 +1,517 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic route_maps fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import re +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.route_maps.route_maps import Route_mapsArgs + +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import remove_empties_from_list + +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic \ + import to_request, edit_config + + +class Route_mapsFacts(object): + """ The sonic route_maps fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = Route_mapsArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for route_maps + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + # Fetch data from the current device configuration + # (Skip if operating on previously fetched configuration.) + data = self.get_all_route_maps() + + # split the unparsed route map configuration list into a list + # of parsed route map statement "instances" (dictonary "objects"). + route_maps = [] + for route_map_cfg in data: + route_map_stmts = self.route_map_cfg_parse(route_map_cfg) + if route_map_stmts: + route_maps.extend(route_map_stmts) + + ansible_facts['ansible_network_resources'].pop('route_maps', None) + facts = {} + if route_maps: + params = utils.validate_config(self.argument_spec, + {'config': route_maps}) + params_cleaned = {'config': remove_empties_from_list(params['config'])} + facts['route_maps'] = params_cleaned['config'] + ansible_facts['ansible_network_resources'].update(facts) + return ansible_facts + + def get_all_route_maps(self): + '''Execute a REST "GET" API to fetch all of the current route map configuration + from the target device.''' + + route_map_fetch_spec = \ + "openconfig-routing-policy:routing-policy/policy-definitions" + route_map_resp_key = "openconfig-routing-policy:policy-definitions" + route_map_key = "policy-definition" + url = "data/%s" % route_map_fetch_spec + method = "GET" + request = [{"path": url, "method": method}] + + try: + response = edit_config(self._module, to_request(self._module, request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc)) + + route_maps_unparsed = [] + resp_route_map_envelope = response[0][1].get(route_map_resp_key, None) + if resp_route_map_envelope: + route_maps_unparsed = resp_route_map_envelope.get(route_map_key, None) + return route_maps_unparsed + + def route_map_cfg_parse(self, unparsed_route_map): + '''Parse the raw input configuration JSON representation for the route map specified + by the "unparsed_route_map" input parameter. Parse the information to + convert it to a dictionary matching the "argspec" for the "route_maps" resource + module.''' + + parsed_route_map_stmts = [] + + if not unparsed_route_map.get("config"): + return parsed_route_map_stmts + route_map_name = unparsed_route_map.get('name') + if not route_map_name: + return parsed_route_map_stmts + route_map_statements = unparsed_route_map.get('statements') + if not route_map_statements: + return parsed_route_map_stmts + route_map_stmts_list = route_map_statements.get('statement') + if not route_map_stmts_list: + return parsed_route_map_stmts + + for route_map_stmt in route_map_stmts_list: + parsed_route_map_stmt = {} + parsed_seq_num = route_map_stmt.get('name') + if not parsed_seq_num: + continue + parsed_route_map_stmt['map_name'] = route_map_name + parsed_route_map_stmt['sequence_num'] = parsed_seq_num + self.get_route_map_stmt_set_attr(route_map_stmt, parsed_route_map_stmt) + self.get_route_map_stmt_match_attr(route_map_stmt, parsed_route_map_stmt) + self.get_route_map_call_attr(route_map_stmt, parsed_route_map_stmt) + parsed_route_map_stmts.append(parsed_route_map_stmt) + + return parsed_route_map_stmts + + def get_route_map_stmt_set_attr(self, route_map_stmt, parsed_route_map_stmt): + '''Parse the "set" attribute portion of the raw input configuration JSON + representation for the route map "statement" specified + by the "route_map_stmt," input parameter. Parse the information to + convert it to a dictionary matching the "argspec" for the "route_maps" resource + module.''' + + stmt_actions = route_map_stmt.get('actions') + if not stmt_actions: + return + + # Fetch the permit/deny action for the route map statement + actions_config = stmt_actions.get('config') + if not actions_config: + return + permit_deny_config = actions_config.get('policy-result') + if not permit_deny_config: + return + if permit_deny_config == "ACCEPT_ROUTE": + parsed_route_map_stmt['action'] = "permit" + elif permit_deny_config == "REJECT_ROUTE": + parsed_route_map_stmt['action'] = "deny" + else: + return + + # Create a dict object to hold "set" attributes. + parsed_route_map_stmt['set'] = {} + parsed_route_map_stmt_set = parsed_route_map_stmt['set'] + + # Fetch non-required top level set attributes + set_metric_action = stmt_actions.get('metric-action') + if set_metric_action: + set_metric_action_cfg = set_metric_action.get('config') + if set_metric_action_cfg: + metric_action = set_metric_action_cfg.get('action') + if metric_action: + parsed_route_map_stmt_set['metric'] = {} + if metric_action == 'openconfig-routing-policy:METRIC_SET_VALUE': + value = set_metric_action_cfg.get('metric') + if value: + parsed_route_map_stmt_set['metric']['value'] = value + elif metric_action == 'openconfig-routing-policy:METRIC_SET_RTT': + parsed_route_map_stmt_set['metric']['rtt_action'] = 'set' + elif metric_action == 'openconfig-routing-policy:METRIC_ADD_RTT': + parsed_route_map_stmt_set['metric']['rtt_action'] = 'add' + elif metric_action == 'openconfig-routing-policy:METRIC_SUBTRACT_RTT': + parsed_route_map_stmt_set['metric']['rtt_action'] = 'subtract' + + # Possible anomalous state due to partial deletion of metric config via REST + if parsed_route_map_stmt_set['metric'] == {}: + parsed_route_map_stmt_set.pop('metric') + + # Fetch BGP policy action attributes + set_bgp_policy = stmt_actions.get('openconfig-bgp-policy:bgp-actions') + if set_bgp_policy: + self.get_route_map_set_bgp_policy_attr(set_bgp_policy, parsed_route_map_stmt_set) + + def get_route_map_set_bgp_policy_attr(self, set_bgp_policy, parsed_route_map_stmt_set): + '''Parse the BGP policy "set" attribute portion of the raw input + configuration JSON representation within the route map "statement" + that is currently being parsed. The configuration section to be parsed + is specified by the "set_bgp_policy" input parameter. Parse the + information to convert it to a dictionary matching the "argspec" for + the "route_maps" resource module.''' + + # Fetch as_path_prepend config + set_as_path_top = set_bgp_policy.get('set-as-path-prepend') + if set_as_path_top and set_as_path_top.get('config'): + as_path_prepend = \ + set_as_path_top['config'].get( + 'openconfig-routing-policy-ext:asn-list') + if as_path_prepend: + parsed_route_map_stmt_set['as_path_prepend'] = \ + as_path_prepend + + # Fetch community list "delete" config + set_comm_list_delete_top = set_bgp_policy.get('set-community-delete') + if set_comm_list_delete_top: + set_comm_list_delete_config = set_comm_list_delete_top.get('config') + if set_comm_list_delete_config: + comm_list_delete = \ + set_comm_list_delete_config.get('community-set-delete') + if comm_list_delete: + parsed_route_map_stmt_set['comm_list_delete'] = \ + comm_list_delete + + # Fetch community attributes. + self.get_rmap_set_community(set_bgp_policy, parsed_route_map_stmt_set) + + # Fetch extended community attributes. + self.get_rmap_set_extcommunity(set_bgp_policy, parsed_route_map_stmt_set) + + # Fetch other BGP policy "set" attributes + set_bgp_policy_cfg = set_bgp_policy.get('config') + if set_bgp_policy_cfg: + ip_next_hop = set_bgp_policy_cfg.get('set-next-hop') + if ip_next_hop: + parsed_route_map_stmt_set['ip_next_hop'] = ip_next_hop + + ipv6_next_hop_global_addr = set_bgp_policy_cfg.get('set-ipv6-next-hop-global') + ipv6_prefer_global = set_bgp_policy_cfg.get('set-ipv6-next-hop-prefer-global') + if ipv6_next_hop_global_addr or (ipv6_prefer_global is not None): + parsed_route_map_stmt_set['ipv6_next_hop'] = {} + set_ipv6_nexthop = parsed_route_map_stmt_set['ipv6_next_hop'] + if ipv6_next_hop_global_addr: + set_ipv6_nexthop['global_addr'] = ipv6_next_hop_global_addr + if ipv6_prefer_global is not None: + set_ipv6_nexthop['prefer_global'] = ipv6_prefer_global + + local_preference = set_bgp_policy_cfg.get('set-local-pref') + if local_preference: + parsed_route_map_stmt_set['local_preference'] = local_preference + + set_origin = set_bgp_policy_cfg.get('set-route-origin') + if set_origin: + if set_origin == 'EGP': + parsed_route_map_stmt_set['origin'] = 'egp' + elif set_origin == 'IGP': + parsed_route_map_stmt_set['origin'] = 'igp' + elif set_origin == 'INCOMPLETE': + parsed_route_map_stmt_set['origin'] = 'incomplete' + + weight = set_bgp_policy_cfg.get('set-weight') + if weight: + parsed_route_map_stmt_set['weight'] = weight + + @staticmethod + def get_rmap_set_community(set_bgp_policy, parsed_route_map_stmt_set): + '''Parse the "community" sub-section of the BGP policy "set" attribute + portion of the raw input configuration JSON representation. + The BGP policy "set" configuration section to be parsed is specified + by the "set_bgp_policy" input parameter. Parse the information + to convert it to a dictionary matching the "argspec" for the "route_maps" + resource module.''' + + set_community_top = set_bgp_policy.get('set-community') + if (set_community_top and set_community_top.get('inline') and + set_community_top['inline'].get('config') and + set_community_top['inline']['config'].get('communities')): + + set_community_config_list = \ + set_community_top['inline']['config']['communities'] + parsed_route_map_stmt_set['community'] = {} + parsed_rmap_stmt_set_comm = parsed_route_map_stmt_set['community'] + for set_community_config_item in set_community_config_list: + if (set_community_config_item.split(':')[0] in + ('openconfig-bgp-types', 'openconfig-routing-policy-ext')): + set_community_attr = set_community_config_item.split(':')[1] + if not parsed_rmap_stmt_set_comm.get('community_attributes'): + parsed_rmap_stmt_set_comm['community_attributes'] = [] + parsed_comm_attr_list = \ + parsed_rmap_stmt_set_comm['community_attributes'] + comm_attr_rest_to_argspec = { + 'NO_EXPORT_SUBCONFED': 'local_as', + 'NO_ADVERTISE': 'no_advertise', + 'NO_EXPORT': 'no_export', + 'NOPEER': 'no_peer', + 'NONE': 'none', + 'ADDITIVE': 'additive' + } + if set_community_attr in comm_attr_rest_to_argspec: + parsed_comm_attr_list.append( + comm_attr_rest_to_argspec[set_community_attr]) + else: + if not parsed_rmap_stmt_set_comm.get('community_number'): + parsed_rmap_stmt_set_comm['community_number'] = [] + parsed_comm_num_list = \ + parsed_rmap_stmt_set_comm['community_number'] + set_community_num_val_match = \ + re.match(r'\d+:\d+$', set_community_config_item) + if set_community_num_val_match: + parsed_comm_num_list.append(set_community_config_item) + + @staticmethod + def get_rmap_set_extcommunity(set_bgp_policy, parsed_route_map_stmt_set): + '''Parse the "extcommunity" sub-section of the BGP policy "set" + attribute portion of the raw input configuration JSON representation. + The BGP policy "set" configuration section to be parsed is specified + by the "set_bgp_policy" input parameter. Parse the information + to convert it to a dictionary matching the "argspec" for the "route_maps" + resource module.''' + set_extcommunity_top = set_bgp_policy.get('set-ext-community') + if (set_extcommunity_top and set_extcommunity_top.get('inline') and + set_extcommunity_top['inline'].get('config') and + set_extcommunity_top['inline']['config'].get('communities')): + set_extcommunity_config_list = \ + set_extcommunity_top['inline']['config']['communities'] + if set_extcommunity_config_list: + parsed_route_map_stmt_set['extcommunity'] = {} + parsed_rmap_stmt_set_extcomm = parsed_route_map_stmt_set['extcommunity'] + for set_extcommunity_config_item in set_extcommunity_config_list: + if 'route-target:' in set_extcommunity_config_item: + rt_val = set_extcommunity_config_item.replace('route-target:', '') + if parsed_rmap_stmt_set_extcomm.get('rt'): + parsed_rmap_stmt_set_extcomm['rt'].append(rt_val) + else: + parsed_rmap_stmt_set_extcomm['rt'] = [rt_val] + elif 'route-origin:' in set_extcommunity_config_item: + soo_val = set_extcommunity_config_item.replace('route-origin:', '') + if parsed_rmap_stmt_set_extcomm.get('soo'): + parsed_rmap_stmt_set_extcomm['soo'].append(soo_val) + else: + parsed_rmap_stmt_set_extcomm['soo'] = [soo_val] + + @staticmethod + def get_route_map_call_attr(route_map_stmt, parsed_route_map_stmt): + '''Parse the "call" attribute portion of the raw input configuration JSON + representation for the route map "statement" specified + by the "route_map_stmt," input parameter. Parse the information to + convert it to a dictionary matching the "argspec" for the "route_maps" resource + module.''' + + stmt_conditions = route_map_stmt.get('conditions') + if not stmt_conditions: + return + + # Fetch the "call" policy configuration for the route map statement + conditions_config = stmt_conditions.get('config') + if not conditions_config: + return + call_str = conditions_config.get('call-policy') + if not call_str: + return + parsed_route_map_stmt['call'] = call_str + + def get_route_map_stmt_match_attr(self, route_map_stmt, parsed_route_map_stmt): + '''Parse the "match" attributes in the raw input configuration JSON + representation for the route map "statement" specified + by the "route_map_stmt," input parameter. Parse the information to + convert it to a dictionary matching the "argspec" for the "route_maps" resource + module.''' + + # Create a dict object to hold "match" attributes. + parsed_route_map_stmt['match'] = {} + parsed_rmap_match = parsed_route_map_stmt['match'] + + stmt_conditions = route_map_stmt.get('conditions') + if not stmt_conditions: + return + + # Fetch match as-path configuration + if (stmt_conditions.get('match-as-path-set') and + stmt_conditions['match-as-path-set'].get('config')): + as_path = \ + stmt_conditions['match-as-path-set']['config'].get('as-path-set') + if as_path: + parsed_rmap_match['as_path'] = as_path + + # Fetch BGP policy match attributes. + rmap_bgp_policy_match = stmt_conditions.get('openconfig-bgp-policy:bgp-conditions') + if rmap_bgp_policy_match: + self.get_rmap_match_bgp_policy_attr(rmap_bgp_policy_match, parsed_rmap_match) + + # Fetch other match attributes + if (stmt_conditions.get('match-interface') and + stmt_conditions['match-interface'].get('config')): + match_interface = stmt_conditions['match-interface']['config'].get('interface') + if match_interface: + parsed_rmap_match['interface'] = match_interface + + if (stmt_conditions.get('match-prefix-set') and + stmt_conditions['match-prefix-set']['config']): + match_prefix_set = \ + stmt_conditions['match-prefix-set']['config'] + if match_prefix_set and match_prefix_set.get('prefix-set'): + if not parsed_rmap_match.get('ip'): + parsed_rmap_match['ip'] = {} + parsed_rmap_match['ip']['address'] = \ + match_prefix_set['prefix-set'] + if (match_prefix_set and + match_prefix_set.get('openconfig-routing-policy-ext:ipv6-prefix-set')): + parsed_rmap_match['ipv6'] = {} + parsed_rmap_match['ipv6']['address'] = \ + match_prefix_set['openconfig-routing-policy-ext:ipv6-prefix-set'] + + if (stmt_conditions.get('match-neighbor-set') and + stmt_conditions['match-neighbor-set'].get('config') and + stmt_conditions['match-neighbor-set']['config'].get( + 'openconfig-routing-policy-ext:address')): + parsed_rmap_match_peer = stmt_conditions[ + 'match-neighbor-set']['config']['openconfig-routing-policy-ext:address'][0] + parsed_rmap_match['peer'] = {} + if ':' in parsed_rmap_match_peer: + parsed_rmap_match['peer']['ipv6'] = parsed_rmap_match_peer + elif '.' in parsed_rmap_match_peer: + parsed_rmap_match['peer']['ip'] = parsed_rmap_match_peer + else: + parsed_rmap_match['peer']['interface'] = parsed_rmap_match_peer + + if (stmt_conditions.get('config') and + stmt_conditions['config'].get('install-protocol-eq')): + parsed_rmap_match_source_protocol = \ + stmt_conditions['config']['install-protocol-eq'] + if parsed_rmap_match_source_protocol == "openconfig-policy-types:BGP": + parsed_rmap_match['source_protocol'] = "bgp" + elif parsed_rmap_match_source_protocol == "openconfig-policy-types:OSPF": + parsed_rmap_match['source_protocol'] = "ospf" + elif parsed_rmap_match_source_protocol == "openconfig-policy-types:STATIC": + parsed_rmap_match['source_protocol'] = "static" + elif parsed_rmap_match_source_protocol == \ + "openconfig-policy-types:DIRECTLY_CONNECTED": + parsed_rmap_match['source_protocol'] = "connected" + + if stmt_conditions.get( + 'openconfig-routing-policy-ext:match-src-network-instance'): + match_src_vrf = \ + stmt_conditions[ + 'openconfig-routing-policy-ext:match-src-network-instance'].get('config') + if match_src_vrf and match_src_vrf.get('name'): + parsed_rmap_match['source_vrf'] = match_src_vrf['name'] + + if (stmt_conditions.get('match-tag-set') and + stmt_conditions['match-tag-set'].get('config')): + match_tag = \ + stmt_conditions['match-tag-set']['config'].get( + 'openconfig-routing-policy-ext:tag-value') + if match_tag: + parsed_rmap_match['tag'] = match_tag[0] + + @staticmethod + def get_rmap_match_bgp_policy_attr(rmap_bgp_policy_match, parsed_rmap_match): + '''Parse the BGP policy "match" attribute portion of the raw input + configuration JSON representation within the route map "statement" + that is currently being parsed. The configuration section to be parsed + is specified by the "rmap_bgp_match_cfg" input parameter. Parse the + information to convert it to a dictionary matching the "argspec" for + the "route_maps" resource module.''' + + if (rmap_bgp_policy_match.get('match-as-path-set') and + rmap_bgp_policy_match['match-as-path-set'].get('config')): + as_path = rmap_bgp_policy_match['match-as-path-set']['config'].get('as-path-set') + if as_path: + parsed_rmap_match['as_path'] = as_path + + # Fetch BGP policy match "config" attributes + rmap_bgp_match_cfg = rmap_bgp_policy_match.get('config') + if rmap_bgp_match_cfg: + match_metric = rmap_bgp_match_cfg.get('med-eq') + if match_metric: + parsed_rmap_match['metric'] = match_metric + + match_origin = rmap_bgp_match_cfg.get('origin-eq') + if match_origin: + if match_origin == 'IGP': + parsed_rmap_match['origin'] = 'igp' + elif match_origin == 'EGP': + parsed_rmap_match['origin'] = 'egp' + elif match_origin == 'INCOMPLETE': + parsed_rmap_match['origin'] = 'incomplete' + + if rmap_bgp_match_cfg.get('local-pref-eq'): + parsed_rmap_match['local_preference'] = rmap_bgp_match_cfg['local-pref-eq'] + + if rmap_bgp_match_cfg.get('community-set'): + parsed_rmap_match['community'] = rmap_bgp_match_cfg['community-set'] + + if rmap_bgp_match_cfg.get('ext-community-set'): + parsed_rmap_match['ext_comm'] = rmap_bgp_match_cfg['ext-community-set'] + + if rmap_bgp_match_cfg.get('openconfig-bgp-policy-ext:next-hop-set'): + parsed_rmap_match['ip'] = {} + parsed_rmap_match['ip']['next_hop'] = \ + rmap_bgp_match_cfg['openconfig-bgp-policy-ext:next-hop-set'] + + # Fetch BGP policy match "evpn" attributes + if rmap_bgp_policy_match.get('openconfig-bgp-policy-ext:match-evpn-set'): + bgp_policy_match_evpn_cfg = \ + rmap_bgp_policy_match['openconfig-bgp-policy-ext:match-evpn-set'].get('config') + if bgp_policy_match_evpn_cfg: + parsed_rmap_match['evpn'] = {} + if bgp_policy_match_evpn_cfg.get('vni-number'): + parsed_rmap_match['evpn']['vni'] = \ + bgp_policy_match_evpn_cfg.get('vni-number') + if bgp_policy_match_evpn_cfg.get('default-type5-route'): + parsed_rmap_match['evpn']['default_route'] = True + evpn_route_type = bgp_policy_match_evpn_cfg.get('route-type') + if evpn_route_type: + if evpn_route_type == "openconfig-bgp-policy-ext:MACIP": + parsed_rmap_match['evpn']['route_type'] = "macip" + elif evpn_route_type == "openconfig-bgp-policy-ext:MULTICAST": + parsed_rmap_match['evpn']['route_type'] = "multicast" + elif evpn_route_type == "openconfig-bgp-policy-ext:PREFIX": + parsed_rmap_match['evpn']['route_type'] = "prefix" diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/static_routes/static_routes.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/static_routes/static_routes.py index f83566440..e0d404be7 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/static_routes/static_routes.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/static_routes/static_routes.py @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -139,7 +138,7 @@ class Static_routesFacts(object): blackhole = config.get('blackhole', None) track = config.get('track', None) tag = config.get('tag', None) - if blackhole: + if blackhole is not None: index_dict['blackhole'] = blackhole if interface: index_dict['interface'] = interface diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/stp/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/stp/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/stp/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/stp/stp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/stp/stp.py new file mode 100644 index 000000000..da779c502 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/stp/stp.py @@ -0,0 +1,364 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic stp fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + remove_empties +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.stp.stp import StpArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) + + +stp_map = { + 'openconfig-spanning-tree-types:EDGE_ENABLE': True, + 'openconfig-spanning-tree-types:EDGE_DISABLE': False, + 'openconfig-spanning-tree-types:MSTP': 'mst', + 'openconfig-spanning-tree-ext:PVST': 'pvst', + 'openconfig-spanning-tree-types:RAPID_PVST': 'rapid_pvst', + 'P2P': 'point-to-point', + 'SHARED': 'shared', + 'LOOP': 'loop', + 'ROOT': 'root', + 'NONE': 'none' +} + + +class StpFacts(object): + """ The sonic stp fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = StpArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for stp + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + objs = [] + + if not data: + stp_cfg = self.get_stp_config(self._module) + data = self.update_stp(stp_cfg) + objs = self.render_config(self.generated_spec, data) + facts = {} + if objs: + params = utils.validate_config(self.argument_spec, {'config': remove_empties(objs)}) + facts['stp'] = params['config'] + ansible_facts['ansible_network_resources'].update(facts) + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + return conf + + def update_stp(self, data): + config_dict = {} + if data: + config_dict['global'] = self.update_global(data) + config_dict['interfaces'] = self.update_interfaces(data) + config_dict['mstp'] = self.update_mstp(data) + config_dict['pvst'] = self.update_pvst(data) + config_dict['rapid_pvst'] = self.update_rapid_pvst(data) + + return config_dict + + def update_global(self, data): + global_dict = {} + stp_global = data.get('global', None) + + if stp_global: + config = stp_global.get('config', None) + if config: + enabled_protocol = config.get('enabled-protocol', None) + loop_guard = config.get('loop-guard', None) + bpdu_filter = config.get('bpdu-filter', None) + disabled_vlans = config.get('openconfig-spanning-tree-ext:disabled-vlans', None) + root_guard_timeout = config.get('openconfig-spanning-tree-ext:rootguard-timeout', None) + portfast = config.get('openconfig-spanning-tree-ext:portfast', None) + hello_time = config.get('openconfig-spanning-tree-ext:hello-time', None) + max_age = config.get('openconfig-spanning-tree-ext:max-age', None) + fwd_delay = config.get('openconfig-spanning-tree-ext:forwarding-delay', None) + bridge_priority = config.get('openconfig-spanning-tree-ext:bridge-priority', None) + + if enabled_protocol: + global_dict['enabled_protocol'] = stp_map[enabled_protocol[0]] + if loop_guard is not None: + global_dict['loop_guard'] = loop_guard + if bpdu_filter is not None: + global_dict['bpdu_filter'] = bpdu_filter + if disabled_vlans: + global_dict['disabled_vlans'] = self.convert_vlans_list(disabled_vlans) + if root_guard_timeout: + global_dict['root_guard_timeout'] = root_guard_timeout + if portfast is not None: + global_dict['portfast'] = portfast + if hello_time: + global_dict['hello_time'] = hello_time + if max_age: + global_dict['max_age'] = max_age + if fwd_delay: + global_dict['fwd_delay'] = fwd_delay + if bridge_priority: + global_dict['bridge_priority'] = bridge_priority + + return global_dict + + def update_interfaces(self, data): + interfaces_list = [] + interfaces = data.get('interfaces', None) + + if interfaces: + intf_list = interfaces.get('interface', None) + if intf_list: + for intf in intf_list: + intf_dict = {} + config = intf.get('config', None) + intf_name = config.get('name', None) + edge_port = config.get('edge-port', None) + link_type = config.get('link-type', None) + guard = config.get('guard', None) + bpdu_guard = config.get('bpdu-guard', None) + bpdu_filter = config.get('bpdu-filter', None) + portfast = config.get('openconfig-spanning-tree-ext:portfast', None) + uplink_fast = config.get('openconfig-spanning-tree-ext:uplink-fast', None) + shutdown = config.get('openconfig-spanning-tree-ext:bpdu-guard-port-shutdown', None) + cost = config.get('openconfig-spanning-tree-ext:cost', None) + port_priority = config.get('openconfig-spanning-tree-ext:port-priority', None) + stp_enable = config.get('openconfig-spanning-tree-ext:spanning-tree-enable', None) + + if intf_name: + intf_dict['intf_name'] = intf_name + if edge_port is not None: + intf_dict['edge_port'] = stp_map[edge_port] + if link_type: + intf_dict['link_type'] = stp_map[link_type] + if guard: + intf_dict['guard'] = stp_map[guard] + if bpdu_guard is not None: + intf_dict['bpdu_guard'] = bpdu_guard + if bpdu_filter is not None: + intf_dict['bpdu_filter'] = bpdu_filter + if portfast is not None: + intf_dict['portfast'] = portfast + if uplink_fast is not None: + intf_dict['uplink_fast'] = uplink_fast + if shutdown is not None: + intf_dict['shutdown'] = shutdown + if cost: + intf_dict['cost'] = cost + if port_priority: + intf_dict['port_priority'] = port_priority + if stp_enable is not None: + intf_dict['stp_enable'] = stp_enable + if intf_dict: + interfaces_list.append(intf_dict) + + return interfaces_list + + def update_mstp(self, data): + mstp_dict = {} + mstp = data.get('mstp', None) + + if mstp: + config = mstp.get('config', None) + mst_instances = mstp.get('mst-instances', None) + interfaces = mstp.get('interfaces', None) + if config: + mst_name = config.get('name', None) + revision = config.get('revision', None) + max_hop = config.get('max-hop', None) + hello_time = config.get('hello-time', None) + max_age = config.get('max-age', None) + fwd_delay = config.get('forwarding-delay', None) + + if mst_name: + mstp_dict['mst_name'] = mst_name + if revision: + mstp_dict['revision'] = revision + if max_hop: + mstp_dict['max_hop'] = max_hop + if hello_time: + mstp_dict['hello_time'] = hello_time + if max_age: + mstp_dict['max_age'] = max_age + if fwd_delay: + mstp_dict['fwd_delay'] = fwd_delay + + if mst_instances: + mst_instance = mst_instances.get('mst-instance', None) + if mst_instance: + mst_instances_list = [] + for inst in mst_instance: + inst_dict = {} + mst_id = inst.get('mst-id', None) + config = inst.get('config', None) + interfaces = inst.get('interfaces', None) + if mst_id: + inst_dict['mst_id'] = mst_id + if interfaces: + intf_list = self.get_interfaces_list(interfaces) + if intf_list: + inst_dict['interfaces'] = intf_list + if config: + vlans = config.get('vlan', None) + bridge_priority = config.get('bridge-priority', None) + if vlans: + inst_dict['vlans'] = self.convert_vlans_list(vlans) + if bridge_priority: + inst_dict['bridge_priority'] = bridge_priority + if inst_dict: + mst_instances_list.append(inst_dict) + if mst_instances_list: + mstp_dict['mst_instances'] = mst_instances_list + + return mstp_dict + + def update_pvst(self, data): + pvst_list = [] + pvst = data.get('openconfig-spanning-tree-ext:pvst', None) + + if pvst: + vlans = pvst.get('vlans', None) + if vlans: + vlans_list = self.get_vlans_list(vlans) + if vlans_list: + pvst_list = vlans_list + + return pvst_list + + def update_rapid_pvst(self, data): + rapid_pvst_list = [] + rapid_pvst = data.get('rapid-pvst', None) + + if rapid_pvst: + vlans = rapid_pvst.get('vlan', None) + if vlans: + vlans_list = self.get_vlans_list(vlans) + if vlans_list: + rapid_pvst_list = vlans_list + + return rapid_pvst_list + + def get_stp_config(self, module): + stp_cfg = None + get_stp_path = '/data/openconfig-spanning-tree:stp' + request = {'path': get_stp_path, 'method': 'get'} + + try: + response = edit_config(module, to_request(module, request)) + stp_cfg = response[0][1].get('openconfig-spanning-tree:stp', None) + except ConnectionError as exc: + module.fail_json(msg=str(exc), code=exc.code) + + return stp_cfg + + def get_interfaces_list(self, data): + intf_list = [] + interface_list = data.get('interface', None) + + if interface_list: + for intf in interface_list: + intf_dict = {} + config = intf.get('config', None) + if config: + intf_name = config.get('name', None) + cost = config.get('cost', None) + port_priority = config.get('port-priority', None) + + if intf_name: + intf_dict['intf_name'] = intf_name + if cost: + intf_dict['cost'] = cost + if port_priority: + intf_dict['port_priority'] = port_priority + if intf_dict: + intf_list.append(intf_dict) + + return intf_list + + def get_vlans_list(self, data): + vlan_list = [] + + for vlan in data: + vlan_dict = {} + vlan_id = vlan.get('vlan-id') + config = vlan.get('config', None) + interfaces = vlan.get('interfaces', None) + + if vlan_id: + vlan_dict['vlan_id'] = vlan_id + if interfaces: + intf_list = self.get_interfaces_list(interfaces) + if intf_list: + vlan_dict['interfaces'] = intf_list + if config: + hello_time = config.get('hello-time', None) + max_age = config.get('max-age', None) + fwd_delay = config.get('forwarding-delay', None) + bridge_priority = config.get('bridge-priority', None) + + if hello_time: + vlan_dict['hello_time'] = hello_time + if max_age: + vlan_dict['max_age'] = max_age + if fwd_delay: + vlan_dict['fwd_delay'] = fwd_delay + if bridge_priority: + vlan_dict['bridge_priority'] = bridge_priority + if vlan_dict: + vlan_list.append(vlan_dict) + + return vlan_list + + def convert_vlans_list(self, vlans): + converted_vlans = [] + + for vlan in vlans: + if isinstance(vlan, int): + converted_vlans.append(str(vlan)) + + else: + converted_vlans.append(vlan.replace('..', '-')) + + return converted_vlans diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/system/system.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/system/system.py index 1d7a82d83..65c4491d3 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/system/system.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/system/system.py @@ -11,7 +11,6 @@ based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/tacacs_server/tacacs_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/tacacs_server/tacacs_server.py index a1e79910f..b752b7a83 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/tacacs_server/tacacs_server.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/tacacs_server/tacacs_server.py @@ -11,8 +11,6 @@ based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -import re -import json from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/users/users.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/users/users.py index 038e97f83..59f08e63e 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/users/users.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/users/users.py @@ -11,7 +11,6 @@ based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -22,6 +21,9 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.s to_request, edit_config ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + remove_empties_from_list +) from ansible.module_utils.connection import ConnectionError GET = "get" @@ -74,8 +76,9 @@ class UsersFacts(object): if objs: facts['users'] = [] params = utils.validate_config(self.argument_spec, {'config': objs}) + if params: - facts['users'].extend(params['config']) + facts['users'].extend(remove_empties_from_list(params['config'])) ansible_facts['ansible_network_resources'].update(facts) return ansible_facts @@ -94,7 +97,7 @@ class UsersFacts(object): def get_all_users(self): """Get all the users configured in the device""" - request = [{"path": "data/sonic-system-aaa:sonic-system-aaa/USER", "method": GET}] + request = [{"path": "data/openconfig-system:system/aaa/authentication/users", "method": GET}] users = [] try: response = edit_config(self._module, to_request(self._module, request)) @@ -102,21 +105,16 @@ class UsersFacts(object): self._module.fail_json(msg=str(exc), code=exc.code) raw_users = [] - if "sonic-system-aaa:USER" in response[0][1]: - raw_users = response[0][1].get("sonic-system-aaa:USER", {}).get('USER_LIST', []) + if "openconfig-system:users" in response[0][1]: + raw_users = response[0][1].get("openconfig-system:users", {}).get('user', []) for raw_user in raw_users: name = raw_user.get('username', None) - role = raw_user.get('role', []) - if role and len(role) > 0: - role = role[0] - password = raw_user.get('password', None) + role = raw_user.get('config', {}).get('role', None) user = {} if name and role: user['name'] = name user['role'] = role - if password: - user['password'] = password if user: users.append(user) return users diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlan_mapping/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlan_mapping/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlan_mapping/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlan_mapping/vlan_mapping.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlan_mapping/vlan_mapping.py new file mode 100644 index 000000000..ac53415c7 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlan_mapping/vlan_mapping.py @@ -0,0 +1,225 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The sonic vlan_mapping fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.vlan_mapping.vlan_mapping import Vlan_mappingArgs + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) +from ansible.module_utils.connection import ConnectionError + + +class Vlan_mappingFacts(object): + """ The sonic vlan_mapping fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = Vlan_mappingArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for vlan_mapping + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if connection: # just for linting purposes, remove + pass + + all_vlan_mapping_configs = {} + if not data: + vlan_mapping_configs = self.get_vlan_mappings() + for interface, vlan_config in vlan_mapping_configs.items(): + vlan_mapping_configs_dict = {} + vlan_mapping_configs_dict['name'] = interface + vlan_mapping_configs_dict['mapping'] = vlan_config + all_vlan_mapping_configs[interface] = vlan_mapping_configs_dict + + objs = [] + for vlan_mapping_config in all_vlan_mapping_configs.items(): + obj = self.render_config(self.generated_spec, vlan_mapping_config) + if obj: + objs.append(obj) + + ansible_facts['ansible_network_resources'].pop('vlan_mapping', None) + facts = {} + if objs: + params = utils.validate_config(self.argument_spec, {'config': objs}) + facts['vlan_mapping'] = params['config'] + + ansible_facts['ansible_network_resources'].update(facts) + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + config['name'] = conf[1]['name'] + config['mapping'] = conf[1]['mapping'] + + return utils.remove_empties(config) + + def get_vlan_mappings(self): + """Get all vlan mappings on device""" + interfaces = self.get_ports() + self.get_portchannels() + + vlan_mapping_configs = {} + for interface in interfaces: + response = self.get_port_mappings(interface) + if "openconfig-interfaces-ext:mapped-vlans" in response: + vlan_list = response["openconfig-interfaces-ext:mapped-vlans"].get("mapped-vlan", {}) + for vlan_mapping in vlan_list: + vlan_mapping_dict = {} + vlan_mapping_dict["vlan_ids"] = [] + + tmp_dot1q_tunnel = (vlan_mapping + .get("egress-mapping", {}) + .get("config", {}) + .get("vlan-stack-action", "SWAP")) + if tmp_dot1q_tunnel == "SWAP": + vlan_mapping_dict["dot1q_tunnel"] = False + vlan_mapping_dict["inner_vlan"] = (vlan_mapping + .get("match", {}) + .get("double-tagged", {}) + .get("config", {}) + .get("inner-vlan-id", None)) + if vlan_mapping_dict["inner_vlan"]: + (vlan_mapping_dict["vlan_ids"] + .append(vlan_mapping.get("match", {}) + .get("double-tagged", {}) + .get("config", {}) + .get("outer-vlan-id", None))) + else: + (vlan_mapping_dict["vlan_ids"] + .append(vlan_mapping.get("match", {}) + .get("single-tagged", {}) + .get("config", {}) + .get("vlan-ids", None))) + if vlan_mapping_dict["vlan_ids"]: + vlan_mapping_dict["vlan_ids"][0] = vlan_mapping_dict["vlan_ids"][0][0] + else: + vlan_mapping_dict["dot1q_tunnel"] = True + tmp_vlan_ids = (vlan_mapping + .get("match", {}) + .get("single-tagged", {}) + .get("config", {}) + .get("vlan-ids", None)) + if tmp_vlan_ids: + vlan_mapping_dict["vlan_ids"].extend(tmp_vlan_ids[0].replace('..', '-').split(',')) + + vlan_mapping_dict["service_vlan"] = vlan_mapping.get("vlan-id", None) + vlan_mapping_dict["priority"] = (vlan_mapping + .get("egress-mapping", {}) + .get("config", {}) + .get("mapped-vlan-priority", None)) + + if interface["ifname"] in vlan_mapping_configs: + vlan_mapping_configs[interface["ifname"]].append(vlan_mapping_dict) + else: + vlan_mapping_configs[interface["ifname"]] = [] + vlan_mapping_configs[interface["ifname"]].append(vlan_mapping_dict) + + return vlan_mapping_configs + + def get_port_mappings(self, interface): + """Get a ports vlan mappings from device""" + ifname = interface["ifname"] + if '/' in ifname: + ifname = ifname.replace('/', '%2F') + + port_mappings = "data/openconfig-interfaces:interfaces/interface=%s/openconfig-interfaces-ext:mapped-vlans" % ifname + method = "GET" + request = [{"path": port_mappings, "method": method}] + + try: + response = edit_config(self._module, to_request(self._module, request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + return response[0][1] + + def get_ports(self): + """Get all port names on device""" + all_ports_path = "data/sonic-port:sonic-port/PORT_TABLE" + method = "GET" + request = [{"path": all_ports_path, "method": method}] + + try: + response = edit_config(self._module, to_request(self._module, request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + response = response[0][1] + + port_list = [] + + if "sonic-port:PORT_TABLE" in response: + component = response["sonic-port:PORT_TABLE"] + if "PORT_TABLE_LIST" in component: + for port in component["PORT_TABLE_LIST"]: + if "Eth" in port["ifname"]: + port_list.append({"ifname": port["ifname"]}) + + return port_list + + def get_portchannels(self): + """Get all portchannel names on device""" + all_portchannels_path = "data/sonic-portchannel:sonic-portchannel" + method = "GET" + request = [{"path": all_portchannels_path, "method": method}] + + try: + response = edit_config(self._module, to_request(self._module, request)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) + + response = response[0][1] + + portchannel_list = [] + + if "sonic-portchannel:sonic-portchannel" in response: + component = response["sonic-portchannel:sonic-portchannel"] + if "PORTCHANNEL" in component: + component = component["PORTCHANNEL"] + if "PORTCHANNEL_LIST" in component: + component = component["PORTCHANNEL_LIST"] + for portchannel in component: + portchannel_list.append({"ifname": portchannel["name"]}) + + return portchannel_list diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlans/vlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlans/vlans.py index 7c4af2ea8..3df200048 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlans/vlans.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlans/vlans.py @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vrfs/vrfs.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vrfs/vrfs.py index 797612bc4..375c453d5 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vrfs/vrfs.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vrfs/vrfs.py @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vxlans/vxlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vxlans/vxlans.py index 51aec6561..e521313b8 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vxlans/vxlans.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vxlans/vxlans.py @@ -13,7 +13,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( @@ -166,10 +165,9 @@ class VxlansFacts(object): vxlan['source_ip'] = each_tunnel.get('src_ip', None) vxlan['primary_ip'] = each_tunnel.get('primary_ip', None) vxlan['evpn_nvo'] = None - if vxlan['source_ip']: - evpn_nvo = next((nvo_map['name'] for nvo_map in vxlans_evpn_nvo_list if nvo_map['source_vtep'] == vxlan['name']), None) - if evpn_nvo: - vxlan['evpn_nvo'] = evpn_nvo + evpn_nvo = next((nvo_map['name'] for nvo_map in vxlans_evpn_nvo_list if nvo_map['source_vtep'] == vxlan['name']), None) + if evpn_nvo: + vxlan['evpn_nvo'] = evpn_nvo vxlans.append(vxlan) def fill_vlan_map(self, vxlans, vxlan_vlan_map): diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/sonic.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/sonic.py index 77a63d425..30739ef82 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/sonic.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/sonic.py @@ -33,7 +33,6 @@ import json import re from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( to_list, ComplexList @@ -132,7 +131,7 @@ def edit_config(module, commands, skip_code=None): # Start: This is to convert interface name from Eth1/1 to Eth1%2f1 for request in commands: # This check is to differenciate between requests and commands - if type(request) is dict: + if isinstance(request, dict): url = request.get("path", None) if url: request["path"] = update_url(url) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/bgp_utils.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/bgp_utils.py index 7471bcb11..9c2d18a52 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/bgp_utils.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/bgp_utils.py @@ -13,16 +13,10 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type -import re -from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( normalize_interface_name, ) -from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp.bgp import BgpArgs from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( to_request, edit_config @@ -195,11 +189,6 @@ def get_peergroups(module, vrf_name): prefix_limit = update_bgp_nbr_pg_prefix_limit_dict(pfx_lmt_conf) if prefix_limit: samp.update({'prefix_limit': prefix_limit}) - elif 'l2vpn-evpn' in each and 'prefix-limit' in each['l2vpn-evpn'] and 'config' in each['l2vpn-evpn']['prefix-limit']: - pfx_lmt_conf = each['l2vpn-evpn']['prefix-limit']['config'] - prefix_limit = update_bgp_nbr_pg_prefix_limit_dict(pfx_lmt_conf) - if prefix_limit: - samp.update({'prefix_limit': prefix_limit}) if 'prefix-list' in each and 'config' in each['prefix-list']: pfx_lst_conf = each['prefix-list']['config'] if 'import-policy' in pfx_lst_conf and pfx_lst_conf['import-policy']: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/formatted_diff_utils.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/formatted_diff_utils.py new file mode 100644 index 000000000..f6385294f --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/formatted_diff_utils.py @@ -0,0 +1,588 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import json +from copy import ( + deepcopy +) +from difflib import ( + context_diff +) + + +def get_key_sets(dict_conf): + key_set = set(dict_conf.keys()) + trival_key_set = set() + dict_list_key_set = set() + for key in key_set: + if dict_conf[key] not in [None, [], {}]: + if isinstance(dict_conf[key], (list, dict)): + dict_list_key_set.add(key) + else: + trival_key_set.add(key) + return trival_key_set, dict_list_key_set + + +def get_test_key_set(key, test_keys): + tst_keys = deepcopy(test_keys) + t_key_set = set() + if not tst_keys or not key: + return t_key_set + + t_keys = next((t_key_item[key] for t_key_item in tst_keys if key in t_key_item), None) + if t_keys: + t_keys.pop('__merge_op', None) + t_keys.pop('__delete_op', None) + t_keys.pop('__key_match_op', None) + t_key_set = set(t_keys.keys()) + + return t_key_set + + +# +# Pre-defined Key Match Operations +# + + +""" +Default key match operation. +""" + + +def __KEY_MATCH_OP_DEFAULT(key_set, command, exist_conf): + trival_cmd_key_set, dict_list_cmd_key_set = get_key_sets(command) + trival_exist_key_set, dict_list_exist_key_set = get_key_sets(exist_conf) + + common_trival_key_set = trival_cmd_key_set.intersection(trival_exist_key_set) + common_dict_list_key_set = dict_list_cmd_key_set.intersection(dict_list_exist_key_set) + + key_matched_cnt = 0 + for key in common_trival_key_set.union(common_dict_list_key_set): + if command[key] == exist_conf[key]: + if key in key_set: + key_matched_cnt += 1 + + key_matched = (key_matched_cnt == len(key_set)) + return key_matched + + +def get_key_match_op(key, test_keys): + k_match_op = __KEY_MATCH_OP_DEFAULT + t_key_set = set() + if not test_keys or not key: + return k_match_op + + t_keys = next((t_key_item[key] for t_key_item in test_keys if key in t_key_item), None) + if t_keys: + k_match_op = t_keys.get('__key_match_op', __KEY_MATCH_OP_DEFAULT) + + return k_match_op + + +# +# Pre-defined Merge Operations +# + + +""" +Default key match operation: simply merge command to existing config. +""" + + +def __MERGE_OP_DEFAULT(key_set, command, exist_conf): + new_conf = exist_conf + trival_cmd_key_set, dict_list_cmd_key_set = get_key_sets(command) + nu, dict_list_exist_key_set = get_key_sets(new_conf) + + for key in trival_cmd_key_set: + new_conf[key] = command[key] + + only_cmd_dict_list_key_set = dict_list_cmd_key_set.difference(dict_list_exist_key_set) + for key in only_cmd_dict_list_key_set: + new_conf[key] = command[key] + + return False, new_conf + + +def get_merge_op(key, test_keys): + mrg_op = __MERGE_OP_DEFAULT + if not test_keys: + return mrg_op + if not key: + key = '__default_ops' + t_keys = next((t_key_item[key] for t_key_item in test_keys if key in t_key_item), None) + if t_keys: + mrg_op = t_keys.get('__merge_op', __MERGE_OP_DEFAULT) + + return mrg_op + + +# +# Pre-defined Delete Operations +# + + +""" +Delete entire configuration. +""" + + +def __DELETE_CONFIG(key_set, command, exist_conf): + new_conf = [] + return True, new_conf + + +""" +Delete entire configuration if there is no sub-configuration. +""" + + +def __DELETE_CONFIG_IF_NO_SUBCONFIG(key_set, command, exist_conf): + nu, dict_list_cmd_key_set = get_key_sets(command) + if len(dict_list_cmd_key_set) == 0: + new_conf = [] + return True, new_conf + else: + new_conf = exist_conf + return False, new_conf + + +""" +Delete sub-configuration and leaf configuration, if any. +""" + + +def __DELETE_SUBCONFIG_AND_LEAFS(key_set, command, exist_conf): + new_conf = exist_conf + + trival_cmd_key_set, dict_list_cmd_key_set = get_key_sets(command) + trival_cmd_key_not_key_set = trival_cmd_key_set.difference(key_set) + for key in trival_cmd_key_not_key_set: + new_conf.pop(key, None) + + nu, dict_list_exist_key_set = get_key_sets(exist_conf) + common_dict_list_key_set = dict_list_cmd_key_set.intersection(dict_list_exist_key_set) + if len(common_dict_list_key_set) != 0: + for key in common_dict_list_key_set: + new_conf.pop(key, None) + + return True, new_conf + + +""" +Delete sub-configuration only, if any. +""" + + +def __DELETE_SUBCONFIG_ONLY(key_set, command, exist_conf): + new_conf = exist_conf + nu, dict_list_cmd_key_set = get_key_sets(command) + nu, dict_list_exist_key_set = get_key_sets(exist_conf) + common_dict_list_key_set = dict_list_cmd_key_set.intersection(dict_list_exist_key_set) + for key in common_dict_list_key_set: + new_conf.pop(key, None) + return True, new_conf + + +""" +Delete configuration if there is no non-key leaf, and +delete non-key leaf configuration, if any. +""" + + +def __DELETE_LEAFS_OR_CONFIG_IF_NO_NON_KEY_LEAF(key_set, command, exist_conf): + new_conf = exist_conf + trival_cmd_key_set, dict_list_cmd_key_set = get_key_sets(command) + + if (len(key_set) == len(trival_cmd_key_set)) and \ + (len(dict_list_cmd_key_set) == 0): + new_conf = [] + return True, new_conf + + trival_cmd_key_not_key_set = trival_cmd_key_set.difference(key_set) + for key in trival_cmd_key_not_key_set: + new_conf.pop(key, None) + + return False, new_conf + + +""" +This is default deletion operation. +Delete configuration if there is no non-key leaf, and +delete non-key leaf configuration, if any, if the values of non-key leaf are +equal between command and existing configuration. +""" + + +def __DELETE_OP_DEFAULT(key_set, command, exist_conf): + new_conf = exist_conf + trival_cmd_key_set, dict_list_cmd_key_set = get_key_sets(command) + + if (len(key_set) == len(trival_cmd_key_set)) and \ + (len(dict_list_cmd_key_set) == 0): + new_conf = [] + return True, new_conf + + trival_cmd_key_not_key_set = trival_cmd_key_set.difference(key_set) + for key in trival_cmd_key_not_key_set: + command_val = command.get(key, None) + new_conf_val = new_conf.get(key, None) + if command_val == new_conf_val: + new_conf.pop(key, None) + + return False, new_conf + + +def get_delete_op(key, test_keys): + del_op = __DELETE_OP_DEFAULT + if not test_keys: + return del_op + if not key: + key = '__default_ops' + t_keys = next((t_key_item[key] for t_key_item in test_keys if key in t_key_item), None) + if t_keys: + del_op = t_keys.get('__delete_op', __DELETE_OP_DEFAULT) + + return del_op + + +def get_new_config(commands, exist_conf, test_keys=None): + + if not commands: + return exist_conf + + cmds = deepcopy(commands) + + n_conf = list() + e_conf = exist_conf + for cmd in cmds: + state = cmd['state'] + cmd.pop('state') + + if state == 'merged': + n_conf = derive_config_from_merged_cmd(cmd, e_conf, test_keys) + elif state == 'deleted': + n_conf = derive_config_from_deleted_cmd(cmd, e_conf, test_keys) + elif state == 'replaced': + n_conf = derive_config_from_merged_cmd(cmd, e_conf, test_keys) + elif state == 'overridden': + n_conf = derive_config_from_merged_cmd(cmd, e_conf, test_keys) + # If the "cmd" is derived from playbook, that is "want", the below + # line should be good enough: + # n_conf = cmd + + e_conf = n_conf + + return n_conf + + +def derive_config_from_merged_cmd(command, exist_conf, test_keys=None): + + if not command: + return exist_conf + + if isinstance(command, list) and isinstance(exist_conf, list): + nu, new_conf_dict = derive_config_from_merged_cmd_dict({"config": command}, + {"config": exist_conf}, + test_keys) + new_conf = new_conf_dict.get("config", []) + elif isinstance(command, dict) and isinstance(exist_conf, dict): + merge_op_dft = get_merge_op('__default_ops', test_keys) + nu, new_conf = derive_config_from_merged_cmd_dict(command, exist_conf, + test_keys, None, + None, merge_op_dft) + elif isinstance(command, dict) and isinstance(exist_conf, list): + nu, new_conf_dict = derive_config_from_merged_cmd_dict({"config": [command]}, + {"config": exist_conf}, + test_keys) + new_conf = new_conf_dict.get("config", []) + else: + new_conf = exist_conf + + return new_conf + + +def derive_config_from_merged_cmd_dict(command, exist_conf, test_keys=None, key_set=None, + key_match_op=None, merge_op=None): + + if test_keys is None: + test_keys = [] + if key_set is None: + key_set = set() + if key_match_op is None: + key_match_op = __KEY_MATCH_OP_DEFAULT + if merge_op is None: + merge_op = __MERGE_OP_DEFAULT + + new_conf = deepcopy(exist_conf) + if not command: + return False, new_conf + + trival_cmd_key_set, dict_list_cmd_key_set = get_key_sets(command) + trival_exist_key_set, dict_list_exist_key_set = get_key_sets(new_conf) + + common_trival_key_set = trival_cmd_key_set.intersection(trival_exist_key_set) + common_dict_list_key_set = dict_list_cmd_key_set.intersection(dict_list_exist_key_set) + + key_matched = key_match_op(key_set, command, new_conf) + if key_matched: + done, new_conf = merge_op(key_set, command, new_conf) + if done: + return key_matched, new_conf + else: + nu, dict_list_exist_key_set = get_key_sets(new_conf) + common_dict_list_key_set = dict_list_cmd_key_set.intersection(dict_list_exist_key_set) + else: + return key_matched, new_conf + + for key in key_set: + common_dict_list_key_set.discard(key) + + for key in common_dict_list_key_set: + + cmd_value = command[key] + exist_value = new_conf[key] + + t_key_set = get_test_key_set(key, test_keys) + t_key_match_op = get_key_match_op(key, test_keys) + t_merge_op = get_merge_op(key, test_keys) + + if (isinstance(cmd_value, list) and isinstance(exist_value, list)): + c_list = cmd_value + e_list = exist_value + + new_conf_list = list() + not_dict_item = False + dict_no_key_item = False + for c_item in c_list: + matched_key_dict = False + for e_item in e_list: + if (isinstance(c_item, dict) and isinstance(e_item, dict)): + if t_key_set: + remaining_keys = [t_key_item for t_key_item in test_keys if key not in t_key_item] + k_mtchd, new_conf_dict = derive_config_from_merged_cmd_dict(c_item, + e_item, + remaining_keys, + t_key_set, + t_key_match_op, + t_merge_op) + if k_mtchd: + new_conf[key].remove(e_item) + if new_conf_dict: + new_conf_list.append(new_conf_dict) + matched_key_dict = True + break + else: + dict_no_key_item = True + break + + else: + not_dict_item = True + break + + if not matched_key_dict: + new_conf_list.append(c_item) + + if not_dict_item or dict_no_key_item: + break + + if dict_no_key_item: + new_conf_list = e_list + c_list + + if not_dict_item: + c_set = set(c_list) + e_set = set(e_list) + merge_set = c_set.union(e_set) + if merge_set: + new_conf[key] = list(merge_set) + elif new_conf_list: + new_conf[key].extend(new_conf_list) + + elif (isinstance(cmd_value, dict) and isinstance(exist_value, dict)): + k_mtchd, new_conf_dict = derive_config_from_merged_cmd_dict(cmd_value, + exist_value, + test_keys, + None, + t_key_match_op, + t_merge_op) + if k_mtchd and new_conf_dict: + new_conf[key] = new_conf_dict + + elif (isinstance(cmd_value, (list, dict)) or isinstance(exist_value, (list, dict))): + new_conf[key] = exist_value + break + + else: + continue + + return key_matched, new_conf + + +def derive_config_from_deleted_cmd(command, exist_conf, test_keys=None): + + if not command or not exist_conf: + return exist_conf + + if isinstance(command, list) and isinstance(exist_conf, list): + nu, new_conf_dict = derive_config_from_deleted_cmd_dict({"config": command}, + {"config": exist_conf}, + test_keys) + new_conf = new_conf_dict.get("config", []) + elif isinstance(command, dict) and isinstance(exist_conf, dict): + delete_op_dft = get_delete_op('__default_ops', test_keys) + nu, new_conf = derive_config_from_deleted_cmd_dict(command, exist_conf, + test_keys, None, + None, delete_op_dft) + elif isinstance(command, dict) and isinstance(exist_conf, list): + nu, new_conf_dict = derive_config_from_deleted_cmd_dict({"config": [command]}, + {"config": exist_conf}, + test_keys) + new_conf = new_conf_dict.get("config", []) + else: + new_conf = exist_conf + + return new_conf + + +def derive_config_from_deleted_cmd_dict(command, exist_conf, test_keys=None, key_set=None, + key_match_op=None, delete_op=None): + + if test_keys is None: + test_keys = [] + if key_set is None: + key_set = set() + if key_match_op is None: + key_match_op = __KEY_MATCH_OP_DEFAULT + if delete_op is None: + delete_op = __DELETE_OP_DEFAULT + + new_conf = deepcopy(exist_conf) + if not command: + return True, [] + + trival_cmd_key_set, dict_list_cmd_key_set = get_key_sets(command) + trival_exist_key_set, dict_list_exist_key_set = get_key_sets(new_conf) + + common_trival_key_set = trival_cmd_key_set.intersection(trival_exist_key_set) + common_dict_list_key_set = dict_list_cmd_key_set.intersection(dict_list_exist_key_set) + + key_matched = key_match_op(key_set, command, new_conf) + if key_matched: + done, new_conf = delete_op(key_set, command, new_conf) + if done: + return key_matched, new_conf + else: + nu, dict_list_exist_key_set = get_key_sets(new_conf) + common_dict_list_key_set = dict_list_cmd_key_set.intersection(dict_list_exist_key_set) + else: + return key_matched, new_conf + + for key in key_set: + common_dict_list_key_set.discard(key) + + for key in common_dict_list_key_set: + + cmd_value = command[key] + exist_value = new_conf[key] + + t_key_set = get_test_key_set(key, test_keys) + t_key_match_op = get_key_match_op(key, test_keys) + t_delete_op = get_delete_op(key, test_keys) + + if (isinstance(cmd_value, list) and isinstance(exist_value, list)): + c_list = cmd_value + e_list = exist_value + + new_conf_list = list() + not_dict_item = False + dict_no_key_item = False + for c_item in c_list: + for e_item in e_list: + if (isinstance(c_item, dict) and isinstance(e_item, dict)): + if t_key_set: + remaining_keys = [t_key_item for t_key_item in test_keys if key not in t_key_item] + k_mtchd, new_conf_dict = derive_config_from_deleted_cmd_dict(c_item, e_item, + remaining_keys, + t_key_set, + t_key_match_op, + t_delete_op) + if k_mtchd: + new_conf[key].remove(e_item) + if new_conf_dict: + new_conf_list.append(new_conf_dict) + break + else: + dict_no_key_item = True + break + + else: + not_dict_item = True + break + + if not_dict_item or dict_no_key_item: + break + + if dict_no_key_item: + new_conf_list = e_list + + if not_dict_item: + c_set = set(c_list) + e_set = set(e_list) + delete_set = e_set.difference(c_set) + if delete_set: + new_conf[key] = list(delete_set) + else: + new_conf[key] = [] + elif new_conf_list: + new_conf[key].extend(new_conf_list) + + elif (isinstance(cmd_value, dict) and isinstance(exist_value, dict)): + k_mtchd, new_conf_dict = derive_config_from_deleted_cmd_dict(cmd_value, + exist_value, + test_keys, + None, + t_key_match_op, + t_delete_op) + if k_mtchd: + new_conf.pop(key, None) + if new_conf_dict: + new_conf[key] = new_conf_dict + + elif (isinstance(cmd_value, (list, dict)) or isinstance(exist_value, (list, dict))): + new_conf[key] = exist_value + break + + else: + continue + + return key_matched, new_conf + + +def get_formatted_config_diff(exist_conf, new_conf, verbosity=0): + + exist_conf = json.dumps(exist_conf, sort_keys=True, indent=4, separators=(u',', u': ')) + u'\n' + new_conf = json.dumps(new_conf, sort_keys=True, indent=4, separators=(u',', u': ')) + u'\n' + + bfr = exist_conf.replace("\"", "\'") + aft = new_conf.replace("\"", "\'") + + bfr_list = bfr.splitlines(True) + aft_list = aft.splitlines(True) + diffs = context_diff(bfr_list, aft_list, fromfile='before', tofile='after') + + if verbosity >= 3: + formatted_diff = list() + for diff in diffs: + formatted_diff.append(diff.rstrip('\n')) + + else: + formatted_diff = {'prepared': u''.join(diffs)} + + return formatted_diff diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/interfaces_util.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/interfaces_util.py index a7f6e9063..60df9251d 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/interfaces_util.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/interfaces_util.py @@ -27,10 +27,16 @@ __metaclass__ = type import traceback import json +import re from ansible.module_utils._text import to_native try: + from urllib import quote +except ImportError: + from urllib.parse import quote + +try: import jinja2 HAS_LIB = True except Exception as e: @@ -38,6 +44,29 @@ except Exception as e: ERR_MSG = to_native(e) LIB_IMP_ERR = traceback.format_exc() +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + to_request, + edit_config +) + +intf_speed_map = { + 0: 'SPEED_DEFAULT', + 10: "SPEED_10MB", + 100: "SPEED_100MB", + 1000: "SPEED_1GB", + 2500: "SPEED_2500MB", + 5000: "SPEED_5GB", + 10000: "SPEED_10GB", + 20000: "SPEED_20GB", + 25000: "SPEED_25GB", + 40000: "SPEED_40GB", + 50000: "SPEED_50GB", + 100000: "SPEED_100GB", + 200000: "SPEED_200GB", + 400000: "SPEED_400GB", + 800000: "SPEED_800GB" +} + # To create Loopback, VLAN interfaces def build_interfaces_create_request(interface_name): @@ -53,3 +82,60 @@ def build_interfaces_create_request(interface_name): "method": method, "data": ret_payload} return request + + +def retrieve_port_group_interfaces(module): + port_group_interfaces = [] + method = "get" + port_num_regex = re.compile(r'[\d]{1,4}$') + port_group_url = 'data/openconfig-port-group:port-groups' + request = {"path": port_group_url, "method": method} + try: + response = edit_config(module, to_request(module, request)) + except ConnectionError as exc: + module.fail_json(msg=str(exc), code=exc.code) + + if 'openconfig-port-group:port-groups' in response[0][1] and "port-group" in response[0][1]['openconfig-port-group:port-groups']: + port_groups = response[0][1]['openconfig-port-group:port-groups']['port-group'] + for pg_config in port_groups: + if 'state' in pg_config: + member_start = pg_config['state'].get('member-if-start', '') + member_start = re.search(port_num_regex, member_start) + member_end = pg_config['state'].get('member-if-end', '') + member_end = re.search(port_num_regex, member_end) + if member_start and member_end: + member_start = int(member_start.group(0)) + member_end = int(member_end.group(0)) + port_group_interfaces.extend(range(member_start, member_end + 1)) + + return port_group_interfaces + + +def retrieve_default_intf_speed(module, intf_name): + + # Read the valid_speeds + dft_intf_speed = 'SPEED_DEFAULT' + method = "get" + sonic_port_url = 'data/sonic-port:sonic-port/PORT/PORT_LIST=%s' + sonic_port_vs_url = (sonic_port_url + '/valid_speeds') % quote(intf_name, safe='') + request = {"path": sonic_port_vs_url, "method": method} + try: + response = edit_config(module, to_request(module, request)) + except ConnectionError as exc: + module.fail_json(msg=str(exc), code=exc.code) + if 'sonic-port:valid_speeds' in response[0][1]: + v_speeds = response[0][1].get('sonic-port:valid_speeds', '') + v_speeds_list = v_speeds.split(",") + v_speeds_int_list = [] + for vs in v_speeds_list: + v_speeds_int_list.append(int(vs)) + + dft_speed_int = 0 + if v_speeds_int_list: + dft_speed_int = max(v_speeds_int_list) + dft_intf_speed = intf_speed_map.get(dft_speed_int, 'SPEED_DEFAULT') + + if dft_intf_speed == 'SPEED_DEFAULT': + module.fail_json(msg="Unable to retireve default port speed for the interface {0}".format(intf_name)) + + return dft_intf_speed diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/utils.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/utils.py index 0d6e6d1a0..bc865790b 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/utils.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/utils.py @@ -13,12 +13,17 @@ __metaclass__ = type import re import json import ast +from copy import copy +from itertools import (count, groupby) from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties +) +from ansible.module_utils.common.network import ( is_masklen, to_netmask, - remove_empties ) +from ansible.module_utils.common.validation import check_required_arguments from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( to_request, edit_config @@ -31,6 +36,21 @@ GET = 'get' intf_naming_mode = "" +def remove_matching_defaults(root, default_entry): + if isinstance(root, list): + for list_item in root: + remove_matching_defaults(list_item, default_entry) + elif isinstance(root, dict): + nextobj = root.get(default_entry[0]['name']) + if nextobj is not None: + if len(default_entry) > 1: + remove_matching_defaults(nextobj, default_entry[1:]) + else: + # Leaf + if nextobj == default_entry[0]['default']: + root.pop(default_entry[0]['name']) + + def get_diff(base_data, compare_with_data, test_keys=None, is_skeleton=None): diff = [] if is_skeleton is None: @@ -319,10 +339,13 @@ def netmask_to_cidr(netmask): def remove_empties_from_list(config_list): ret_config = [] - if not config_list: + if not config_list or not isinstance(config_list, list): return ret_config for config in config_list: - ret_config.append(remove_empties(config)) + if isinstance(config, dict): + ret_config.append(remove_empties(config)) + else: + ret_config.append(copy(config)) return ret_config @@ -432,14 +455,7 @@ def get_normalize_interface_name(intf_name, module): def get_speed_from_breakout_mode(breakout_mode): - speed = None - speed_breakout_mode_map = { - "4x10G": "SPEED_10GB", "1x100G": "SPEED_100GB", "1x40G": "SPEED_40GB", "4x25G": "SPEED_25GB", "2x50G": "SPEED_50GB", - "1x400G": "SPEED_400GB", "4x100G": "SPEED_100GB", "4x50G": "SPEED_50GB", "2x100G": "SPEED_100GB", "2x200G": "SPEED_200GB" - } - if breakout_mode in speed_breakout_mode_map: - speed = speed_breakout_mode_map[breakout_mode] - return speed + return 'SPEED_' + breakout_mode.split('x')[1].replace('G', 'GB') def get_breakout_mode(module, name): @@ -455,7 +471,7 @@ def get_breakout_mode(module, name): except ConnectionError as exc: try: json_obj = json.loads(str(exc).replace("'", '"')) - if json_obj and type(json_obj) is dict and 404 == json_obj['code']: + if json_obj and isinstance(json_obj, dict) and 404 == json_obj['code']: response = None else: module.fail_json(msg=str(exc), code=exc.code) @@ -509,3 +525,205 @@ def command_list_str_to_dict(module, warnings, cmd_list_in, exec_cmd=False): cmd_list_out.append(cmd_out) return cmd_list_out + + +def send_requests(module, requests): + + reply = dict() + response = [] + if not module.check_mode and requests: + try: + response = edit_config(module, to_request(module, requests)) + except ConnectionError as exc: + module.fail_json(msg=str(exc), code=exc.code) + + reply = response[0][1] + + return reply + + +def get_replaced_config(new_conf, exist_conf, test_keys=None): + + replace_conf = [] + if not new_conf or not exist_conf: + return replace_conf + + if isinstance(new_conf, list) and isinstance(exist_conf, list): + + replace_conf_dict = get_replaced_config_dict({"config": new_conf}, + {"config": exist_conf}, + test_keys) + replaced_conf = replace_conf_dict.get("config", []) + else: + replaced_conf = get_replaced_config_dict(new_conf, exist_conf, test_keys) + + return replaced_conf + + +def get_replaced_config_dict(new_conf, exist_conf, test_keys=None, key_set=None): + + replaced_conf = dict() + + if test_keys is None: + test_keys = [] + if key_set is None: + key_set = [] + + if not new_conf: + return replaced_conf + + new_key_set = set(new_conf.keys()) + exist_key_set = set(exist_conf.keys()) + + trival_new_key_set = set() + dict_list_new_key_set = set() + for key in new_key_set: + if new_conf[key] not in [None, [], {}]: + if isinstance(new_conf[key], (list, dict)): + dict_list_new_key_set.add(key) + else: + trival_new_key_set.add(key) + + trival_exist_key_set = set() + dict_list_exist_key_set = set() + for key in exist_key_set: + if exist_conf[key] not in [None, [], {}]: + if isinstance(exist_conf[key], (list, dict)): + dict_list_exist_key_set.add(key) + else: + trival_exist_key_set.add(key) + + common_trival_key_set = trival_new_key_set.intersection(trival_exist_key_set) + common_dict_list_key_set = dict_list_new_key_set.intersection(dict_list_exist_key_set) + + key_matched_cnt = 0 + common_trival_key_matched = True + for key in common_trival_key_set: + if new_conf[key] == exist_conf[key]: + if key in key_set: + key_matched_cnt += 1 + else: + if key not in key_set: + common_trival_key_matched = False + + for key in common_dict_list_key_set: + if new_conf[key] == exist_conf[key]: + if key in key_set: + key_matched_cnt += 1 + + key_matched = (key_matched_cnt == len(key_set)) + if key_matched: + extra_trival_new_key_set = trival_new_key_set - common_trival_key_set + extra_trival_exist_key_set = trival_exist_key_set - common_trival_key_set + if extra_trival_new_key_set or extra_trival_exist_key_set or \ + not common_trival_key_matched: + # Replace whole dict. + replaced_conf = exist_conf + return replaced_conf + else: + replaced_conf = [] + return replaced_conf + + for key in key_set: + common_dict_list_key_set.discard(key) + + replace_whole_dict = False + replace_some_list = False + replace_some_dict = False + for key in common_dict_list_key_set: + + new_value = new_conf[key] + exist_value = exist_conf[key] + + if (isinstance(new_value, list) and isinstance(exist_value, list)): + n_list = new_value + e_list = exist_value + t_keys = next((t_key_item[key] for t_key_item in test_keys if key in t_key_item), None) + t_key_set = set() + if t_keys: + t_key_set = set(t_keys.keys()) + + replaced_list = list() + not_dict_item = False + dict_no_key_item = False + for n_item in n_list: + for e_item in e_list: + if (isinstance(n_item, dict) and isinstance(e_item, dict)): + if t_keys: + remaining_keys = [t_key_item for t_key_item in test_keys if key not in t_key_item] + replaced_dict = get_replaced_config_dict(n_item, e_item, + remaining_keys, t_key_set) + else: + dict_no_key_item = True + break + + if replaced_dict: + replaced_list.append(replaced_dict) + break + else: + not_dict_item = True + break + + if not_dict_item or dict_no_key_item: + break + + if dict_no_key_item: + replaced_list = e_list + + if not_dict_item: + n_set = set(n_list) + e_set = set(e_list) + diff_set = n_set.symmetric_difference(e_set) + if diff_set: + replaced_conf[key] = e_list + replace_some_list = True + + elif replaced_list: + replaced_conf[key] = replaced_list + replace_some_list = True + + elif (isinstance(new_value, dict) and isinstance(exist_value, dict)): + replaced_dict = get_replaced_config_dict(new_conf[key], exist_conf[key], test_keys) + if replaced_dict: + replaced_conf[key] = replaced_dict + replace_some_dict = True + + elif (isinstance(new_value, (list, dict)) or isinstance(exist_value, (list, dict))): + # Replace whole dict. + replaced_conf = exist_conf + replace_whole_dict = True + break + + else: + continue + + if ((replace_some_dict or replace_some_list) and (not replace_whole_dict)): + for key in key_set: + replaced_conf[key] = exist_conf[key] + + return replaced_conf + + +def check_required(module, required_parameters, parameters, options_context=None): + '''This utility is a wrapper for the Ansible "check_required_arguments" + function. The "required_parameters" input list provides a list of + key names that are required in the dictionary specified by "parameters". + The optional "options_context" parameter specifies the context/path + from the top level parent dict to the dict being checked.''' + if required_parameters: + spec = {} + for parameter in required_parameters: + spec[parameter] = {'required': True} + + try: + check_required_arguments(spec, parameters, options_context) + except TypeError as exc: + module.fail_json(msg=str(exc)) + + +def get_ranges_in_list(num_list): + """Returns a generator for list(s) of consecutive numbers + present in the given sorted list of numbers + """ + for key, group in groupby(num_list, lambda num, i=count(): num - next(i)): + yield list(group) diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_aaa.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_aaa.py index ddc71331f..c17c0f711 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_aaa.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_aaa.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -79,8 +79,10 @@ options: - Specifies the operation to be performed on the aaa parameters configured on the device. - In case of merged, the input configuration will be merged with the existing aaa configuration on the device. - In case of deleted the existing aaa configuration will be removed from the device. + - In case of replaced, the existing aaa configuration will be replaced with provided configuration. + - In case of overridden, the existing aaa configuration will be overridden with the provided configuration. default: merged - choices: ['merged', 'deleted'] + choices: ['merged', 'deleted', 'overridden', 'replaced'] type: str """ EXAMPLES = """ @@ -169,6 +171,65 @@ EXAMPLES = """ # login-method : local +# Using replaced +# +# Before state: +# ------------- +# +# do show aaa +# AAA Authentication Information +# --------------------------------------------------------- +# failthrough : False +# login-method : local, radius + +- name: Replace aaa configurations + dellemc.enterprise_sonic.sonic_aaa: + config: + authentication: + data: + group: ldap + fail_through: true + state: replaced + +# After state: +# ------------ +# +# do show aaa +# AAA Authentication Information +# --------------------------------------------------------- +# failthrough : True +# login-method : local, ldap + + +# Using overridden +# +# Before state: +# ------------- +# +# do show aaa +# AAA Authentication Information +# --------------------------------------------------------- +# failthrough : False +# login-method : local, radius + +- name: Override aaa configurations + dellemc.enterprise_sonic.sonic_aaa: + config: + authentication: + data: + group: tacacs+ + fail_through: true + state: overridden + +# After state: +# ------------ +# +# do show aaa +# AAA Authentication Information +# --------------------------------------------------------- +# failthrough : True +# login-method : tacacs+ + """ RETURN = """ before: @@ -185,6 +246,13 @@ after: sample: > The configuration returned will always be in the same format of the parameters above. +after(generated): + description: The generated configuration model invocation. + returned: when C(check_mode) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_acl_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_acl_interfaces.py new file mode 100644 index 000000000..883252bc8 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_acl_interfaces.py @@ -0,0 +1,385 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_acl_interfaces +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: sonic_acl_interfaces +version_added: '2.1.0' +notes: + - Supports C(check_mode). +short_description: Manage access control list (ACL) to interface binding on SONiC +description: + - This module provides configuration management of applying access control lists (ACL) + to interfaces in devices running SONiC. + - ACL needs to be created earlier in the device. +author: 'Arun Saravanan Balachandran (@ArunSaravananBalachandran)' +options: + config: + description: + - Specifies interface access-group configurations. + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface, i.e. Eth1/1. + type: str + required: true + access_groups: + description: + - Access-group configurations to be set for the interface. + type: list + elements: dict + suboptions: + type: + description: + - Type of the ACLs to be applied on the interface. + type: str + required: true + choices: + - mac + - ipv4 + - ipv6 + acls: + description: + - List of ACLs for the given type. + type: list + elements: dict + suboptions: + name: + description: + - Name of the ACL to be applied on the interface. + type: str + required: true + direction: + description: + - Specifies the direction of the packets that the ACL will be applied on. + type: str + required: true + choices: + - in + - out + state: + description: + - The state of the configuration after module completion. + - I(merged) - Merges provided interface access-group configuration with on-device configuration. + - I(replaced) - Replaces on-device access-group configuration of the specified interfaces with provided configuration. + - I(overridden) - Overrides all on-device interface access-group configurations with the provided configuration. + - I(deleted) - Deletes on-device interface access-group configuration. + type: str + choices: + - merged + - replaced + - overridden + - deleted + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before State: +# ------------- +# +# sonic# show mac access-group +# sonic# +# sonic# show ip access-group +# sonic# +# sonic# show ipv6 access-group +# Ingress IPV6 access-list ipv6-acl-1 on Eth1/1 +# sonic# + + - name: Merge provided interface access-group configurations + dellemc.enterprise_sonic.sonic_acl_interfaces: + config: + - name: 'Eth1/1' + access_groups: + - type: 'mac' + acls: + - name: 'mac-acl-1' + direction: 'in' + - name: 'mac-acl-2' + direction: 'out' + - type: 'ipv6' + acls: + - name: 'ipv6-acl-2' + direction: 'out' + - name: 'Eth1/2' + access_groups: + - type: 'ipv4' + acls: + - name: 'ip-acl-1' + direction: 'in' + state: merged + +# After State: +# ------------ +# +# sonic# show mac access-group +# Ingress MAC access-list mac-acl-1 on Eth1/1 +# Egress MAC access-list mac-acl-2 on Eth1/1 +# sonic# +# sonic# show ip access-group +# Ingress IP access-list ip-acl-1 on Eth1/2 +# sonic# +# sonic# show ipv6 access-group +# Ingress IPV6 access-list ipv6-acl-1 on Eth1/1 +# Egress IPV6 access-list ipv6-acl-2 on Eth1/1 +# sonic# + + +# Using replaced +# +# Before State: +# ------------- +# +# sonic# show mac access-group +# Ingress MAC access-list mac-acl-1 on Eth1/1 +# Egress MAC access-list mac-acl-2 on Eth1/1 +# sonic# +# sonic# show ip access-group +# Ingress IP access-list ip-acl-1 on Eth1/2 +# sonic# +# sonic# show ipv6 access-group +# Ingress IPV6 access-list ipv6-acl-1 on Eth1/1 +# Egress IPV6 access-list ipv6-acl-2 on Eth1/1 +# sonic# + + - name: Replace device access-group configuration of specified interfaces with provided configuration + dellemc.enterprise_sonic.sonic_acl_interfaces: + config: + - name: 'Eth1/2' + access_groups: + - type: 'ipv6' + acls: + - name: 'ipv6-acl-2' + direction: 'out' + - name: 'Eth1/3' + access_groups: + - type: 'ipv4' + acls: + - name: 'ip-acl-2' + direction: 'out' + state: replaced + +# After State: +# ------------ +# +# sonic# show mac access-group +# Ingress MAC access-list mac-acl-1 on Eth1/1 +# Egress MAC access-list mac-acl-2 on Eth1/1 +# sonic# +# sonic# show ip access-group +# Egress IP access-list ip-acl-2 on Eth1/3 +# sonic# +# sonic# show ipv6 access-group +# Ingress IPV6 access-list ipv6-acl-1 on Eth1/1 +# Egress IPV6 access-list ipv6-acl-2 on Eth1/1 +# Egress IPV6 access-list ipv6-acl-2 on Eth1/2 +# sonic# + + +# Using overridden +# +# Before State: +# ------------- +# +# sonic# show mac access-group +# Ingress MAC access-list mac-acl-1 on Eth1/1 +# Egress MAC access-list mac-acl-2 on Eth1/1 +# sonic# +# sonic# show ip access-group +# Egress IP access-list ip-acl-2 on Eth1/3 +# sonic# +# sonic# show ipv6 access-group +# Ingress IPV6 access-list ipv6-acl-1 on Eth1/1 +# Egress IPV6 access-list ipv6-acl-2 on Eth1/1 +# Egress IPV6 access-list ipv6-acl-2 on Eth1/2 +# sonic# + + - name: Override all interfaces access-group device configuration with provided configuration + dellemc.enterprise_sonic.sonic_acl_interfaces: + config: + - name: 'Eth1/1' + access_groups: + - type: 'ip' + acls: + - name: 'ip-acl-2' + direction: 'out' + - name: 'Eth1/2' + access_groups: + - type: 'ip' + acls: + - name: 'ip-acl-2' + direction: 'out' + state: overridden + +# After State: +# ------------ +# +# sonic# show mac access-group +# sonic# +# sonic# show ip access-group +# Egress IP access-list ip-acl-2 on Eth1/1 +# Egress IP access-list ip-acl-2 on Eth1/2 +# sonic# +# sonic# show ipv6 access-group +# sonic# + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show mac access-group +# Ingress MAC access-list mac-acl-1 on Eth1/1 +# Egress MAC access-list mac-acl-2 on Eth1/1 +# sonic# +# sonic# show ip access-group +# Egress IP access-list ip-acl-2 on Eth1/3 +# sonic# +# sonic# show ipv6 access-group +# Ingress IPV6 access-list ipv6-acl-1 on Eth1/1 +# Egress IPV6 access-list ipv6-acl-2 on Eth1/1 +# Egress IPV6 access-list ipv6-acl-2 on Eth1/2 +# sonic# + + - name: Delete specified interfaces access-group configurations + dellemc.enterprise_sonic.sonic_l2_acls: + config: + - name: 'Eth1/1' + access_groups: + - type: 'mac' + acls: + - name: 'mac-acl-1' + direction: 'in' + - type: 'ipv6' + - name: 'Eth1/2' + state: deleted + +# After State: +# ------------ +# +# sonic# show mac access-group +# Egress MAC access-list mac-acl-2 on Eth1/1 +# sonic# +# sonic# show ip access-group +# Egress IP access-list ip-acl-2 on Eth1/3 +# sonic# +# sonic# show ipv6 access-group +# sonic# + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show mac access-group +# Ingress MAC access-list mac-acl-1 on Eth1/1 +# Egress MAC access-list mac-acl-2 on Eth1/1 +# sonic# +# sonic# show ip access-group +# Egress IP access-list ip-acl-2 on Eth1/3 +# sonic# +# sonic# show ipv6 access-group +# Ingress IPV6 access-list ipv6-acl-1 on Eth1/1 +# Egress IPV6 access-list ipv6-acl-2 on Eth1/1 +# Egress IPV6 access-list ipv6-acl-2 on Eth1/2 +# sonic# + + - name: Delete all interface access-group configurations + dellemc.enterprise_sonic.sonic_acl_interfaces: + config: + state: deleted + +# After State: +# ------------ +# +# sonic# show mac access-group +# sonic# +# sonic# show ip access-group +# sonic# +# sonic# show ipv6 access-group +# sonic# + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after(generated): + description: The generated configuration model invocation. + returned: when C(check_mode) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.acl_interfaces.acl_interfaces import Acl_interfacesArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.acl_interfaces.acl_interfaces import Acl_interfaces + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=Acl_interfacesArgs.argument_spec, + supports_check_mode=True) + + result = Acl_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bfd.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bfd.py new file mode 100644 index 000000000..c969b1a69 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bfd.py @@ -0,0 +1,684 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_bfd +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = """ +--- +module: sonic_bfd +version_added: "2.1.0" +short_description: Manage BFD configuration on SONiC +description: + - This module provides configuration management of BFD for devices running SONiC +author: "Shade Talabi (@stalabi1)" +options: + config: + description: + - Specifies BFD configurations + type: dict + suboptions: + profiles: + description: + - List of preconfiguration profiles + type: list + elements: dict + suboptions: + profile_name: + description: + - BFD profile name + type: str + required: True + enabled: + description: + - Enables BFD session when set to true + type: bool + default: True + transmit_interval: + description: + - Specifies peer transmit interval + type: int + default: 300 + receive_interval: + description: + - Specifies peer receive interval + type: int + default: 300 + detect_multiplier: + description: + - Number of missed packets to bring down a BFD session + type: int + default: 3 + passive_mode: + description: + - Specifies BFD peer as passive when set to true + type: bool + default: False + min_ttl: + description: + - Minimum expected TTL on received packets + type: int + default: 254 + echo_interval: + description: + - Specifies echo interval + type: int + default: 300 + echo_mode: + description: + - Echo mode is enabled when set to true + type: bool + default: False + single_hops: + description: + - List of single-hop sessions + type: list + elements: dict + suboptions: + remote_address: + description: + - IP address used by the remote system for the BFD session + type: str + required: True + vrf: + description: + - Name of the configured VRF on the device + type: str + required: True + interface: + description: + - Interface to use to contact peer + type: str + required: True + local_address: + description: + - Source IP address to be used for BFD sessions over the interface + type: str + required: True + enabled: + description: + - Enables BFD session when set to true + type: bool + default: True + transmit_interval: + description: + - Specifies peer transmit interval + type: int + default: 300 + receive_interval: + description: + - Specifies peer receive interval + type: int + default: 300 + detect_multiplier: + description: + - Number of missed packets to bring down a BFD session + type: int + default: 3 + passive_mode: + description: + - Specifies BFD peer as passive when set to true + type: bool + default: False + echo_interval: + description: + - Specifies echo interval + type: int + default: 300 + echo_mode: + description: + - Echo mode is enabled when set to true + type: bool + default: False + profile_name: + description: + - BFD profile name + type: str + multi_hops: + description: + - List of multi-hop sessions + type: list + elements: dict + suboptions: + remote_address: + description: + - IP address used by the remote system for the BFD session + type: str + required: True + vrf: + description: + - Name of the configured VRF on the device + type: str + required: True + local_address: + description: + - Source IP address to be used for BFD sessions over the interface + type: str + required: True + enabled: + description: + - Enables BFD session when set to true + type: bool + default: True + transmit_interval: + description: + - Specifies peer transmit interval + type: int + default: 300 + receive_interval: + description: + - Specifies peer receive interval + type: int + default: 300 + detect_multiplier: + description: + - Number of missed packets to bring down a BFD session + type: int + default: 3 + passive_mode: + description: + - Specifies BFD peer as passive when set to true + type: bool + default: False + min_ttl: + description: + - Minimum expected TTL on received packets + type: int + default: 254 + profile_name: + description: + - BFD profile name + type: str + state: + description: + - The state of the configuration after module completion. + type: str + choices: ['merged', 'deleted', 'replaced', 'overridden'] + default: merged +""" +EXAMPLES = """ +# Using Merged +# +# Before state: +# ------------- +# +# sonic# show bfd profile +# (No "bfd profile" configuration present) +# sonic# show bfd peers +# (No "bfd peers" configuration present) + + - name: Merge BFD configuration + dellemc.enterprise_sonic.sonic_bfd: + config: + profiles: + - profile_name: 'p1' + enabled: True + transmit_interval: 120 + receive_interval: 200 + detect_multiplier: 2 + passive_mode: True + min_ttl: 140 + echo_interval: 150 + echo_mode: True + single_hops: + - remote_address: '196.88.6.1' + vrf: 'default' + interface: 'Ethernet20' + local_address: '1.1.1.1' + enabled: True + transmit_interval: 50 + receive_interval: 80 + detect_multiplier: 4 + passive_mode: True + echo_interval: 110 + echo_mode: True + profile_name: 'p1' + multi_hops: + - remote_address: '192.40.1.3' + vrf: 'default' + local_address: '3.3.3.3' + enabled: True + transmit_interval: 75 + receive_interval: 100 + detect_multiplier: 3 + passive_mode: True + min_ttl: 125 + profile_name: 'p1' + state: merged + +# After state: +# ------------ +# +# sonic# show bfd profile +# BFD Profile: +# Profile-name: p1 +# Enabled: True +# Echo-mode: Enabled +# Passive-mode: Enabled +# Minimum-Ttl: 140 +# Detect-multiplier: 2 +# Receive interval: 200ms +# Transmission interval: 120ms +# Echo transmission interval: 150ms +# sonic# show bfd peers +# BFD Peers: +# +# peer 192.40.1.3 multihop local-address 3.3.3.3 vrf default +# ID: 989720421 +# Remote ID: 0 +# Passive mode: Enabled +# Profile: p1 +# Minimum TTL: 125 +# Status: down +# Downtime: 0 day(s), 0 hour(s), 1 min(s), 46 sec(s) +# Diagnostics: ok +# Remote diagnostics: ok +# Peer Type: configured +# Local timers: +# Detect-multiplier: 2 +# Receive interval: 100ms +# Transmission interval: 75ms +# Echo transmission interval: ms +# Remote timers: +# Detect-multiplier: 3 +# Receive interval: 1000ms +# Transmission interval: 1000ms +# Echo transmission interval: 0ms +# +# peer 196.88.6.1 local-address 1.1.1.1 vrf default interface Ethernet20 +# ID: 1134635660 +# Remote ID: 0 +# Passive mode: Enabled +# Profile: p1 +# Status: down +# Downtime: 0 day(s), 1 hour(s), 50 min(s), 48 sec(s) +# Diagnostics: ok +# Remote diagnostics: ok +# Peer Type: configured +# Local timers: +# Detect-multiplier: 4 +# Receive interval: 80ms +# Transmission interval: 50ms +# Echo transmission interval: 110ms +# Remote timers: +# Detect-multiplier: 3 +# Receive interval: 1000ms +# Transmission interval: 1000ms +# Echo transmission interval: 0ms +# +# +# Using replaced +# +# Before state: +# ------------- +# +# sonic# show bfd profile +# BFD Profile: +# Profile-name: p1 +# Enabled: True +# Echo-mode: Enabled +# Passive-mode: Enabled +# Minimum-Ttl: 140 +# Detect-multiplier: 2 +# Receive interval: 200ms +# Transmission interval: 120ms +# Echo transmission interval: 150ms +# Profile-name: p2 +# Enabled: True +# Echo-mode: Disabled +# Passive-mode: Disabled +# Minimum-Ttl: 254 +# Detect-multiplier: 3 +# Receive interval: 300ms +# Transmission interval: 300ms +# Echo transmission interval: 300ms + + - name: Replace BFD configuration + dellemc.enterprise_sonic.sonic_bfd: + config: + profiles: + - profile_name: 'p1' + transmit_interval: 144 + - profile_name: 'p2' + enabled: False + transmit_interval: 110 + receive_interval: 235 + detect_multiplier: 5 + passive_mode: True + min_ttl: 155 + echo_interval: 163 + echo_mode: True + state: replaced + +# After state: +# ------------ +# +# sonic# show bfd profile +# BFD Profile: +# Profile-name: p1 +# Enabled: True +# Echo-mode: Enabled +# Passive-mode: Enabled +# Minimum-Ttl: 140 +# Detect-multiplier: 2 +# Receive interval: 200ms +# Transmission interval: 144ms +# Echo transmission interval: 150ms +# Profile-name: p2 +# Enabled: False +# Echo-mode: Enabled +# Passive-mode: Enabled +# Minimum-Ttl: 155 +# Detect-multiplier: 5 +# Receive interval: 235ms +# Transmission interval: 110ms +# Echo transmission interval: 163ms +# +# +# Using overridden +# +# Before state: +# ------------- +# +# sonic# show bfd peers +# BFD Peers: +# +# peer 192.40.1.3 multihop local-address 3.3.3.3 vrf default +# ID: 989720421 +# Remote ID: 0 +# Passive mode: Enabled +# Profile: p1 +# Minimum TTL: 125 +# Status: down +# Downtime: 0 day(s), 0 hour(s), 1 min(s), 46 sec(s) +# Diagnostics: ok +# Remote diagnostics: ok +# Peer Type: configured +# Local timers: +# Detect-multiplier: 2 +# Receive interval: 100ms +# Transmission interval: 75ms +# Echo transmission interval: ms +# Remote timers: +# Detect-multiplier: 3 +# Receive interval: 1000ms +# Transmission interval: 1000ms +# Echo transmission interval: 0ms +# +# peer 196.88.6.1 local-address 1.1.1.1 vrf default interface Ethernet20 +# ID: 1134635660 +# Remote ID: 0 +# Passive mode: Enabled +# Profile: p1 +# Status: down +# Downtime: 0 day(s), 1 hour(s), 50 min(s), 48 sec(s) +# Diagnostics: ok +# Remote diagnostics: ok +# Peer Type: configured +# Local timers: +# Detect-multiplier: 4 +# Receive interval: 80ms +# Transmission interval: 50ms +# Echo transmission interval: 110ms +# Remote timers: +# Detect-multiplier: 3 +# Receive interval: 1000ms +# Transmission interval: 1000ms +# Echo transmission interval: 0ms + + - name: Override BFD configuration + dellemc.enterprise_sonic.sonic_bfd: + config: + single_hops: + - remote_address: '172.68.2.1' + vrf: 'default' + interface: 'Ethernet16' + local_address: '2.2.2.2' + enabled: True + transmit_interval: 60 + receive_interval: 88 + detect_multiplier: 6 + passive_mode: True + echo_interval: 112 + echo_mode: True + profile_name: 'p3' + multi_hops: + - remote_address: '186.42.1.2' + vrf: 'default' + local_address: '1.1.1.1' + enabled: False + transmit_interval: 85 + receive_interval: 122 + detect_multiplier: 4 + passive_mode: False + min_ttl: 120 + profile_name: 'p3' + state: overridden + +# After state: +# ------------ +# +# sonic# show bfd peers +# BFD Peers: +# +# peer 186.42.1.2 multihop local-address 1.1.1.1 vrf default +# ID: 989720421 +# Remote ID: 0 +# Passive mode: Disabled +# Profile: p3 +# Minimum TTL: 120 +# Status: down +# Downtime: 0 day(s), 0 hour(s), 1 min(s), 46 sec(s) +# Diagnostics: ok +# Remote diagnostics: ok +# Peer Type: configured +# Local timers: +# Detect-multiplier: 4 +# Receive interval: 122ms +# Transmission interval: 85ms +# Echo transmission interval: ms +# Remote timers: +# Detect-multiplier: 3 +# Receive interval: 1000ms +# Transmission interval: 1000ms +# Echo transmission interval: 0ms +# +# peer 172.68.2.1 local-address 2.2.2.2 vrf default interface Ethernet16 +# ID: 1134635660 +# Remote ID: 0 +# Passive mode: Enabled +# Profile: p3 +# Status: down +# Downtime: 0 day(s), 1 hour(s), 50 min(s), 48 sec(s) +# Diagnostics: ok +# Remote diagnostics: ok +# Peer Type: configured +# Local timers: +# Detect-multiplier: 6 +# Receive interval: 88ms +# Transmission interval: 60ms +# Echo transmission interval: 112ms +# Remote timers: +# Detect-multiplier: 3 +# Receive interval: 1000ms +# Transmission interval: 1000ms +# Echo transmission interval: 0ms +# +# +# Using deleted +# +# Before state: +# ------------- +# +# sonic# show bfd profile +# BFD Profile: +# Profile-name: p1 +# Enabled: True +# Echo-mode: Enabled +# Passive-mode: Enabled +# Minimum-Ttl: 140 +# Detect-multiplier: 2 +# Receive interval: 200ms +# Transmission interval: 120ms +# Echo transmission interval: 150ms +# sonic# show bfd peers +# BFD Peers: +# +# peer 192.40.1.3 multihop local-address 3.3.3.3 vrf default +# ID: 989720421 +# Remote ID: 0 +# Passive mode: Enabled +# Profile: p1 +# Minimum TTL: 125 +# Status: down +# Downtime: 0 day(s), 0 hour(s), 1 min(s), 46 sec(s) +# Diagnostics: ok +# Remote diagnostics: ok +# Peer Type: configured +# Local timers: +# Detect-multiplier: 2 +# Receive interval: 100ms +# Transmission interval: 75ms +# Echo transmission interval: ms +# Remote timers: +# Detect-multiplier: 3 +# Receive interval: 1000ms +# Transmission interval: 1000ms +# Echo transmission interval: 0ms +# +# peer 196.88.6.1 local-address 1.1.1.1 vrf default interface Ethernet20 +# ID: 1134635660 +# Remote ID: 0 +# Passive mode: Enabled +# Profile: p1 +# Status: down +# Downtime: 0 day(s), 1 hour(s), 50 min(s), 48 sec(s) +# Diagnostics: ok +# Remote diagnostics: ok +# Peer Type: configured +# Local timers: +# Detect-multiplier: 4 +# Receive interval: 80ms +# Transmission interval: 50ms +# Echo transmission interval: 110ms +# Remote timers: +# Detect-multiplier: 3 +# Receive interval: 1000ms +# Transmission interval: 1000ms +# Echo transmission interval: 0ms + + - name: Delete BFD configuration + dellemc.enterprise_sonic.sonic_bfd: + config: + profiles: + - profile_name: 'p1' + enabled: True + transmit_interval: 120 + receive_interval: 200 + detect_multiplier: 2 + passive_mode: True + min_ttl: 140 + echo_interval: 150 + echo_mode: True + single_hops: + - remote_address: '196.88.6.1' + vrf: 'default' + interface: 'Ethernet20' + local_address: '1.1.1.1' + multi_hops: + - remote_address: '192.40.1.3' + vrf: 'default' + local_address: '3.3.3.3' + state: deleted + +# After state +# ----------- +# +# sonic# show bfd profile +# BFD Profile: +# Profile-name: p1 +# Enabled: True +# Echo-mode: Disabled +# Passive-mode: Disabled +# Minimum-Ttl: 254 +# Detect-multiplier: 3 +# Receive interval: 300ms +# Transmission interval: 300ms +# Echo transmission interval: 300ms +# sonic# show bfd peers +# (No "bfd peers" configuration present) +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bfd.bfd import BfdArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bfd.bfd import Bfd + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=BfdArgs.argument_spec, + supports_check_mode=True) + + result = Bfd(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp.py index bc53ca40c..aaf52a40c 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# © Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -143,13 +143,20 @@ options: description: - Allows comparing meds from different neighbors if set to true type: bool + rt_delay: + description: + - Time in seconds to wait before processing route-map changes. + - Range is 0-600. 0 disables the timer and changes to route-map will not be updated. + type: int state: description: - Specifies the operation to be performed on the BGP process that is configured on the device. - In case of merged, the input configuration is merged with the existing BGP configuration on the device. - In case of deleted, the existing BGP configuration is removed from the device. + - In case of replaced, the existing configuration of the specified BGP AS will be replaced with provided configuration. + - In case of overridden, the existing BGP configuration will be overridden with the provided configuration. default: merged - choices: ['merged', 'deleted'] + choices: ['merged', 'deleted', 'replaced', 'overridden'] type: str """ EXAMPLES = """ @@ -158,30 +165,33 @@ EXAMPLES = """ # Before state: # ------------- # -#! -#router bgp 10 vrf VrfCheck1 -# router-id 10.2.2.32 -# log-neighbor-changes -#! -#router bgp 11 vrf VrfCheck2 -# log-neighbor-changes -# bestpath as-path ignore -# bestpath med missing-as-worst confed -# bestpath compare-routerid -#! -#router bgp 4 -# router-id 10.2.2.4 -# bestpath as-path ignore -# bestpath as-path confed -# bestpath med missing-as-worst confed -# bestpath compare-routerid -#! +# ! +# router bgp 10 vrf VrfCheck1 +# router-id 10.2.2.32 +# route-map delay-timer 20 +# log-neighbor-changes +# ! +# router bgp 11 vrf VrfCheck2 +# log-neighbor-changes +# bestpath as-path ignore +# bestpath med missing-as-worst confed +# bestpath compare-routerid +# ! +# router bgp 4 +# router-id 10.2.2.4 +# route-map delay-timer 10 +# bestpath as-path ignore +# bestpath as-path confed +# bestpath med missing-as-worst confed +# bestpath compare-routerid +# ! # - name: Delete BGP Global attributes dellemc.enterprise_sonic.sonic_bgp: config: - bgp_as: 4 router_id: 10.2.2.4 + rt_delay: 10 log_neighbor_changes: False bestpath: as_path: @@ -195,6 +205,7 @@ EXAMPLES = """ missing_as_worst: True - bgp_as: 10 router_id: 10.2.2.32 + rt_delay: 20 log_neighbor_changes: True vrf_name: 'VrfCheck1' - bgp_as: 11 @@ -215,18 +226,18 @@ EXAMPLES = """ # After state: # ------------ # -#! -#router bgp 10 vrf VrfCheck1 -# log-neighbor-changes -#! -#router bgp 11 vrf VrfCheck2 -# log-neighbor-changes -# bestpath compare-routerid -#! -#router bgp 4 -# log-neighbor-changes -# bestpath compare-routerid -#! +# ! +# router bgp 10 vrf VrfCheck1 +# log-neighbor-changes +# ! +# router bgp 11 vrf VrfCheck2 +# log-neighbor-changes +# bestpath compare-routerid +# ! +# router bgp 4 +# log-neighbor-changes +# bestpath compare-routerid +# ! # Using deleted @@ -234,24 +245,26 @@ EXAMPLES = """ # Before state: # ------------- # -#! -#router bgp 10 vrf VrfCheck1 -# router-id 10.2.2.32 -# log-neighbor-changes -#! -#router bgp 11 vrf VrfCheck2 -# log-neighbor-changes -# bestpath as-path ignore -# bestpath med missing-as-worst confed -# bestpath compare-routerid -#! -#router bgp 4 -# router-id 10.2.2.4 -# bestpath as-path ignore -# bestpath as-path confed -# bestpath med missing-as-worst confed -# bestpath compare-routerid -#! +# ! +# router bgp 10 vrf VrfCheck1 +# router-id 10.2.2.32 +# route-map delay-timer 20 +# log-neighbor-changes +# ! +# router bgp 11 vrf VrfCheck2 +# log-neighbor-changes +# bestpath as-path ignore +# bestpath med missing-as-worst confed +# bestpath compare-routerid +# ! +# router bgp 4 +# router-id 10.2.2.4 +# route-map delay-timer 10 +# bestpath as-path ignore +# bestpath as-path confed +# bestpath med missing-as-worst confed +# bestpath compare-routerid +# ! - name: Deletes all the bgp global configurations dellemc.enterprise_sonic.sonic_bgp: @@ -261,8 +274,8 @@ EXAMPLES = """ # After state: # ------------ # -#! -#! +# ! +# ! # Using merged @@ -270,16 +283,17 @@ EXAMPLES = """ # Before state: # ------------- # -#! -#router bgp 4 -# router-id 10.1.1.4 -#! +# ! +# router bgp 4 +# router-id 10.1.1.4 +# ! # - name: Merges provided configuration with device configuration dellemc.enterprise_sonic.sonic_bgp: config: - bgp_as: 4 router_id: 10.2.2.4 + rt_delay: 10 log_neighbor_changes: False timers: holdtime: 20 @@ -301,6 +315,7 @@ EXAMPLES = """ med_val: 7878 - bgp_as: 10 router_id: 10.2.2.32 + rt_delay: 20 log_neighbor_changes: True vrf_name: 'VrfCheck1' - bgp_as: 11 @@ -320,28 +335,172 @@ EXAMPLES = """ # After state: # ------------ # +# ! +# router bgp 10 vrf VrfCheck1 +# router-id 10.2.2.32 +# route-map delay-timer 20 +# log-neighbor-changes +# ! +# router bgp 11 vrf VrfCheck2 +# log-neighbor-changes +# bestpath as-path ignore +# bestpath med missing-as-worst confed +# bestpath compare-routerid +# ! +# router bgp 4 +# router-id 10.2.2.4 +# route-map delay-timer 10 +# bestpath as-path ignore +# bestpath as-path confed +# bestpath med missing-as-worst confed +# bestpath compare-routerid +# always-compare-med +# max-med on-startup 667 7878 +# timers 20 30 +# +# ! + + +# Using replaced +# +# Before state: +# ------------- +# +#! +#router bgp 10 vrf VrfCheck1 +# router-id 10.2.2.32 +# log-neighbor-changes +# timers 60 180 +#! +#router bgp 4 +# router-id 10.2.2.4 +# max-med on-startup 667 7878 +# bestpath as-path ignore +# bestpath as-path confed +# bestpath med missing-as-worst confed +# bestpath compare-routerid +# timers 20 30 +#! +# + +- name: Replace device configuration of specified BGP AS with provided + dellemc.enterprise_sonic.sonic_bgp: + config: + - bgp_as: 4 + router_id: 10.2.2.44 + log_neighbor_changes: True + bestpath: + as_path: + confed: True + compare_routerid: True + - bgp_as: 11 + vrf_name: 'VrfCheck2' + router_id: 10.2.2.33 + log_neighbor_changes: True + bestpath: + as_path: + confed: True + ignore: True + compare_routerid: True + med: + confed: True + missing_as_worst: True + state: replaced + +# +# After state: +# ------------ +# #! #router bgp 10 vrf VrfCheck1 # router-id 10.2.2.32 # log-neighbor-changes +# timers 60 180 #! #router bgp 11 vrf VrfCheck2 +# router-id 10.2.2.33 # log-neighbor-changes # bestpath as-path ignore +# bestpath as-path confed # bestpath med missing-as-worst confed # bestpath compare-routerid +# timers 60 180 +#! +#router bgp 4 +# router-id 10.2.2.44 +# log-neighbor-changes +# bestpath as-path confed +# bestpath compare-routerid +# timers 60 180 +#! + + +# Using overridden +# +# Before state: +# ------------- +# +#! +#router bgp 10 vrf VrfCheck1 +# router-id 10.2.2.32 +# log-neighbor-changes +# timers 60 180 #! #router bgp 4 # router-id 10.2.2.4 +# max-med on-startup 667 7878 # bestpath as-path ignore # bestpath as-path confed # bestpath med missing-as-worst confed # bestpath compare-routerid -# always-compare-med -# max-med on-startup 667 7878 # timers 20 30 +#! +# + +- name: Override device configuration of global BGP with provided configuration + dellemc.enterprise_sonic.sonic_bgp: + config: + - bgp_as: 4 + router_id: 10.2.2.44 + log_neighbor_changes: True + bestpath: + as_path: + confed: True + compare_routerid: True + - bgp_as: 11 + vrf_name: 'VrfCheck2' + router_id: 10.2.2.33 + log_neighbor_changes: True + bestpath: + as_path: + confed: True + ignore: True + compare_routerid: True + timers: + holdtime: 90 + keepalive_interval: 30 + state: overridden + +# +# After state: +# ------------ # #! +#router bgp 11 vrf VrfCheck2 +# router-id 10.2.2.33 +# log-neighbor-changes +# bestpath as-path ignore +# bestpath as-path confed +# bestpath compare-routerid +# timers 30 90 +#! +#router bgp 4 +# router-id 10.2.2.44 +# log-neighbor-changes +# bestpath as-path confed +# bestpath compare-routerid +# timers 60 180 +#! """ diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_af.py index 6d55355c9..af00093c6 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_af.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_af.py @@ -172,13 +172,62 @@ options: description: - Specifies the count of the ebgp multipaths count. type: int + rd: + description: + - Specifies the route distiguisher to be used by the VRF instance. + type: str + rt_in: + description: + - Route-targets to be imported. + type: list + elements: str + rt_out: + description: + - Route-targets to be exported. + type: list + elements: str + vnis: + description: + - VNI configuration for the EVPN. + type: list + elements: dict + suboptions: + vni_number: + description: + - Specifies the VNI number. + type: int + required: True + advertise_default_gw: + description: + - Specifies the advertise default gateway flag. + type: bool + advertise_svi_ip: + description: + - Enables advertise SVI MACIP routes + type: bool + rd: + description: + - Specifies the route distiguisher to be used by the VRF instance. + type: str + rt_in: + description: + - Route-targets to be imported. + type: list + elements: str + rt_out: + description: + - Route-targets to be exported. + type: list + elements: str state: description: - Specifies the operation to be performed on the BGP_AF process configured on the device. - In case of merged, the input configuration is merged with the existing BGP_AF configuration on the device. - In case of deleted, the existing BGP_AF configuration is removed from the device. + - In case of replaced, the existing BGP_AF of specified BGP AS will be replaced with provided configuration. + - In case of overridden, the existing BGP_AF configuration will be overridden with the provided configuration. default: merged - choices: ['merged', 'deleted'] + choices: ['merged', 'deleted', 'overridden', 'replaced'] type: str """ EXAMPLES = """ @@ -208,8 +257,17 @@ EXAMPLES = """ # address-family l2vpn evpn # advertise-svi-ip # advertise ipv6 unicast route-map aa +# rd 3.3.3.3:33 +# route-target import 22:22 +# route-target export 33:33 # advertise-pip ip 1.1.1.1 peer-ip 2.2.2.2 -#! +# ! +# vni 1 +# advertise-default-gw +# advertise-svi-ip +# rd 5.5.5.5:55 +# route-target import 88:88 +# route-target export 77:77 # - name: Delete BGP Address family configuration from the device dellemc.enterprise_sonic.sonic_bgp_af: @@ -228,6 +286,13 @@ EXAMPLES = """ route_advertise_list: - advertise_afi: ipv6 route_map: aa + rd: "3.3.3.3:33" + rt_in: + - "22:22" + rt_out: + - "33:33" + vnis: + - vni_number: 1 - afi: ipv4 safi: unicast - afi: ipv6 @@ -320,6 +385,20 @@ EXAMPLES = """ route_advertise_list: - advertise_afi: ipv4 route_map: bb + rd: "1.1.1.1:11" + rt_in: + - "12:12" + rt_out: + - "13:13" + vnis: + - vni_number: 1 + advertise_default_gw: True + advertise_svi_ip: True + rd: "5.5.5.5:55" + rt_in: + - "88:88" + rt_out: + - "77:77" - afi: ipv4 safi: unicast network: @@ -366,8 +445,300 @@ EXAMPLES = """ # address-family l2vpn evpn # advertise-svi-ip # advertise ipv4 unicast route-map bb +# rd 1.1.1.1:11 +# route-target import 12:12 +# route-target import 13:13 # advertise-pip ip 3.3.3.3 peer-ip 4.4.4.4 +# ! +# vni 1 +# advertise-default-gw +# advertise-svi-ip +# rd 5.5.5.5:55 +# route-target import 88:88 +# route-target export 77:77 +# + + +# Using replaced # +# Before state: +# ------------- +# +#do show running-configuration bgp +#! +#router bgp 52 vrf VrfReg1 +# log-neighbor-changes +# timers 60 180 +# ! +# address-family ipv4 unicast +# maximum-paths 1 +# maximum-paths ibgp 1 +# network 3.3.3.3/16 +# dampening +#! +#router bgp 51 +# router-id 111.2.2.41 +# log-neighbor-changes +# timers 60 180 +# ! +# address-family ipv4 unicast +# redistribute connected route-map bb metric 21 +# redistribute ospf route-map bb metric 27 +# maximum-paths 1 +# maximum-paths ibgp 1 +# network 2.2.2.2/16 +# network 192.168.10.1/32 +# dampening +# ! +# address-family ipv6 unicast +# redistribute static route-map aa metric 26 +# maximum-paths 4 +# maximum-paths ibgp 5 +# ! +# address-family l2vpn evpn +# advertise-all-vni +# advertise-svi-ip +# advertise ipv4 unicast route-map bb +# rd 1.1.1.1:11 +# route-target import 12:12 +# route-target export 13:13 +# dup-addr-detection +# advertise-pip ip 3.3.3.3 peer-ip 4.4.4.4 +# ! +# vni 1 +# advertise-default-gw +# advertise-svi-ip +# rd 5.5.5.5:55 +# route-target import 88:88 +# route-target export 77:77 + +- name: Replace device configuration of address families of specified BGP AS with provided configuration. + dellemc.enterprise_sonic.sonic_bgp_af: + config: + - bgp_as: 51 + address_family: + afis: + - afi: l2vpn + safi: evpn + advertise_pip: True + advertise_pip_ip: "3.3.3.3" + advertise_pip_peer_ip: "4.4.4.4" + advertise_svi_ip: True + advertise_all_vni: False + advertise_default_gw: False + route_advertise_list: + - advertise_afi: ipv4 + route_map: bb + rd: "1.1.1.1:11" + rt_in: + - "22:22" + rt_out: + - "13:13" + vnis: + - vni_number: 5 + advertise_default_gw: True + advertise_svi_ip: True + rd: "10.10.10.10:55" + rt_in: + - "88:88" + rt_out: + - "77:77" + - afi: ipv4 + safi: unicast + network: + - 2.2.2.2/16 + - 192.168.10.1/32 + dampening: True + redistribute: + - protocol: connected + - protocol: ospf + metric: 30 + state: replaced + +# After state: +# ------------ +# +#do show running-configuration bgp +#! +#router bgp 52 vrf VrfReg1 +# log-neighbor-changes +# timers 60 180 +# ! +# address-family ipv4 unicast +# maximum-paths 1 +# maximum-paths ibgp 1 +# network 3.3.3.3/16 +# dampening +#! +#router bgp 51 +# router-id 111.2.2.41 +# log-neighbor-changes +# timers 60 180 +# ! +# address-family ipv4 unicast +# redistribute connected +# redistribute ospf metric 30 +# maximum-paths 1 +# maximum-paths ibgp 1 +# network 2.2.2.2/16 +# network 192.168.10.1/32 +# dampening +# ! +# address-family l2vpn evpn +# advertise-all-vni +# advertise-svi-ip +# advertise ipv4 unicast route-map bb +# rd 1.1.1.1:11 +# route-target import 22:22 +# route-target export 13:13 +# dup-addr-detection +# advertise-pip ip 3.3.3.3 peer-ip 4.4.4.4 +# ! +# vni 5 +# advertise-default-gw +# advertise-svi-ip +# rd 10.10.10.10:55 +# route-target import 88:88 +# route-target export 77:77 + + +# Using overridden +# +# Before state: +# ------------- +# +#do show running-configuration bgp +#! +#router bgp 52 vrf VrfReg1 +# log-neighbor-changes +# timers 60 180 +# ! +# address-family ipv4 unicast +# maximum-paths 1 +# maximum-paths ibgp 1 +# network 3.3.3.3/16 +# dampening +#! +#router bgp 51 +# router-id 111.2.2.41 +# log-neighbor-changes +# timers 60 180 +# ! +# address-family ipv4 unicast +# redistribute connected route-map bb metric 21 +# redistribute ospf route-map bb metric 27 +# maximum-paths 1 +# maximum-paths ibgp 1 +# network 2.2.2.2/16 +# network 192.168.10.1/32 +# dampening +# ! +# address-family ipv6 unicast +# redistribute static route-map aa metric 26 +# maximum-paths 4 +# maximum-paths ibgp 5 +# ! +# address-family l2vpn evpn +# advertise-all-vni +# advertise-svi-ip +# advertise ipv4 unicast route-map bb +# rd 1.1.1.1:11 +# route-target import 12:12 +# route-target export 13:13 +# dup-addr-detection +# advertise-pip ip 3.3.3.3 peer-ip 4.4.4.4 +# ! +# vni 1 +# advertise-default-gw +# advertise-svi-ip +# rd 5.5.5.5:55 +# route-target import 88:88 +# route-target export 77:77 + +- name: Override device configuration of BGP address families with provided configuration. + dellemc.enterprise_sonic.sonic_bgp_af: + config: + - bgp_as: 51 + address_family: + afis: + - afi: l2vpn + safi: evpn + advertise_pip: True + advertise_pip_ip: "3.3.3.3" + advertise_pip_peer_ip: "4.4.4.4" + advertise_svi_ip: True + advertise_all_vni: False + advertise_default_gw: False + route_advertise_list: + - advertise_afi: ipv4 + route_map: bb + rd: "1.1.1.1:11" + rt_in: + - "22:22" + rt_out: + - "13:13" + vnis: + - vni_number: 5 + advertise_default_gw: True + advertise_svi_ip: True + rd: "10.10.10.10:55" + rt_in: + - "88:88" + rt_out: + - "77:77" + - afi: ipv4 + safi: unicast + network: + - 2.2.2.2/16 + - 192.168.10.1/32 + dampening: True + redistribute: + - protocol: connected + - protocol: ospf + metric: 30 + state: overridden + +# After state: +# ------------ +# +#do show running-configuration bgp +#! +#router bgp 52 vrf VrfReg1 +# log-neighbor-changes +# timers 60 180 +#! +#router bgp 51 +# router-id 111.2.2.41 +# log-neighbor-changes +# timers 60 180 +# ! +# address-family ipv4 unicast +# redistribute connected +# redistribute ospf metric 30 +# maximum-paths 1 +# maximum-paths ibgp 1 +# network 2.2.2.2/16 +# network 192.168.10.1/32 +# dampening +# ! +# address-family l2vpn evpn +# advertise-all-vni +# advertise-svi-ip +# advertise ipv4 unicast route-map bb +# rd 1.1.1.1:11 +# route-target import 22:22 +# route-target export 13:13 +# dup-addr-detection +# advertise-pip ip 3.3.3.3 peer-ip 4.4.4.4 +# ! +# vni 5 +# advertise-default-gw +# advertise-svi-ip +# rd 10.10.10.10:55 +# route-target import 88:88 +# route-target export 77:77 + + """ RETURN = """ before: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_as_paths.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_as_paths.py index bd2ff74a1..9bc3f43f5 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_as_paths.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_as_paths.py @@ -62,7 +62,8 @@ options: required: False type: bool description: - - Permits or denies this as path. + - Permits or denies this as-path. + - Default value while adding a new as-path-list is C(False). state: description: - The state of the configuration after module completion. @@ -70,6 +71,8 @@ options: choices: - merged - deleted + - replaced + - overridden default: merged """ EXAMPLES = """ @@ -83,21 +86,21 @@ EXAMPLES = """ # action: permit # members: 808.*,909.* -- name: Delete BGP as path list - dellemc.enterprise_sonic.sonic_bgp_as_paths: - config: - - name: test - members: - - 909.* - permit: true - state: deleted + - name: Delete BGP as path list + dellemc.enterprise_sonic.sonic_bgp_as_paths: + config: + - name: test + members: + - 909.* + permit: true + state: deleted # After state: # ------------ # # show bgp as-path-access-list # AS path list test: -# action: +# action: permit # members: 808.* @@ -114,12 +117,12 @@ EXAMPLES = """ # action: deny # members: 608.*,709.* -- name: Deletes BGP as-path list - dellemc.enterprise_sonic.sonic_bgp_as_paths: - config: - - name: test - members: - state: deleted + - name: Deletes BGP as-path list + dellemc.enterprise_sonic.sonic_bgp_as_paths: + config: + - name: test + members: + state: deleted # After state: # ------------ @@ -140,10 +143,10 @@ EXAMPLES = """ # action: permit # members: 808.*,909.* -- name: Deletes BGP as-path list - dellemc.enterprise_sonic.sonic_bgp_as_paths: - config: - state: deleted + - name: Deletes BGP as-path list + dellemc.enterprise_sonic.sonic_bgp_as_paths: + config: + state: deleted # After state: # ------------ @@ -158,16 +161,16 @@ EXAMPLES = """ # ------------- # # show bgp as-path-access-list -# AS path list test: +# (No bgp as-path-access-list configuration present) -- name: Adds 909.* to test as-path list - dellemc.enterprise_sonic.sonic_bgp_as_paths: - config: - - name: test - members: - - 909.* - permit: true - state: merged + - name: Create a BGP as-path list + dellemc.enterprise_sonic.sonic_bgp_as_paths: + config: + - name: test + members: + - 909.* + permit: true + state: merged # After state: # ------------ @@ -178,6 +181,78 @@ EXAMPLES = """ # members: 909.* +# Using replaced + +# Before state: +# ------------- +# +# show bgp as-path-access-list +# AS path list test: +# action: permit +# members: 800.*,808.* +# AS path list test1: +# action: deny +# members: 500.* + + - name: Replace device configuration of specified BGP as-path lists with provided configuration + dellemc.enterprise_sonic.sonic_bgp_as_paths: + config: + - name: test + members: + - 900.* + - 901.* + permit: true + - name: test1 + - name: test2 + members: + - 100.* + permit: true + state: replaced + +# After state: +# ------------ +# +# show bgp as-path-access-list +# AS path list test: +# action: permit +# members: 900.*,901.* +# AS path list test2: +# action: permit +# members: 100.* + + +# Using overridden + +# Before state: +# ------------- +# +# show bgp as-path-access-list +# AS path list test: +# action: permit +# members: 800.*,808.* +# AS path list test1: +# action: deny +# members: 500.* + + - name: Override device configuration of all BGP as-path lists with provided configuration + dellemc.enterprise_sonic.sonic_bgp_as_paths: + config: + - name: test + members: + - 900.* + - 901.* + permit: true + state: overridden + +# After state: +# ------------ +# +# show bgp as-path-access-list +# AS path list test: +# action: permit +# members: 900.*,901.* + + """ RETURN = """ before: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_communities.py index 08c8dcc7f..dd1c2b083 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_communities.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_communities.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -52,7 +52,7 @@ options: required: True type: str description: - - Name of the BGP communitylist. + - Name of the BGP community-list. type: type: str description: @@ -67,6 +67,7 @@ options: type: bool description: - Permits or denies this community. + - Default value while adding a new community-list is C(False). aann: required: False type: str @@ -120,6 +121,8 @@ options: choices: - merged - deleted + - replaced + - overridden default: merged """ EXAMPLES = """ @@ -130,18 +133,21 @@ EXAMPLES = """ # # show bgp community-list # Standard community list test: match: ANY -# 101 -# 201 -# Standard community list test1: match: ANY -# 301 +# permit local-as +# permit no-peer +# Expanded community list test1: match: ANY +# deny 101 +# deny 302 -- name: Deletes BGP community member +- name: Delete a BGP community-list member dellemc.enterprise_sonic.sonic_bgp_communities: config: - - name: test + - name: test1 + type: expanded + permit: false members: regex: - - 201 + - 302 state: deleted # After state: @@ -149,9 +155,10 @@ EXAMPLES = """ # # show bgp community-list # Standard community list test: match: ANY -# 101 -# Standard community list test1: match: ANY -# 301 +# permit local-as +# permit no-peer +# Expanded community list test1: match: ANY +# deny 101 # Using deleted @@ -161,15 +168,17 @@ EXAMPLES = """ # # show bgp community-list # Standard community list test: match: ANY -# 101 +# permit local-as +# permit no-peer # Expanded community list test1: match: ANY -# 201 +# deny 101 +# deny 302 -- name: Deletes a single BGP community +- name: Delete a single BGP community-list dellemc.enterprise_sonic.sonic_bgp_communities: config: - name: test - members: + type: standard state: deleted # After state: @@ -177,7 +186,8 @@ EXAMPLES = """ # # show bgp community-list # Expanded community list test1: match: ANY -# 201 +# deny 101 +# deny 302 # Using deleted @@ -187,11 +197,13 @@ EXAMPLES = """ # # show bgp community-list # Standard community list test: match: ANY -# 101 +# permit local-as +# permit no-peer # Expanded community list test1: match: ANY -# 201 +# deny 101 +# deny 302 -- name: Delete All BGP communities +- name: Delete All BGP community-lists dellemc.enterprise_sonic.sonic_bgp_communities: config: state: deleted @@ -210,14 +222,17 @@ EXAMPLES = """ # # show bgp community-list # Standard community list test: match: ANY -# 101 +# permit local-as +# permit no-peer # Expanded community list test1: match: ANY -# 201 +# deny 101 +# deny 302 -- name: Deletes all members in a single BGP community +- name: Delete all members in a single BGP community-list dellemc.enterprise_sonic.sonic_bgp_communities: config: - - name: test + - name: test1 + type: expanded members: regex: state: deleted @@ -226,9 +241,9 @@ EXAMPLES = """ # ------------ # # show bgp community-list -# Expanded community list test: match: ANY -# Expanded community list test1: match: ANY -# 201 +# Standard community list test: match: ANY +# permit local-as +# permit no-peer # Using merged @@ -236,23 +251,105 @@ EXAMPLES = """ # Before state: # ------------- # -# show bgp as-path-access-list -# AS path list test: +# show bgp community-list +# Expanded community list test1: match: ANY +# permit 101 +# permit 302 -- name: Adds 909.* to test as-path list - dellemc.enterprise_sonic.sonic_bgp_as_paths: +- name: Add a new BGP community-list + dellemc.enterprise_sonic.sonic_bgp_communities: config: - - name: test + - name: test2 + type: expanded + permit: true members: - - 909.* + regex: + - 909 state: merged # After state: # ------------ # -# show bgp as-path-access-list -# AS path list test: -# members: 909.* +# show bgp community-list +# Expanded community list test1: match: ANY +# permit 101 +# permit 302 +# Expanded community list test2: match: ANY +# permit 909 + + +# Using replaced + +# Before state: +# ------------- +# +# show bgp community-list +# Standard community list test: match: ANY +# permit local-as +# permit no-peer +# Expanded community list test1: match: ANY +# deny 101 +# deny 302 + +- name: Replacing a single BGP community-list + dellemc.enterprise_sonic.sonic_bgp_communities: + config: + - name: test + type: expanded + members: + regex: + - 301 + - name: test3 + type: standard + no_advertise: true + no_peer: true + permit: false + match: ALL + state: replaced + +# After state: +# ------------ +# +# show bgp community-list +# Expanded community list test: match: ANY +# deny 301 +# Expanded community list test1: match: ANY +# deny 101 +# deny 302 +# Standard community list test3: match: ALL +# deny no-advertise +# deny no-peer + + +# Using overridden + +# Before state: +# ------------- +# +# show bgp community-list +# Standard community list test: match: ANY +# permit local-as +# permit no-peer +# Expanded community list test1: match: ANY +# deny 101 +# deny 302 + +- name: Override entire BGP community-lists + dellemc.enterprise_sonic.sonic_bgp_communities: + config: + - name: test3 + type: expanded + members: + regex: + - 301 + state: overridden + +# After state: +# ------------ +# +# show bgp community-list +# Expanded community list test3: match: ANY +# deny 301 """ diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_ext_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_ext_communities.py index c2af0c488..49a30c9f9 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_ext_communities.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_ext_communities.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -66,6 +66,7 @@ options: type: bool description: - Permits or denies this community. + - Default value while adding a new ext-community-list is False. members: required: False type: dict @@ -106,6 +107,8 @@ options: choices: - merged - deleted + - replaced + - overridden default: merged """ EXAMPLES = """ @@ -116,15 +119,16 @@ EXAMPLES = """ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# rt:101:101 -# rt:201:201 +# permit rt:101:101 +# permit rt:201:201 - name: Deletes a BGP ext community member dellemc.enterprise_sonic.sonic_bgp_ext_communities: config: - name: test + type: standard members: - regex: + route_target: - 201:201 state: deleted @@ -133,7 +137,7 @@ EXAMPLES = """ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# rt:101:101 +# permit rt:101:101 # @@ -144,9 +148,10 @@ EXAMPLES = """ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# 101 -# Expanded extended community list test1: match: ANY -# 201 +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 - name: Deletes a single BGP extended community dellemc.enterprise_sonic.sonic_bgp_ext_communities: @@ -160,7 +165,8 @@ EXAMPLES = """ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# 101 +# permit rt:101:101 +# permit rt:201:201 # @@ -171,9 +177,10 @@ EXAMPLES = """ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# 101 -# Expanded extended community list test1: match: ANY -# 201 +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 - name: Deletes all BGP extended communities dellemc.enterprise_sonic.sonic_bgp_ext_communities: @@ -194,9 +201,10 @@ EXAMPLES = """ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# 101 -# Expanded extended community list test1: match: ANY -# 201 +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 - name: Deletes all members in a single BGP extended community dellemc.enterprise_sonic.sonic_bgp_ext_communities: @@ -211,8 +219,8 @@ EXAMPLES = """ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# 101 -# Expanded extended community list test1: match: ANY +# permit rt:101:101 +# permit rt:201:201 # @@ -221,23 +229,108 @@ EXAMPLES = """ # Before state: # ------------- # -# show bgp as-path-access-list -# AS path list test: +# show bgp ext-community-list +# Standard extended community list test: match: ANY +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 -- name: Adds 909.* to test as-path list - dellemc.enterprise_sonic.sonic_bgp_as_paths: +- name: Adds new community list + dellemc.enterprise_sonic.sonic_bgp_ext_communities: config: - - name: test + - name: test3 + type: standard + match: any + permit: true members: - - 909.* + route_origin: + - "301:301" + - "401:401" state: merged # After state: # ------------ # -# show bgp as-path-access-list -# AS path list test: -# members: 909.* +# show bgp ext-community-list +# Standard extended community list test: match: ANY +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 +# Standard extended community list test3: match: ANY +# permit soo:301:301 +# permit soo:401:401 + + + +# Using replaced + +# Before state: +# ------------- +# +# show bgp ext-community-list +# Standard extended community list test: match: ANY +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 + +- name: Replacing a single BGP extended community + dellemc.enterprise_sonic.sonic_bgp_ext_communities: + config: + - name: test + type: expanded + permit: true + match: all + members: + regex: + - 301:302 + state: replaced + +# After state: +# ------------ +# +# show bgp ext-community-list +# Expanded extended community list test: match: ALL +# permit 301:302 +# Expanded extended community list test1: match: ALL +# deny 101:102 +# + + +# Using overridden + +# Before state: +# ------------- +# +# show bgp ext-community-list +# Standard extended community list test: match: ANY +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 + + +- name: Override the entire list of BGP extended community + dellemc.enterprise_sonic.sonic_bgp_ext_communities: + config: + - name: test3 + type: expanded + permit: true + match: all + members: + regex: + - 301:302 + state: overridden + +# After state: +# ------------ +# +# show bgp ext-community-list +# Expanded extended community list test3: match: ALL +# permit 301:302 +# """ diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors.py index 19aeb6fc9..47a414b0a 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors.py @@ -296,7 +296,7 @@ options: default: False prefix_limit: description: - - Specifies prefix limit attributes. + - Specifies prefix limit attributes for ipv4-unicast and ipv6-unicast. type: dict suboptions: max_prefixes: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors_af.py index 10400cfe2..d3b23dfb2 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors_af.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors_af.py @@ -127,7 +127,7 @@ options: default: False prefix_limit: description: - - Specifies prefix limit attributes. + - Specifies prefix limit attributes for ipv4-unicast and ipv6-unicast. type: dict suboptions: max_prefixes: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_config.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_config.py index dd054419f..96c0ee1ba 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_config.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_config.py @@ -318,7 +318,7 @@ def main(): if module.params['save']: result['changed'] = True if not module.check_mode: - cmd = {r'command': ' write memory'} + cmd = {r'command': 'write memory'} run_commands(module, [cmd]) result['saved'] = True diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_copp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_copp.py new file mode 100644 index 000000000..e4e7d358a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_copp.py @@ -0,0 +1,295 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_copp +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = """ +--- +module: sonic_copp +version_added: "2.1.0" +short_description: Manage CoPP configuration on SONiC +description: + - This module provides configuration management of CoPP for devices running SONiC +author: "Shade Talabi (@stalabi1)" +options: + config: + description: + - Specifies CoPP configurations + type: dict + suboptions: + copp_groups: + description: + - List of CoPP entries that comprise a CoPP group + type: list + elements: dict + suboptions: + copp_name: + description: + - Name of CoPP classifier + type: str + required: True + trap_priority: + description: + - CoPP trap priority + type: int + trap_action: + description: + - CoPP trap action + type: str + queue: + description: + - CoPP queue ID + type: int + cir: + description: + - Committed information rate in bps or pps (packets per second) + type: str + cbs: + description: + - Committed bucket size in packets or bytes + type: str + state: + description: + - The state of the configuration after module completion + type: str + choices: ['merged', 'deleted', 'replaced', 'overridden'] + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# sonic# show copp actions +# (No "copp actions" configuration present) + + - name: Merge CoPP groups configuration + dellemc.enterprise_sonic.sonic_copp: + config: + copp_groups: + - copp_name: 'copp-1' + trap_priority: 1 + trap_action: 'DROP' + queue: 1 + cir: '45' + cbs: '45' + - copp_name: 'copp-2' + trap_priority: 2 + trap_action: 'FORWARD' + queue: 2 + cir: '90' + cbs: '90' + state: merged + +# After state: +# ------------ +# +# sonic# show copp actions +# CoPP action group copp-1 +# trap-action drop +# trap-priority 1 +# trap-queue 1 +# police cir 45 cbs 45 +# CoPP action group copp-2 +# trap-action forward +# trap-priority 2 +# trap-queue 2 +# police cir 90 cbs 90 +# +# +# Using replaced +# +# Before state: +# ------------- +# +# sonic# show copp actions +# CoPP action group copp-1 +# trap-action drop +# trap-priority 1 +# trap-queue 1 +# police cir 45 cbs 45 + + - name: Replace CoPP groups configuration + dellemc.enterprise_sonic.sonic_copp: + config: + copp_groups: + - copp_name: 'copp-1' + trap_priority: 2 + trap_action: 'FORWARD' + queue: 2 + - copp_name: 'copp-3' + trap_priority: 3 + trap_action: 'DROP' + queue: 3 + cir: '1000' + cbs: '1000' + state: replaced + +# After state: +# ------------ +# +# sonic# show copp actions +# CoPP action group copp-1 +# trap-action forward +# trap-priority 2 +# trap-queue 2 +# CoPP action group copp-3 +# trap-action drop +# trap-priority 3 +# trap-queue 3 +# police cir 1000 cbs 1000 +# +# +# Using overridden +# +# Before state: +# ------------- +# +# sonic# show copp actions +# CoPP action group copp-1 +# trap-action forward +# trap-priority 2 +# trap-queue 2 +# CoPP action group copp-3 +# trap-action drop +# trap-priority 3 +# trap-queue 3 +# police cir 1000 cbs 1000 + + - name: Override CoPP groups configuration + dellemc.enterprise_sonic.sonic_copp: + config: + copp_groups: + - copp_name: 'copp-4' + trap_priority: 4 + trap_action: 'FORWARD' + queue: 4 + cir: 200 + cbs: 200 + state: overridden + +# After state: +# ------------ +# +# sonic# show copp actions +# CoPP action group copp-4 +# trap-action forward +# trap-priority 4 +# trap-queue 4 +# police cir 200 cbs 200 +# +# +# Using deleted +# +# Before state: +# ------------- +# +# sonic# show copp actions +# CoPP action group copp-1 +# trap-action drop +# trap-priority 1 +# trap-queue 1 +# police cir 45 cbs 45 +# CoPP action group copp-2 +# trap-action forward +# trap-priority 2 +# trap-queue 2 +# police cir 90 cbs 90 + + - name: Delete CoPP groups configuration + dellemc.enterprise_sonic.sonic_copp: + config: + copp_groups: + - copp_name: 'copp-1' + trap_action: 'DROP' + cir: '45' + cbs: '45' + - copp_name: 'copp-2' + state: deleted + +# After state: +# ------------ +# +# sonic# show copp actions +# CoPP action group copp-1 +# trap-action drop +# police cir 45 cbs 45 + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.copp.copp import CoppArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.copp.copp import Copp + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=CoppArgs.argument_spec, + supports_check_mode=True) + + result = Copp(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_dhcp_relay.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_dhcp_relay.py new file mode 100644 index 000000000..321a673eb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_dhcp_relay.py @@ -0,0 +1,781 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_dhcp_relay +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: sonic_dhcp_relay +version_added: '2.1.0' +short_description: Manage DHCP and DHCPv6 relay configurations on SONiC +description: + - This module provides configuration management of DHCP and DHCPv6 relay + parameters on Layer 3 interfaces of devices running SONiC. + - Layer 3 interface and VRF name need to be created earlier in the device. +author: 'Arun Saravanan Balachandran (@ArunSaravananBalachandran)' +options: + config: + description: + - Specifies the DHCP and DHCPv6 relay configurations. + type: list + elements: dict + suboptions: + name: + description: + - Full name of the Layer 3 interface, i.e. Eth1/1. + type: str + required: true + ipv4: + description: + - DHCP relay configurations to be set for the interface mentioned in name option. + type: dict + suboptions: + server_addresses: + description: + - List of DHCP server IPv4 addresses. + type: list + elements: dict + suboptions: + address: + description: + - IPv4 address of the DHCP server. + type: str + vrf_name: + description: + - Specifies name of the VRF in which the DHCP server resides. + - This option is not used with state I(deleted). + type: str + source_interface: + description: + - Specifies the DHCP relay source interface. + type: str + max_hop_count: + description: + - Specifies the maximum hop count for DHCP relay packets. + - The range is from 1 to 16. + type: int + link_select: + description: + - Enable link selection suboption. + type: bool + vrf_select: + description: + - Enable VRF selection suboption. + type: bool + circuit_id: + description: + - Specifies the DHCP relay circuit-id format. + - C(%h:%p) - Hostname followed by interface name eg. sonic:Vlan100 + - C(%i) - Name of the physical interface eg. Eth1/2 + - C(%p) - Name of the interface eg. Vlan100 + type: str + choices: + - '%h:%p' + - '%i' + - '%p' + policy_action: + description: + - Specifies the policy for handling of DHCP relay options. + type: str + choices: + - append + - discard + - replace + ipv6: + description: + - DHCPv6 relay configurations to be set for the interface mentioned in name option. + type: dict + suboptions: + server_addresses: + description: + - List of DHCPv6 server IPv6 addresses. + type: list + elements: dict + suboptions: + address: + description: + - IPv6 address of the DHCPv6 server. + type: str + vrf_name: + description: + - Specifies name of the VRF in which the DHCPv6 server resides. + - This option is used only with state I(merged). + type: str + source_interface: + description: + - Specifies the DHCPv6 relay source interface. + type: str + max_hop_count: + description: + - Specifies the maximum hop count for DHCPv6 relay packets. + - The range is from 1 to 16. + type: int + vrf_select: + description: + - Enable VRF selection suboption. + type: bool + state: + description: + - The state of the configuration after module completion. + - C(merged) - Merges provided DHCP and DHCPv6 relay configuration with on-device configuration. + - C(deleted) - Deletes on-device DHCP and DHCPv6 relay configuration. + - C(replaced) - Replaces on-device DHCP and DHCPv6 relay configuration of the specified interfaces with provided configuration. + - C(overridden) - Overrides all on-device DHCP and DHCPv6 relay configurations with the provided configuration. + type: str + choices: + - merged + - deleted + - replaced + - overridden + default: merged +""" +EXAMPLES = """ +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ip dhcp-relay 91.1.1.1 92.1.1.1 vrf VrfReg1 +# ip dhcp-relay max-hop-count 5 +# ip dhcp-relay vrf-select +# ip dhcp-relay policy-action append +# ipv6 address 81::1/24 +# ipv6 dhcp-relay 91::1 92::1 +# ipv6 dhcp-relay max-hop-count 5 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ip dhcp-relay 71.1.1.1 72.1.1.1 73.1.1.1 +# ip dhcp-relay source-interface Vlan100 +# ip dhcp-relay link-select +# ip dhcp-relay circuit-id %h:%p +# ! + + - name: Delete DHCP and DHCPv6 relay configurations + dellemc.enterprise_sonic.sonic_dhcp_relay: + config: + - name: 'Eth1/1' + ipv4: + server_addresses: + - address: '92.1.1.1' + vrf_select: true + max_hop_count: 5 + ipv6: + server_addresses: + - address: '91::1' + - address: '92::1' + - name: 'Eth1/2' + ipv4: + server_addresses: + - address: '71.1.1.1' + - address: '72.1.1.1' + source_interface: 'Vlan100' + link_select: true + circuit_id: '%h:%p' + state: deleted + +# After State: +# ------------ +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ip dhcp-relay 91.1.1.1 vrf VrfReg1 +# ip dhcp-relay policy-action append +# ipv6 address 81::1/24 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ip dhcp-relay 73.1.1.1 +# ! + + +# Using deleted +# +# NOTE: Support is provided in the dhcp_relay resource module for deletion of all attributes for a +# given address family (IPv4 or IPv6) by using a "special" YAML sequence specifying a server address list +# containing a single "blank" IP address under the target address family. The following example shows +# a task using this syntax for deletion of all DHCP (IPv4) configurations for an interface, but the +# equivalent syntax is supported for DHCPv6 (IPv6) as well. +# +# Before State: +# ------------- +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ip dhcp-relay 91.1.1.1 92.1.1.1 vrf VrfReg1 +# ip dhcp-relay max-hop-count 5 +# ip dhcp-relay vrf-select +# ip dhcp-relay policy-action append +# ipv6 address 81::1/24 +# ipv6 dhcp-relay 91::1 92::1 +# ipv6 dhcp-relay max-hop-count 5 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ip dhcp-relay 71.1.1.1 72.1.1.1 73.1.1.1 +# ip dhcp-relay source-interface Vlan100 +# ip dhcp-relay link-select +# ip dhcp-relay circuit-id %h:%p +# ! + + - name: Delete all IPv4 DHCP relay configurations for interface Eth1/1 + dellemc.enterprise_sonic.sonic_dhcp_relay: + config: + - name: 'Eth1/1' + ipv4: + server_addresses: + - address: + state: deleted + +# After State: +# ------------ +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ipv6 address 81::1/24 +# ipv6 dhcp-relay 91::1 92::1 +# ipv6 dhcp-relay max-hop-count 5 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ip dhcp-relay 71.1.1.1 72.1.1.1 73.1.1.1 +# ip dhcp-relay source-interface Vlan100 +# ip dhcp-relay link-select +# ip dhcp-relay circuit-id %h:%p +# ! + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ip dhcp-relay 91.1.1.1 92.1.1.1 vrf VrfReg1 +# ip dhcp-relay max-hop-count 5 +# ip dhcp-relay vrf-select +# ip dhcp-relay policy-action append +# ipv6 address 81::1/24 +# ipv6 dhcp-relay 91::1 92::1 +# ipv6 dhcp-relay max-hop-count 5 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ip dhcp-relay 71.1.1.1 72.1.1.1 73.1.1.1 +# ip dhcp-relay source-interface Vlan100 +# ip dhcp-relay link-select +# ip dhcp-relay circuit-id %h:%p +# ! + + - name: Delete all DHCP and DHCPv6 relay configurations for interface Eth1/1 + dellemc.enterprise_sonic.sonic_dhcp_relay: + config: + - name: 'Eth1/1' + state: deleted + +# After State: +# ------------ +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ipv6 address 81::1/24 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ip dhcp-relay 71.1.1.1 72.1.1.1 73.1.1.1 +# ip dhcp-relay source-interface Vlan100 +# ip dhcp-relay link-select +# ip dhcp-relay circuit-id %h:%p +# ! + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ip dhcp-relay 91.1.1.1 92.1.1.1 vrf VrfReg1 +# ip dhcp-relay max-hop-count 5 +# ip dhcp-relay vrf-select +# ip dhcp-relay policy-action append +# ipv6 address 81::1/24 +# ipv6 dhcp-relay 91::1 92::1 +# ipv6 dhcp-relay max-hop-count 5 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ip dhcp-relay 71.1.1.1 72.1.1.1 73.1.1.1 +# ip dhcp-relay source-interface Vlan100 +# ip dhcp-relay link-select +# ip dhcp-relay circuit-id %h:%p +# ! + + - name: Delete all DHCP and DHCPv6 relay configurations + dellemc.enterprise_sonic.sonic_dhcp_relay: + config: + state: deleted + +# After State: +# ------------ +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ipv6 address 81::1/24 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ! + + +# Using merged +# +# Before State: +# ------------- +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ipv6 address 81::1/24 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ip dhcp-relay 71.1.1.1 72.1.1.1 +# ! + + - name: Add DHCP and DHCPv6 relay configurations + dellemc.enterprise_sonic.sonic_dhcp_relay: + config: + - name: 'Eth1/1' + ipv4: + server_addresses: + - address: '91.1.1.1' + - address: '92.1.1.1' + vrf_name: 'VrfReg1' + vrf_select: true + max_hop_count: 5 + policy_action: 'append' + ipv6: + server_addresses: + - address: '91::1' + - address: '92::1' + max_hop_count: 5 + - name: 'Eth1/2' + ipv4: + server_addresses: + - address: '73.1.1.1' + source_interface: 'Vlan100' + link_select: true + circuit_id: '%h:%p' + state: merged + +# After State: +# ------------ +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ip dhcp-relay 91.1.1.1 92.1.1.1 vrf VrfReg1 +# ip dhcp-relay max-hop-count 5 +# ip dhcp-relay vrf-select +# ip dhcp-relay policy-action append +# ipv6 address 81::1/24 +# ipv6 dhcp-relay 91::1 92::1 +# ipv6 dhcp-relay max-hop-count 5 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ip dhcp-relay 71.1.1.1 72.1.1.1 73.1.1.1 +# ip dhcp-relay source-interface Vlan100 +# ip dhcp-relay link-select +# ip dhcp-relay circuit-id %h:%p +# ! + + +# Using replaced +# +# Before State: +# ------------- +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ip dhcp-relay 91.1.1.1 92.1.1.1 vrf VrfReg1 +# ip dhcp-relay max-hop-count 5 +# ip dhcp-relay vrf-select +# ip dhcp-relay policy-action append +# ipv6 address 81::1/24 +# ipv6 dhcp-relay 91::1 92::1 +# ipv6 dhcp-relay max-hop-count 5 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ip dhcp-relay 71.1.1.1 72.1.1.1 73.1.1.1 +# ip dhcp-relay source-interface Vlan100 +# ip dhcp-relay link-select +# ip dhcp-relay circuit-id %h:%p +# ipv6 address 61::1/24 +# ipv6 dhcp-relay 71::1 72::1 +# ! +# interface Eth1/3 +# mtu 9100 +# speed 400000 +# fec RS +# shutdown +# ip address 41.1.1.1/24 +# ip dhcp-relay 51.1.1.1 52.1.1.1 +# ip dhcp-relay circuit-id %h:%p +# ipv6 address 41::1/24 +# ipv6 dhcp-relay 51::1 52::1 +# ! + + - name: Replace DHCP and DHCPv6 relay configurations of specified interfaces + dellemc.enterprise_sonic.sonic_dhcp_relay: + config: + - name: 'Eth1/1' + ipv4: + server_addresses: + - address: '91.1.1.1' + - address: '93.1.1.1' + - address: '95.1.1.1' + vrf_name: 'VrfReg1' + vrf_select: true + ipv6: + server_addresses: + - address: '93::1' + - address: '94::1' + source_interface: 'Vlan100' + - name: 'Eth1/2' + ipv4: + server_addresses: + - address: '73.1.1.1' + circuit_id: '%h:%p' + state: replaced + +# After State: +# ------------ +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ip dhcp-relay 91.1.1.1 93.1.1.1 95.1.1.1 vrf VrfReg1 +# ip dhcp-relay vrf-select +# ipv6 address 81::1/24 +# ipv6 dhcp-relay 93::1 94::1 +# ipv6 dhcp-relay source-interface Vlan100 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ip dhcp-relay 73.1.1.1 +# ip dhcp-relay circuit-id %h:%p +# ipv6 address 61::1/24 +# ! +# interface Eth1/3 +# mtu 9100 +# speed 400000 +# fec RS +# shutdown +# ip address 41.1.1.1/24 +# ip dhcp-relay 51.1.1.1 52.1.1.1 +# ip dhcp-relay circuit-id %h:%p +# ipv6 address 41::1/24 +# ipv6 dhcp-relay 51::1 52::1 +# ! + + +# Using overridden +# +# Before State: +# ------------- +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ip dhcp-relay 91.1.1.1 92.1.1.1 vrf VrfReg1 +# ip dhcp-relay max-hop-count 5 +# ip dhcp-relay vrf-select +# ip dhcp-relay policy-action append +# ipv6 address 81::1/24 +# ipv6 dhcp-relay 91::1 92::1 +# ipv6 dhcp-relay max-hop-count 5 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ip dhcp-relay 71.1.1.1 72.1.1.1 73.1.1.1 +# ip dhcp-relay source-interface Vlan100 +# ip dhcp-relay link-select +# ip dhcp-relay circuit-id %h:%p +# ipv6 address 61::1/24 +# ipv6 dhcp-relay 71::1 72::1 +# ! +# interface Eth1/3 +# mtu 9100 +# speed 400000 +# fec RS +# shutdown +# ip address 41.1.1.1/24 +# ip dhcp-relay 51.1.1.1 52.1.1.1 +# ip dhcp-relay circuit-id %h:%p +# ipv6 address 41::1/24 +# ipv6 dhcp-relay 51::1 52::1 +# ! + + - name: Override DHCP and DHCPv6 relay configurations + dellemc.enterprise_sonic.sonic_dhcp_relay: + config: + - name: 'Eth1/1' + ipv4: + server_addresses: + - address: '91.1.1.1' + - address: '93.1.1.1' + - address: '95.1.1.1' + vrf_name: 'VrfReg1' + vrf_select: true + ipv6: + server_addresses: + - address: '93::1' + - address: '94::1' + source_interface: 'Vlan100' + - name: 'Eth1/2' + ipv4: + server_addresses: + - address: '73.1.1.1' + circuit_id: '%h:%p' + state: overridden + +# After State: +# ------------ +# +# sonic# show running-configuration interface +# ! +# interface Eth1/1 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 81.1.1.1/24 +# ip dhcp-relay 91.1.1.1 93.1.1.1 95.1.1.1 vrf VrfReg1 +# ip dhcp-relay vrf-select +# ipv6 address 81::1/24 +# ipv6 dhcp-relay 93::1 94::1 +# ipv6 dhcp-relay source-interface Vlan100 +# ! +# interface Eth1/2 +# mtu 9100 +# speed 400000 +# fec RS +# no shutdown +# ip address 61.1.1.1/24 +# ip dhcp-relay 73.1.1.1 +# ip dhcp-relay circuit-id %h:%p +# ipv6 address 61::1/24 +# ! +# interface Eth1/3 +# mtu 9100 +# speed 400000 +# fec RS +# shutdown +# ip address 41.1.1.1/24 +# ipv6 address 41::1/24 +# ! + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.dhcp_relay.dhcp_relay import Dhcp_relayArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.dhcp_relay.dhcp_relay import Dhcp_relay + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=Dhcp_relayArgs.argument_spec, + supports_check_mode=True) + + result = Dhcp_relay(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_dhcp_snooping.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_dhcp_snooping.py new file mode 100644 index 000000000..948ecb891 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_dhcp_snooping.py @@ -0,0 +1,499 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_dhcp_snooping +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: sonic_dhcp_snooping +version_added: 2.3.0 +notes: + - "Tested against Enterprise SONiC Distribution by Dell Technologies." +short_description: "Manage DHCP Snooping on SONiC" +description: "This module provides configuration management of DHCP snooping for devices running SONiC." +author: Simon Nathans (@simon-nathans), Xiao Han (@Xiao_Han2) +options: + config: + description: The DHCP snooping configuration. + type: dict + suboptions: + afis: + description: + - List of address families to configure. + - "There can be up to two items in this list: one where I(afi=ipv4) and one where I(afi=ipv6) to configure DHCPv4 and DHCPv6, respectively." + type: list + elements: dict + suboptions: + afi: + description: + - The address family to configure. + type: str + choices: ['ipv4', 'ipv6'] + required: true + enabled: + description: + - Enable DHCP snooping for I(afi). + type: bool + vlans: + description: + - Enable DHCP snooping on a list of VLANs for I(afi). + - When I(state=deleted), passing an empty list will disable DHCP snooping in all VLANs + type: list + elements: str + verify_mac: + description: + - Enable DHCP snooping MAC verification for I(afi). + type: bool + trusted: + description: + - Mark interfaces as trusted for DHCP snooping for I(afi). + - When I(state=deleted), passing an empty list will delete all trusted interfaces. + type: list + elements: dict + suboptions: + intf_name: + description: + - The interface name. + type: str + required: true + source_bindings: + description: + - Create a static entry in the DHCP snooping binding database for I(afi). + - When I(state=deleted), passing an empty list will delete all source bindings. + type: list + elements: dict + suboptions: + mac_addr: + description: + - The binding's MAC address. + type: str + required: true + ip_addr: + description: + - The bindings's IP address. + type: str + intf_name: + description: + - The binding's interface name. + - Can be an Ethernet or a PortChannel interface. + type: str + vlan_id: + description: + - The binding's VLAN ID. + type: int + state: + description: + - The state of the configuration after module completion. + default: merged + choices: ['merged', 'deleted', 'overridden', 'replaced'] + type: str +""" +EXAMPLES = """ +# Using merged +# +# Before State: +# ------------- +# +# sonic# show ip dhcp snooping +# ! +# DHCP snooping is Disabled +# DHCP snooping source MAC verification is Disabled +# DHCP snooping is enabled on the following VLANs: +# DHCP snooping trusted interfaces: +# ! + +- name: Configure DHCPv4 snooping global settings + dellemc.enterprise_sonic.sonic_dhcp_snooping: + config: + afis: + - afi: 'ipv4' + enabled: true + verify_mac: true + vlans: ['1', '2', '3', '5'] + trusted: + - intf_name: 'Ethernet8' + state: merged + +# After State: +# ------------ +# +# sonic# show ip dhcp snooping +# ! +# DHCP snooping is Enabled +# DHCP snooping source MAC verification is Enabled +# DHCP snooping is enabled on the following VLANs: 1 2 3 5 +# DHCP snooping trusted interfaces: Ethernet8 +# ! + + +# Using merged +# +# Before State: +# ------------- +# +# sonic# show ipv6 dhcp snooping +# ! +# DHCPv6 snooping is Disabled +# DHCPv6 snooping source MAC verification is Disabled +# DHCPv6 snooping is enabled on the following VLANs: +# DHCPv6 snooping trusted interfaces: +# ! + +- name: Configure DHCPv6 snooping global settings + dellemc.enterprise_sonic.sonic_dhcp_snooping: + config: + afis: + - afi: 'ipv6' + enabled: true + vlans: + - '4' + trusted: + - intf_name: 'Ethernet2' + - intf_name: PortChannel1 + state: merged + +# After State: +# ------------ +# +# sonic# show ipv6 dhcp snooping +# ! +# DHCPv6 snooping is Enabled +# DHCPv6 snooping source MAC verification is Disabled +# DHCPv6 snooping is enabled on the following VLANs: 4 +# DHCPv6 snooping trusted interfaces: PortChannel1 +# ! + + +# Using merged +# +# Before State: +# ------------- +# +# sonic# show ip dhcp snooping binding +# ! +# Total number of Dynamic bindings: 0 +# Total number of Static bindings: 0 +# Total number of Tentative bindings: 0 +# MAC Address IP Address VLAN Interface Type Lease (Secs) +# ----------------- --------------- ---- ----------- ------- ----------- +# ! + +- name: Add DHCPv4 snooping bindings + dellemc.enterprise_sonic.sonic_dhcp_snooping: + config: + afis: + - afi: 'ipv4' + source_bindings: + - mac_addr: '00:b0:d0:63:c2:26' + ip_addr: '192.0.2.146' + intf_name: 'Ethernet4' + vlan_id: '1' + - mac_addr: 'aa:f7:67:fc:f4:9a' + ip_addr: '156.33.90.167' + intf_name: 'PortChannel1' + vlan_id: '2' + state: merged + +# After State: +# ------------ +# +# sonic# show ip dhcp snooping binding +# ! +# Total number of Dynamic bindings: 0 +# Total number of Static bindings: 2 +# Total number of Tentative bindings: 0 +# MAC Address IP Address VLAN Interface Type Lease (Secs) +# ----------------- --------------- ---- ----------- ------- ----------- +# 00:b0:d0:63:c2:26 192.0.2.146 1 Ethernet4 static NA +# aa:f7:67:fc:f4:9a 156.33.90.167 2 PortChannel1 static NA +# ! + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show ip dhcp snooping +# ! +# DHCP snooping is Enabled +# DHCP snooping source MAC verification is Enabled +# DHCP snooping is enabled on the following VLANs: 1 2 3 5 +# DHCP snooping trusted interfaces: Ethernet8 +# ! + +- name: Disable DHCPv4 snooping on some VLANs + dellemc.enterprise_sonic.sonic_dhcp_snooping: + config: + afis: + - afi: 'ipv4' + vlans: + - '3' + - '5' + state: deleted + +# After State: +# ------------ +# +# sonic# show ip dhcp snooping +# ! +# DHCP snooping is Enabled +# DHCP snooping source MAC verification is Enabled +# DHCP snooping is enabled on the following VLANs: 1 2 +# DHCP snooping trusted interfaces: +# ! + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show ipv6 dhcp snooping +# ! +# DHCPv6 snooping is Enabled +# DHCPv6 snooping source MAC verification is Disabled +# DHCPv6 snooping is enabled on the following VLANs: 4 +# DHCPv6 snooping trusted interfaces: PortChannel1 PortChannel2 PortChannel3 PortChannel4 +# ! + +- name: Disable DHCPv6 snooping on all VLANs + dellemc.enterprise_sonic.sonic_dhcp_snooping: + config: + afis: + - afi: 'ipv6' + vlans: [] + state: deleted + +# After State: +# ------------ +# +# sonic# show ipv6 dhcp snooping +# ! +# DHCPv6 snooping is Enabled +# DHCPv6 snooping source MAC verification is Disabled +# DHCPv6 snooping is enabled on the following VLANs: +# DHCPv6 snooping trusted interfaces: PortChannel1 PortChannel2 PortChannel3 PortChannel4 +# ! + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show ipv6 dhcp snooping +# ! +# DHCPv6 snooping is Enabled +# DHCPv6 snooping source MAC verification is Disabled +# DHCPv6 snooping is enabled on the following VLANs: 4 +# DHCPv6 snooping trusted interfaces: PortChannel1 PortChannel2 PortChannel3 PortChannel4 +# ! + +- name: Delete all DHCPv6 configuration + dellemc.enterprise_sonic.sonic_dhcp_snooping: + config: + afis: + - afi: 'ipv6' + state: deleted + +# After State: +# ------------ +# +# sonic# show ipv6 dhcp snooping +# ! +# DHCPv6 snooping is Disabled +# DHCPv6 snooping source MAC verification is Disabled +# DHCPv6 snooping is enabled on the following VLANs: +# DHCPv6 snooping trusted interfaces: +# ! + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show ip dhcp snooping binding +# ! +# Total number of Dynamic bindings: 0 +# Total number of Static bindings: 2 +# Total number of Tentative bindings: 0 +# MAC Address IP Address VLAN Interface Type Lease (Secs) +# ----------------- --------------- ---- ----------- ------- ----------- +# 00:b0:d0:63:c2:26 192.0.2.146 1 Ethernet4 static NA +# aa:f7:67:fc:f4:9a 156.33.90.167 2 PortChannel1 static NA +# ! + +- name: Delete a DHCPv4 snooping binding + dellemc.enterprise_sonic.sonic_dhcp_snooping: + config: + afis: + - afi: 'ipv4' + source_bindings: + - mac_addr: '00:b0:d0:63:c2:26' + ip_addr: '192.0.2.146' + intf_name: 'Ethernet4' + vlan_id: '1' + state: deleted + +# After State: +# ------------ +# +# sonic# show ip dhcp snooping binding +# ! +# Total number of Dynamic bindings: 0 +# Total number of Static bindings: 2 +# Total number of Tentative bindings: 0 +# MAC Address IP Address VLAN Interface Type Lease (Secs) +# ----------------- --------------- ---- ----------- ------- ----------- +# aa:f7:67:fc:f4:9a 156.33.90.167 2 PortChannel1 static NA +# ! + + +# Using overridden +# +# Before State: +# ------------- +# +# sonic# show ipv4 dhcp snooping binding +# ! +# MAC Address IP Address VLAN Interface Type Lease (Secs) +# ----------------- --------------- ---- ----------- ------- ----------- +# 00:b0:d0:63:c2:26 192.0.2.146 1 Ethernet4 static NA +# 28:21:28:15:c1:1b 141.202.222.118 1 Ethernet2 static NA +# aa:f7:67:fc:f4:9a 156.33.90.167 2 PortChannel1 static NA +# ! + +- name: Override DHCPv4 snooping bindings + dellemc.enterprise_sonic.sonic_dhcp_snooping: + config: + afis: + - afi: 'ipv4' + source_bindings: + - mac_addr: '00:b0:d0:63:c2:26' + ip_addr: '192.0.2.146' + intf_name: 'Ethernet4' + vlan_id: '3' + state: overridden + +# After State: +# ------------ +# +# sonic# show ipv4 dhcp snooping binding +# ! +# MAC Address IP Address VLAN Interface Type Lease (Secs) +# ----------------- --------------- ---- ----------- ------- ----------- +# 00:b0:d0:63:c2:26 192.0.2.146 3 Ethernet4 static NA +# ! + + +# Using replaced +# +# Before State: +# ------------- +# +# sonic# show ipv4 dhcp snooping binding +# ! +# MAC Address IP Address VLAN Interface Type Lease (Secs) +# ----------------- --------------- ---- ----------- ------- ----------- +# 00:b0:d0:63:c2:26 192.0.2.146 1 Ethernet4 static NA +# 28:21:28:15:c1:1b 141.202.222.118 1 Ethernet2 static NA +# aa:f7:67:fc:f4:9a 156.33.90.167 2 PortChannel1 static NA +# ! + +- name: Replace DHCPv4 snooping bindings + dellemc.enterprise_sonic.sonic_dhcp_snooping: + config: + afis: + - afi: 'ipv4' + source_bindings: + - mac_addr: '00:b0:d0:63:c2:26' + ip_addr: '192.0.2.146' + intf_name: 'Ethernet4' + vlan_id: '3' + state: replaced + +# After State: +# ------------ +# +# sonic# show ipv4 dhcp snooping binding +# ! +# MAC Address IP Address VLAN Interface Type Lease (Secs) +# ----------------- --------------- ---- ----------- ------- ----------- +# 00:b0:d0:63:c2:26 192.0.2.146 3 Ethernet4 static NA +# 28:21:28:15:c1:1b 141.202.222.118 1 Ethernet2 static NA +# aa:f7:67:fc:f4:9a 156.33.90.167 2 PortChannel1 static NA +# ! + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: dict + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: dict + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.dhcp_snooping.dhcp_snooping import Dhcp_snoopingArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.dhcp_snooping.dhcp_snooping import Dhcp_snooping + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=Dhcp_snoopingArgs.argument_spec, + supports_check_mode=True) + + result = Dhcp_snooping(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_facts.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_facts.py index f13e9defd..3fa261381 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_facts.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_facts.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -67,6 +67,7 @@ options: - bgp_ext_communities - mclag - prefix_lists + - vlan_mapping - vrfs - vxlans - users @@ -77,6 +78,21 @@ options: - radius_server - static_routes - ntp + - logging + - pki + - ip_neighbor + - port_group + - dhcp_relay + - acl_interfaces + - l2_acls + - l3_acls + - lldp_global + - mac + - bfd + - copp + - route_maps + - stp + - dhcp_snooping """ EXAMPLES = """ diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_interfaces.py index 0cd6a1896..09a5d0e18 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_interfaces.py @@ -63,12 +63,53 @@ options: description: - MTU of the interface. type: int + speed: + description: + - Interface speed. + - Supported speeds are dependent on the type of switch. + type: str + choices: + - SPEED_10MB + - SPEED_100MB + - SPEED_1GB + - SPEED_2500MB + - SPEED_5GB + - SPEED_10GB + - SPEED_20GB + - SPEED_25GB + - SPEED_40GB + - SPEED_50GB + - SPEED_100GB + - SPEED_400GB + auto_negotiate: + description: + - auto-negotiate transmission parameters with peer interface. + type: bool + advertised_speed: + description: + - Advertised speeds of the interface. + - Supported speeds are dependent on the type of switch. + - Speeds may be 10, 100, 1000, 2500, 5000, 10000, 20000, 25000, 40000, 50000, 100000 or 400000. + type: list + elements: str + fec: + description: + - Interface FEC (Forward Error Correction). + type: str + choices: + - FEC_RS + - FEC_FC + - FEC_DISABLED + - FEC_DEFAULT + - FEC_AUTO state: description: - The state the configuration should be left in. type: str choices: - merged + - replaced + - overridden - deleted default: merged """ @@ -80,18 +121,28 @@ EXAMPLES = """ # # show interface status | no-more #------------------------------------------------------------------------------------------ -#Name Description Admin Oper Speed MTU +#Name Description Admin Oper AutoNeg Speed MTU #------------------------------------------------------------------------------------------ -#Eth1/1 - up 100000 9100 -#Eth1/2 - up 100000 9100 -#Eth1/3 - down 100000 9100 -#Eth1/3 - down 1000 5000 -#Eth1/5 - down 100000 9100 +#Ethernet0 - up 100000 9100 +#Ethernet4 - up 100000 9100 +#Ethernet8 Ethernet-8 down 100000 9100 +#Ethernet12 Ethernet-12 down on - 5000 +#Ethernet16 - down 40000 9100 # -- name: Configures interfaces - dellemc.enterprise_sonic.sonic_interfaces: +# show running-configuration interface Ethernet 8 +#! +#interface Ethernet8 +# mtu 9100 +# speed 100000 +# fec AUTO +# shutdown +# +- name: Configure interfaces + sonic_interfaces: config: - name: Eth1/3 + - name: Ethernet8 + - name: Ethernet12 + - name: Ethernet16 state: deleted # # After state: @@ -99,14 +150,20 @@ EXAMPLES = """ # # show interface status | no-more #------------------------------------------------------------------------------------------ -#Name Description Admin Oper Speed MTU +#Name Description Admin Oper AutoNeg Speed MTU #------------------------------------------------------------------------------------------ -#Eth1/1 - up 100000 9100 -#Eth1/2 - up 100000 9100 -#Eth1/3 - down 100000 9100 -#Eth1/3 - up 100000 9100 -#Eth1/5 - down 100000 9100 +#Ethernet0 - up 100000 9100 +#Ethernet4 - up 100000 9100 +#Ethernet8 - up 100000 9100 +#Ethernet12 - up 100000 9100 +#Ethernet16 - up 100000 9100 # +# show running-configuration interface Ethernet 8 +#! +#interface Ethernet8 +# mtu 9100 +# speed 100000 +# shutdown # # Using deleted # @@ -115,33 +172,33 @@ EXAMPLES = """ # # show interface status | no-more #------------------------------------------------------------------------------------------ -#Name Description Admin Oper Speed MTU +#Name Description Admin Oper AutoNeg Speed MTU #------------------------------------------------------------------------------------------ -#Eth1/1 - up 100000 9100 -#Eth1/2 - up 100000 9100 -#Eth1/3 - down 100000 9100 -#Eth1/3 - down 1000 9100 -#Eth1/5 - down 100000 9100 +#Ethernet0 - up 100000 9100 +#Ethernet4 - up 100000 9100 +#Ethernet8 - down 100000 9100 +#Ethernet12 - down 1000 9100 +#Ethernet16 - down 100000 9100 # - -- name: Configures interfaces - dellemc.enterprise_sonic.sonic_interfaces: +- name: Configure interfaces + sonic_interfaces: config: - state: deleted + state: deleted # # After state: # ------------- # # show interface status | no-more #------------------------------------------------------------------------------------------ -#Name Description Admin Oper Speed MTU +#Name Description Admin Oper AutoNeg Speed MTU #------------------------------------------------------------------------------------------ -#Eth1/1 - up 100000 9100 -#Eth1/2 - up 100000 9100 -#Eth1/3 - up 100000 9100 -#Eth1/3 - up 100000 9100 -#Eth1/5 - up 100000 9100 +#Ethernet0 - up 100000 9100 +#Ethernet4 - up 100000 9100 +#Ethernet8 - up 100000 9100 +#Ethernet12 - up 100000 9100 +#Ethernet16 - up 100000 9100 +# # # # Using merged @@ -151,38 +208,177 @@ EXAMPLES = """ # # show interface status | no-more #------------------------------------------------------------------------------------------ -#Name Description Admin Oper Speed MTU +#Name Description Admin Oper AutoNeg Speed MTU #------------------------------------------------------------------------------------------ -#Eth1/1 - up 100000 9100 -#Eth1/2 - up 100000 9100 -#Eth1/3 - down 100000 9100 -#Eth1/3 - down 1000 9100 +#Ethernet0 - up 100000 9100 +#Ethernet4 - up 100000 9100 +#Ethernet8 - down 100000 9100 +#Ethernet12 - down 100000 9100 +#Ethernet16 - down 100000 9100 +# +# show running-configuration interface Ethernet 8 +#! +#interface Ethernet8 +# mtu 9100 +# speed 100000 +# shutdown # -- name: Configures interfaces - dellemc.enterprise_sonic.sonic_interfaces: +- name: Configure interfaces + sonic_interfaces: config: - - name: Eth1/3 - description: 'Ethernet Twelve' - - name: Eth1/5 - description: 'Ethernet Sixteen' - enable: True - mtu: 3500 + - name: Ethernet8 + fec: FEC_AUTO + - name: Ethernet12 + description: 'Ethernet Twelve' + auto_negotiate: True + - name: Ethernet16 + description: 'Ethernet Sixteen' + enabled: True + mtu: 3500 + speed: SPEED_40GB state: merged # +# After state: +# ------------ +# +# show interface status | no-more +#------------------------------------------------------------------------------------------ +#Name Description Admin Oper AutoNeg Speed MTU +#------------------------------------------------------------------------------------------ +#Ethernet0 - up 100000 9100 +#Ethernet4 - up 100000 9100 +#Ethernet8 - down 100000 9100 +#Ethernet12 Ethernet Twelve down on 100000 9100 +#Ethernet16 Ethernet Sixteen up 40000 3500 +# +# show running-configuration interface Ethernet 8 +#! +#interface Ethernet8 +# mtu 9100 +# speed 100000 +# fec AUTO +# shutdown +# +# Using overridden +# +# Before state: +# ------------- +# +# show interface status | no-more +#------------------------------------------------------------------------------------------ +#Name Description Admin Oper AutoNeg Speed MTU +#------------------------------------------------------------------------------------------ +#Ethernet0 E0 up 100000 9100 +#Ethernet4 E4 up 100000 9100 +#Ethernet8 E8 down 100000 9100 +#Ethernet12 - down 1000 9100 +#Ethernet16 - down 100000 9100 +# +# show running-configuration interface Ethernet 8 +#! +#interface Ethernet8 +# mtu 9100 +# speed 100000 +# shutdown +# +- name: Configure interfaces + sonic_interfaces: + config: + - name: Ethernet8 + fec: FEC_AUTO + - name: Ethernet12 + description: 'Ethernet Twelve' + mtu: 3500 + enabled: True + auto_negotiate: True + - name: Ethernet16 + description: 'Ethernet Sixteen' + mtu: 3000 + enabled: False + speed: SPEED_40GB + state: overridden +# +# After state: +# ------------ +# +# show interface status | no-more +#------------------------------------------------------------------------------------------ +#Name Description Admin Oper AutoNeg Speed MTU +#------------------------------------------------------------------------------------------ +#Ethernet0 - up 100000 9100 +#Ethernet4 - up 100000 9100 +#Ethernet8 - up 100000 9100 +#Ethernet12 Ethernet Twelve up on 100000 3500 +#Ethernet16 Ethernet Sixteen down 40000 3000 +# +# show running-configuration interface Ethernet 8 +#! +#interface Ethernet8 +# mtu 9100 +# speed 100000 +# fec AUTO +# no shutdown +# +# Using replaced +# +# Before state: +# ------------- +# +# show interface status | no-more +#------------------------------------------------------------------------------------------ +#Name Description Admin Oper AutoNeg Speed MTU +#------------------------------------------------------------------------------------------ +#Ethernet0 - up 100000 9100 +#Ethernet4 - up 100000 9100 +#Ethernet8 - down on 100000 9100 +#Ethernet12 - down 1000 9100 +#Ethernet16 - down 100000 9100 +# +# show running-configuration interface Ethernet 8 +#! +#interface Ethernet8 +# mtu 9100 +# speed auto 40000 +# shutdown +# +- name: Configure interfaces + sonic_interfaces: + config: + - name: Ethernet8 + advertised_speed: + - "100000" + - name: Ethernet12 + description: 'Ethernet Twelve' + mtu: 3500 + enabled: True + auto_negotiate: True + - name: Ethernet16 + description: 'Ethernet Sixteen' + mtu: 3000 + enabled: False + speed: SPEED_40GB + state: replaced # # After state: # ------------ # # show interface status | no-more #------------------------------------------------------------------------------------------ -#Name Description Admin Oper Speed MTU +#Name Description Admin Oper AutoNeg Speed MTU #------------------------------------------------------------------------------------------ -#Eth1/1 - up 100000 9100 -#Eth1/2 - up 100000 9100 -#Eth1/3 - down 100000 9100 -#Eth1/4 - down 1000 9100 -#Eth1/5 - down 100000 3500 +#Ethernet0 - up 100000 9100 +#Ethernet4 - up 100000 9100 +#Ethernet8 - down on 100000 9100 +#Ethernet12 Ethernet Twelve up on 100000 3500 +#Ethernet16 Ethernet Sixteen down 40000 3000 # +# show running-configuration interface Ethernet 8 +#! +#interface Ethernet8 +# mtu 9100 +# speed auto 100000 +# fec AUTO +# shutdown # """ RETURN = """ diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_ip_neighbor.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_ip_neighbor.py new file mode 100644 index 000000000..f1e4acc82 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_ip_neighbor.py @@ -0,0 +1,300 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_ip_neighbor +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = """ +--- +module: sonic_ip_neighbor +version_added: 2.1.0 +notes: + - Supports C(check_mode). +short_description: Manage IP neighbor global configuration on SONiC. +description: + - This module provides configuration management of IP neighbor global for devices running SONiC. +author: "M. Zhang (@mingjunzhang2019)" +options: + config: + description: + - Specifies IP neighbor global configurations. + type: dict + suboptions: + ipv4_arp_timeout: + type: int + description: + - IPv4 ARP timeout. + - The range is from 60 to 14400. + ipv6_nd_cache_expiry: + type: int + description: + - IPv6 ND cache expiry. + - The range is from 60 to 14400. + num_local_neigh: + type: int + description: + - The number of reserved local neighbors. + - The range is from 0 to 32000. + ipv4_drop_neighbor_aging_time: + type: int + description: + - IPv4 drop neighbor aging time. + - The range is from 60 to 14400. + ipv6_drop_neighbor_aging_time: + type: int + description: + - IPv6 drop neighbor aging time. + - The range is from 60 to 14400. + state: + description: + - The state of the configuration after module completion. + type: str + choices: + - merged + - replaced + - overridden + - deleted + default: merged +""" +EXAMPLES = """ +# +# Using merged +# +# Before state: +# ------------- +# +#sonic# show running-configuration +#! +#ip arp timeout 180 +#ip drop-neighbor aging-time 300 +#ipv6 drop-neighbor aging-time 300 +#ip reserve local-neigh 0 +#ipv6 nd cache expire 180 +#! +- name: Configure IP neighbor global + sonic_ip_neighbor: + config: + ipv4_arp_timeout: 1200 + ipv4_drop_neighbor_aging_time: 600 + ipv6_drop_neighbor_aging_time: 600 + ipv6_nd_cache_expiry: 1200 + num_local_neigh: 1000 + state: merged + +# After state: +# ------------ +# +#sonic# show running-configuration +#! +#ip arp timeout 1200 +#ip drop-neighbor aging-time 600 +#ipv6 drop-neighbor aging-time 600 +#ip reserve local-neigh 1000 +#ipv6 nd cache expire 1200 +#! +# +# Using deleted +# +# Before state: +# ------------- +# +#sonic# show running-configuration +#! +#ip arp timeout 1200 +#ip drop-neighbor aging-time 600 +#ipv6 drop-neighbor aging-time 600 +#ip reserve local-neigh 1000 +#ipv6 nd cache expire 1200 +#! +- name: Delete some IP neighbor configuration + sonic_ip_neighbor: + config: + ipv4_arp_timeout: 0 + ipv4_drop_neighbor_aging_time: 0 + state: deleted + +# After state: +# ------------ +# +#sonic# show running-configuration +#! +#ip arp timeout 180 +#ip drop-neighbor aging-time 300 +#ipv6 drop-neighbor aging-time 600 +#ip reserve local-neigh 1000 +#ipv6 nd cache expire 1200 +#! +# +# Using deleted +# +# Before state: +# ------------- +# +#sonic# show running-configuration +#! +#ip arp timeout 1200 +#ip drop-neighbor aging-time 600 +#ipv6 drop-neighbor aging-time 600 +#ip reserve local-neigh 1000 +#ipv6 nd cache expire 1200 +#! +- name: Delete all IP neighbor configuration + sonic_ip_neighbor: + config: {} + state: deleted + +# After state: +# ------------ +# +#sonic# show running-configuration +#! +#ip arp timeout 180 +#ip drop-neighbor aging-time 300 +#ipv6 drop-neighbor aging-time 300 +#ip reserve local-neigh 0 +#ipv6 nd cache expire 180 +#! +# +# Using replaced +# +# Before state: +# ------------- +# +#sonic# show running-configuration +#! +#ip arp timeout 1200 +#ip drop-neighbor aging-time 600 +#ipv6 drop-neighbor aging-time 300 +#ip reserve local-neigh 0 +#ipv6 nd cache expire 180 +#! +- name: Change some IP neighbor configuration + sonic_ip_neighbor: + config: + ipv6_drop_neighbor_aging_time: 600 + ipv6_nd_cache_expiry: 1200 + num_local_neigh: 1000 + state: replaced + +# After state: +# ------------ +# +#sonic# show running-configuration +#! +#ip arp timeout 1200 +#ip drop-neighbor aging-time 600 +#ipv6 drop-neighbor aging-time 600 +#ip reserve local-neigh 1000 +#ipv6 nd cache expire 1200 +#! +# +# Using overridden +# +# Before state: +# ------------- +# +#sonic# show running-configuration +#! +#ip arp timeout 1200 +#ip drop-neighbor aging-time 600 +#ipv6 drop-neighbor aging-time 300 +#ip reserve local-neigh 0 +#ipv6 nd cache expire 180 +#! +- name: Reset IP neighbor configuration, then configure some + sonic_ip_neighbor: + config: + ipv6_drop_neighbor_aging_time: 600 + ipv6_nd_cache_expiry: 1200 + num_local_neigh: 1000 + state: overridden + +# After state: +# ------------ +# +#sonic# show running-configuration +#! +#ip arp timeout 180 +#ip drop-neighbor aging-time 300 +#ipv6 drop-neighbor aging-time 600 +#ip reserve local-neigh 1000 +#ipv6 nd cache expire 1200 +#! +# +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after(generated): + description: The generated configuration model invocation. + returned: when C(check_mode) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.ip_neighbor.ip_neighbor import Ip_neighborArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.ip_neighbor.ip_neighbor import Ip_neighbor + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=Ip_neighborArgs.argument_spec, + supports_check_mode=True) + + result = Ip_neighbor(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l2_acls.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l2_acls.py new file mode 100644 index 000000000..cda50242b --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l2_acls.py @@ -0,0 +1,582 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_l2_acls +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: sonic_l2_acls +version_added: '2.1.0' +notes: + - Supports C(check_mode). +short_description: Manage Layer 2 access control lists (ACL) configurations on SONiC +description: + - This module provides configuration management of Layer 2 access control lists (ACL) + in devices running SONiC. +author: 'Arun Saravanan Balachandran (@ArunSaravananBalachandran)' +options: + config: + description: + - Specifies Layer 2 ACL configurations. + type: list + elements: dict + suboptions: + name: + description: + - Specifies the ACL name. + type: str + required: true + remark: + description: + - Specifies remark for the ACL. + type: str + rules: + description: + - List of rules with the ACL. + - I(sequence_num), I(action), I(source) & I(destination) are required for adding a new rule. + - If I(state=deleted), options other than I(sequence_num) are not considered. + - I(ethertype) and I(vlan_tag_format) are mutually exclusive. + type: list + elements: dict + suboptions: + sequence_num: + description: + - Specifies the sequence number of the rule. + - The range is from 1 to 65535. + type: int + required: true + action: + description: + - Specifies the action taken on the matched Ethernet frame. + type: str + choices: + - deny + - discard + - do-not-nat + - permit + - transit + source: + description: + - Specifies the source of the Ethernet frame. + - I(address) and I(address_mask) are required together. + - I(any), I(host) and I(address) are mutually exclusive. + type: dict + suboptions: + any: + description: + - Match any source MAC address. + type: bool + host: + description: + - MAC address of a single source host. + type: str + address: + description: + - Source MAC address. + type: str + address_mask: + description: + - Source MAC address mask. + type: str + destination: + description: + - Specifies the destination of the Ethernet frame. + - I(address) and I(address_mask) are required together. + - I(any), I(host) and I(address) are mutually exclusive. + type: dict + suboptions: + any: + description: + - Match any destination MAC address. + type: bool + host: + description: + - MAC address of a single destination host. + type: str + address: + description: + - Destination MAC address. + type: str + address_mask: + description: + - Destination MAC address mask. + type: str + ethertype: + description: + - Specifies the EtherType of the Ethernet frame. + - Only one suboption can be specified for ethertype in a rule. + type: dict + suboptions: + value: + description: + - Specifies the EtherType value to match as a hexadecimal string. + - The range is from 0x600 to 0xffff. + type: str + arp: + description: + - Match Ethernet frame with ARP EtherType (0x806). + type: bool + ipv4: + description: + - Match Ethernet frame with IPv4 EtherType (0x800). + type: bool + ipv6: + description: + - Match Ethernet frame with IPv6 EtherType (0x86DD). + type: bool + vlan_id: + description: + - Match Ethernet frame with the given VLAN ID. + type: int + vlan_tag_format: + description: + - Match Ethernet frame with the given VLAN tag format. + type: dict + suboptions: + multi_tagged: + description: + - Match three of more VLAN tagged Ethernet frame. + type: bool + dei: + description: + - Match Ethernet frame with the given Drop Eligible Indicator (DEI) value. + type: int + choices: + - 0 + - 1 + pcp: + description: + - Match Ethernet frames using Priority Code Point (PCP) value. + - I(mask) is valid only when I(value) is specified. + - I(value) and I(traffic_type) are mutually exclusive. + type: dict + suboptions: + value: + description: + - Match Ethernet frame with the given PCP value. + - The range is from 0 to 7 + type: int + mask: + description: + - Match Ethernet frame with given PCP value and mask. + - The range is from 0 to 7. + type: int + traffic_type: + description: + - Match Ethernet frame with PCP value for the given traffic type. + - C(be) - Match Ethernet frame with Best effort PCP (0). + - C(bk) - Match Ethernet frame with Background PCP (1). + - C(ee) - Match Ethernet frame with Excellent effort PCP (2). + - C(ca) - Match Ethernet frame with Critical applications PCP (3). + - C(vi) - Match Ethernet frame with Video, < 100 ms latency and jitter PCP (4). + - C(vo) - Match Ethernet frame with Voice, < 10 ms latency and jitter PCP (5). + - C(ic) - Match Ethernet frame with Internetwork control PCP (6). + - C(nc) - Match Ethernet frame with Network control PCP (7). + type: str + choices: + - be + - bk + - ee + - ca + - vi + - vo + - ic + - nc + remark: + description: + - Specifies remark for the ACL rule. + type: str + state: + description: + - The state of the configuration after module completion. + - C(merged) - Merges provided L2 ACL configuration with on-device configuration. + - C(replaced) - Replaces on-device configuration of the specified L2 ACLs with provided configuration. + - C(overridden) - Overrides all on-device L2 ACL configurations with the provided configuration. + - C(deleted) - Deletes on-device L2 ACL configuration. + type: str + choices: + - merged + - replaced + - overridden + - deleted + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before State: +# ------------- +# +# sonic# show running-configuration mac access-list +# ! +# mac access-list test +# seq 1 permit host 22:22:22:22:22:22 any vlan 20 +# sonic# + + - name: Merge provided Layer 2 ACL configurations + dellemc.enterprise_sonic.sonic_l2_acls: + config: + - name: 'test' + rules: + - sequence_num: 2 + action: 'permit' + source: + any: true + destination: + any: true + ethertype: + value: '0x88cc' + remark: 'LLDP' + - sequence_num: 3 + action: 'permit' + source: + any: true + destination: + address: '00:00:10:00:00:00' + address_mask: '00:00:ff:ff:00:00' + pcp: + value: 4 + mask: 6 + - sequence_num: 4 + action: 'deny' + source: + any: true + destination: + any: true + vlan_tag_format: + multi_tagged: true + - name: 'test1' + remark: 'test_mac_acl' + rules: + - sequence_num: 1 + action: 'permit' + source: + host: '11:11:11:11:11:11' + destination: + any: true + - sequence_num: 2 + action: 'permit' + source: + any: true + destination: + any: true + ethertype: + arp: true + vlan_id: 100 + - sequence_num: 3 + action: 'deny' + source: + any: true + destination: + any: true + dei: 0 + state: merged + +# After State: +# ------------ +# +# sonic# show running-configuration mac access-list +# ! +# mac access-list test +# seq 1 permit host 22:22:22:22:22:22 any vlan 20 +# seq 2 permit any any 0x88cc remark LLDP +# seq 3 permit any 00:00:10:00:00:00 00:00:ff:ff:00:00 pcp vi pcp-mask 6 +# seq 4 deny any any vlan-tag-format multi-tagged +# ! +# mac access-list test1 +# remark test_mac_acl +# seq 1 permit host 11:11:11:11:11:11 any +# seq 2 permit any any arp vlan 100 +# seq 3 deny any any dei 0 +# sonic# + + +# Using replaced +# +# Before State: +# ------------- +# +# sonic# show running-configuration mac access-list +# ! +# mac access-list test +# seq 1 permit host 22:22:22:22:22:22 any vlan 20 +# seq 2 permit any any 0x88cc remark LLDP +# seq 3 permit any 00:00:10:00:00:00 00:00:ff:ff:00:00 pcp vi pcp-mask 6 +# ! +# mac access-list test1 +# remark test_mac_acl +# seq 1 permit host 11:11:11:11:11:11 any +# seq 2 permit any any arp vlan 100 +# seq 3 deny any any dei 0 +# sonic# + + - name: Replace device configuration of specified Layer 2 ACLs with provided configuration + dellemc.enterprise_sonic.sonic_l2_acls: + config: + - name: 'test1' + rules: + - sequence_num: 1 + action: 'permit' + source: + any: true + destination: + any: true + ethertype: + arp: true + vlan_id: 200 + - sequence_num: 2 + action: 'discard' + source: + any: true + destination: + any: true + - name: 'test2' + rules: + - sequence_num: 1 + action: 'permit' + source: + host: '33:33:33:33:33:33' + destination: + host: '44:44:44:44:44:44' + state: replaced + +# After State: +# ------------ +# +# sonic# show running-configuration mac access-list +# ! +# mac access-list test +# seq 1 permit host 22:22:22:22:22:22 any vlan 20 +# seq 2 permit any any 0x88cc remark LLDP +# seq 3 permit any 00:00:10:00:00:00 00:00:ff:ff:00:00 pcp vi pcp-mask 6 +# ! +# mac access-list test1 +# seq 1 permit any any arp vlan 200 +# seq 2 discard any any +# ! +# mac access-list test2 +# seq 1 permit host 33:33:33:33:33:33 host 44:44:44:44:44:44 +# sonic# + + +# Using overridden +# +# Before State: +# ------------- +# +# sonic# show running-configuration mac access-list +# ! +# mac access-list test +# seq 1 permit host 22:22:22:22:22:22 any vlan 20 +# seq 2 permit any any 0x88cc remark LLDP +# seq 3 permit any 00:00:10:00:00:00 00:00:ff:ff:00:00 pcp vi pcp-mask 6 +# ! +# mac access-list test1 +# seq 1 permit any any arp vlan 200 +# seq 2 discard any any +# ! +# mac access-list test2 +# seq 1 permit host 33:33:33:33:33:33 host 44:44:44:44:44:44 +# sonic# + + - name: Override device configuration of all Layer 2 ACLs with provided configuration + dellemc.enterprise_sonic.sonic_l2_acls: + config: + - name: 'test1' + remark: 'test_mac_acl' + rules: + - sequence_num: 1 + action: 'permit' + source: + host: '11:11:11:11:11:11' + destination: + any: true + vlan_id: 100 + - sequence_num: 2 + action: 'permit' + source: + any: true + destination: + any: true + pcp: + traffic_type: 'ca' + - sequence_num: 3 + action: 'deny' + source: + any: true + destination: + any: true + ethertype: + ipv4: true + state: overridden + +# After State: +# ------------ +# +# sonic# show running-configuration mac access-list +# ! +# mac access-list test1 +# remark test_mac_acl +# seq 1 permit host 11:11:11:11:11:11 any vlan 100 +# seq 2 permit any any pcp ca +# seq 3 deny any any ip +# sonic# + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show running-configuration mac access-list +# ! +# mac access-list test +# seq 1 permit host 22:22:22:22:22:22 any vlan 20 +# seq 2 permit any any 0x88cc remark LLDP +# seq 3 permit any 00:00:10:00:00:00 00:00:ff:ff:00:00 pcp vi pcp-mask 6 +# ! +# mac access-list test1 +# remark test_mac_acl +# seq 1 permit host 11:11:11:11:11:11 any vlan 100 +# seq 2 deny any any ip +# ! +# mac access-list test2 +# seq 1 permit host 33:33:33:33:33:33 host 44:44:44:44:44:44 +# sonic# + + - name: Delete specified Layer 2 ACLs, ACL remark and ACL rule entries + dellemc.enterprise_sonic.sonic_l2_acls: + config: + - name: 'test' + rules: + - sequence_num: 3 + - name: 'test1' + remark: 'test_mac_acl' + - name: 'test2' + state: deleted + +# After State: +# ------------ +# +# sonic# show running-configuration mac access-list +# ! +# mac access-list test +# seq 1 permit host 22:22:22:22:22:22 any vlan 20 +# seq 2 permit any any 0x88cc remark LLDP +# ! +# mac access-list test1 +# seq 1 permit host 11:11:11:11:11:11 any vlan 100 +# seq 2 deny any any ip +# sonic# + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show running-configuration mac access-list +# ! +# mac access-list test +# seq 1 permit host 22:22:22:22:22:22 any vlan 20 +# seq 2 permit any any 0x88cc remark LLDP +# seq 3 permit any 00:00:10:00:00:00 00:00:ff:ff:00:00 pcp vi pcp-mask 6 +# ! +# mac access-list test1 +# remark test_mac_acl +# seq 1 permit host 11:11:11:11:11:11 any vlan 100 +# seq 2 deny any any ip +# ! +# mac access-list test2 +# seq 1 permit host 33:33:33:33:33:33 host 44:44:44:44:44:44 +# sonic# + + - name: Delete all Layer 2 ACL configurations + dellemc.enterprise_sonic.sonic_l2_acls: + config: + state: deleted + +# After State: +# ------------ +# +# sonic# show running-configuration mac access-list +# sonic# + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after(generated): + description: The generated configuration model invocation. + returned: when C(check_mode) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.l2_acls.l2_acls import L2_aclsArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.l2_acls.l2_acls import L2_acls + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=L2_aclsArgs.argument_spec, + supports_check_mode=True) + + result = L2_acls(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l2_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l2_interfaces.py index 34a8ff720..8d70f8a3f 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l2_interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l2_interfaces.py @@ -54,13 +54,13 @@ options: description: Configures trunking parameters on an interface. suboptions: allowed_vlans: - description: Specifies list of allowed VLANs of trunk mode on the interface. + description: Specifies a list of allowed trunk mode VLANs and VLAN ranges for the interface. type: list elements: dict suboptions: vlan: - type: int - description: Configures the specified VLAN in trunk mode. + type: str + description: Configures the specified trunk mode VLAN or VLAN range. access: type: dict description: Configures access mode characteristics of the interface. @@ -74,6 +74,8 @@ options: choices: - merged - deleted + - replaced + - overridden default: merged """ EXAMPLES = """ @@ -145,6 +147,47 @@ EXAMPLES = """ #15 Inactive # # +# Using deleted +# +# Before state: +# ------------- +# +#do show Vlan +#Q: A - Access (Untagged), T - Tagged +#NUM Status Q Ports +#11 Inactive T Ethernet12 +#12 Inactive A Ethernet12 +#13 Inactive T Ethernet12 +#14 Inactive T Ethernet12 +#15 Inactive T Ethernet12 +#16 Inactive T Ethernet12 + +- name: Delete the access vlan and a range of trunk vlans for an interface + sonic_l2_interfaces: + config: + - name: Ethernet12 + access: + vlan: 12 + trunk: + allowed_vlans: + - vlan: 13-16 + state: deleted + +# After state: +# ------------ +# +#do show Vlan +#Q: A - Access (Untagged), T - Tagged +#NUM Status Q Ports +#11 Inactive T Ethernet12 +#12 Inactive +#13 Inactive +#14 Inactive +#15 Inactive +#16 Inactive +# +# +# # Using merged # # Before state: @@ -153,10 +196,11 @@ EXAMPLES = """ #do show Vlan #Q: A - Access (Untagged), T - Tagged #NUM Status Q Ports +#10 Inactive #11 Inactive T Eth1/7 #12 Inactive T Eth1/7 # -- name: Configures switch port of interfaces +- name: Configures an access vlan for an interface dellemc.enterprise_sonic.sonic_l2_interfaces: config: - name: Eth1/3 @@ -184,15 +228,23 @@ EXAMPLES = """ #Q: A - Access (Untagged), T - Tagged #NUM Status Q Ports #10 Inactive A Eth1/3 +#12 Inactive +#13 Inactive +#14 Inactive +#15 Inactive +#16 Inactive +#18 Inactive # -- name: Configures switch port of interfaces +- name: Modify the access vlan, add a range of trunk vlans and a single trunk vlan for an interface dellemc.enterprise_sonic.sonic_l2_interfaces: config: - name: Eth1/3 + access: + vlan: 12 trunk: allowed_vlans: - - vlan: 11 - - vlan: 12 + - vlan: 13-16 + - vlan: 18 state: merged # # After state: @@ -201,9 +253,13 @@ EXAMPLES = """ #do show Vlan #Q: A - Access (Untagged), T - Tagged #NUM Status Q Ports -#10 Inactive A Eth1/3 -#11 Inactive T Eth1/7 -#12 Inactive T Eth1/7 +#10 Inactive +#12 Inactive A Eth1/3 +#13 Inactive T Eth1/3 +#14 Inactive T Eth1/3 +#15 Inactive T Eth1/3 +#16 Inactive T Eth1/3 +#18 Inactive T Eth1/3 # # # Using merged @@ -250,6 +306,89 @@ EXAMPLES = """ #15 Inactive T Eth1/5 # # +# Using replaced +# +# Before state: +# ------------- +# +#do show Vlan +#Q: A - Access (Untagged), T - Tagged +#NUM Status Q Ports +#10 Inactive A Ethernet12 +# A Ethernet13 +#11 Inactive T Ethernet12 +# T Ethernet13 + +- name: Replace access vlan and trunk vlans for specified interfaces + sonic_l2_interfaces: + config: + - name: Ethernet12 + access: + vlan: 12 + trunk: + allowed_vlans: + - vlan: 13-14 + - name: Ethernet14 + access: + vlan: 10 + trunk: + allowed_vlans: + - vlan: 11 + - vlan: 13-14 + state: replaced + +# After state: +# ------------ +# +#do show Vlan +#Q: A - Access (Untagged), T - Tagged +#NUM Status Q Ports +#10 Inactive A Ethernet13 +# A Ethernet14 +#11 Inactive T Ethernet13 +# T Ethernet14 +#12 Inactive A Ethernet12 +#13 Inactive T Ethernet12 +# T Ethernet14 +#14 Inactive T Ethernet12 +# T Ethernet14 +# +# +# Using overridden +# +# Before state: +# ------------- +# +#do show Vlan +#Q: A - Access (Untagged), T - Tagged +#NUM Status Q Ports +#10 Inactive A Ethernet11 +#11 Inactive T Ethernet11 +#12 Inactive A Ethernet12 +#13 Inactive T Ethernet12 + +- name: Override L2 interfaces configuration in device with provided configuration + sonic_l2_interfaces: + config: + - name: Ethernet13 + access: + vlan: 12 + trunk: + allowed_vlans: + - vlan: 13-14 + state: overridden + +# After state: +# ------------ +# +#do show Vlan +#Q: A - Access (Untagged), T - Tagged +#NUM Status Q Ports +#12 Inactive A Ethernet13 +#13 Inactive T Ethernet13 +#14 Inactive T Ethernet13 +# +# """ RETURN = """ before: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l3_acls.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l3_acls.py new file mode 100644 index 000000000..ad34025df --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l3_acls.py @@ -0,0 +1,1058 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_l3_acls +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: sonic_l3_acls +version_added: '2.1.0' +notes: + - Supports C(check_mode). +short_description: Manage Layer 3 access control lists (ACL) configurations on SONiC +description: + - This module provides configuration management of Layer 3 access control lists (ACL) + in devices running SONiC. +author: 'Arun Saravanan Balachandran (@ArunSaravananBalachandran)' +options: + config: + description: + - Specifies Layer 3 ACL configurations. + type: list + elements: dict + suboptions: + address_family: + description: + - Specifies the address family of the ACLs. + type: str + required: true + choices: + - ipv4 + - ipv6 + acls: + description: + - List of ACL configuration for the given address family. + type: list + elements: dict + suboptions: + name: + description: + - Specifies the ACL name. + type: str + required: true + remark: + description: + - Specifies remark for the ACL. + type: str + rules: + description: + - List of rules with the ACL. + - I(sequence_num), I(action), I(protocol), I(source) & I(destination) are required for adding a new rule. + - If I(state=deleted), options other than I(sequence_num) are not considered. + type: list + elements: dict + suboptions: + sequence_num: + description: + - Specifies the sequence number of the rule. + - The range is from 1 to 65535. + type: int + required: true + action: + description: + - Specifies the action taken on the matched packet. + type: str + choices: + - deny + - discard + - do-not-nat + - permit + - transit + protocol: + description: + - Specifies the protocol to match. + - Only one suboption can be specified for protocol in a rule. + type: dict + suboptions: + name: + description: + - Match packets with the given protocol. + - C(ip) - Match any IPv4 packets. + - C(ipv6) - Match any IPv6 packets. + - C(icmp) - Match ICMP packets. + - C(icmpv6) - Match ICMPv6 packets. + - C(tcp) - Match TCP packets. + - C(udp) - Match UDP packets. + - C(ip) and C(icmp) are valid only for IPv4 ACLs. + - C(ipv6) and C(icmpv6) are valid only for IPv6 ACLs. + type: str + choices: + - ip + - ipv6 + - icmp + - icmpv6 + - tcp + - udp + number: + description: + - Match packets with given protocol number. + - The range is from 0 to 255. + type: int + source: + description: + - Specifies the source of the packet. + - I(any), I(host) and I(prefix) are mutually exclusive. + type: dict + suboptions: + any: + description: + - Match any source network address. + type: bool + host: + description: + - Network address of a single source host. + type: str + prefix: + description: + - Source network prefix in the format A.B.C.D/mask (ipv4) or A::B/mask (ipv6). + type: str + port_number: + description: + - Specifies the source port (valid only for TCP or UDP) + - Only one suboption can be specified for port_number in a rule. + type: dict + suboptions: + eq: + description: + - Match packets with source port equal to the given port number. + - The range is from 0 to 65535. + type: int + gt: + description: + - Match packets with source port greater than the given port number. + - The range is from 0 to 65534. + type: int + lt: + description: + - Match packets with source port lesser than the given port number. + - The range is from 1 to 65535. + type: int + range: + description: + - Match packets with source port in the given range. + - I(begin) and I(end) are required together. + type: dict + suboptions: + begin: + description: + - Specifies the beginning of the port range. + - The range is from 0 to 65534. + type: int + end: + description: + - Specifies the end of the port range. + - The range is from 1 to 65535. + type: int + destination: + description: + - Specifies the destination of the packet. + - I(any), I(host) and I(prefix) are mutually exclusive. + type: dict + suboptions: + any: + description: + - Match any destination network address. + type: bool + host: + description: + - Network address of a single destination host. + type: str + prefix: + description: + - Destination network prefix in the format A.B.C.D/mask (ipv4) or A::B/mask (ipv6). + type: str + port_number: + description: + - Specifies the destination port (valid only for TCP or UDP) + - Only one suboption can be specified for port_number in a rule. + type: dict + suboptions: + eq: + description: + - Match packets with destination port equal to the given port number. + - The range is from 0 to 65535. + type: int + gt: + description: + - Match packets with destination port greater than the given port number. + - The range is from 0 to 65534. + type: int + lt: + description: + - Match packets with destination port lesser than the given port number. + - The range is from 1 to 65535. + type: int + range: + description: + - Match packets with destination port in the given range. + - I(begin) and I(end) are required together. + type: dict + suboptions: + begin: + description: + - Specifies the beginning of the port range. + - The range is from 0 to 65534. + type: int + end: + description: + - Specifies the end of the port range. + - The range is from 1 to 65535. + type: int + protocol_options: + description: + - Specifies the additional packet match options for the chosen protocol. + - I(icmp), I(icmpv6) and I(tcp) are mutually exclusive. + type: dict + suboptions: + icmp: + description: + - Packet match options for ICMP. + type: dict + suboptions: + code: + description: + - Match packets with given ICMP code. + - The range is from 0 to 255. + type: int + type: + description: + - Match packets with given ICMP type. + - The range is from 0 to 255. + type: int + icmpv6: + description: + - Packet match options for ICMPv6. + type: dict + suboptions: + code: + description: + - Match packets with given ICMPv6 code. + - The range is from 0 to 255. + type: int + type: + description: + - Match packets with given ICMPv6 type. + - The range is from 0 to 255. + type: int + tcp: + description: + - Packet match options for TCP. + - I(established) and other TCP flag options are mutually exclusive. + type: dict + suboptions: + established: + description: + - Match packets which are part of established TCP session. + type: bool + ack: + description: + - Match packets with ACK flag set. + type: bool + not_ack: + description: + - Match packets with ACK flag cleared. + type: bool + fin: + description: + - Match packets with FIN flag set. + type: bool + not_fin: + description: + - Match packets with FIN flag cleared. + type: bool + psh: + description: + - Match packets with PSH flag set. + type: bool + not_psh: + description: + - Match packets with PSH flag cleared. + type: bool + rst: + description: + - Match packets with RST flag set. + type: bool + not_rst: + description: + - Match packets with RST flag cleared. + type: bool + syn: + description: + - Match packets with SYN flag set. + type: bool + not_syn: + description: + - Match packets with SYN flag cleared. + type: bool + urg: + description: + - Match packets with URG flag set. + type: bool + not_urg: + description: + - Match packets with URG flag cleared. + type: bool + vlan_id: + description: + - Match packets with the given VLAN ID value. + type: int + dscp: + description: + - Match packets using DSCP value. + - Only one suboption can be specified for dscp in a rule. + type: dict + suboptions: + value: + description: + - Match packets with given DSCP value. + - The range is from 0 to 63. + type: int + af11: + description: + - Match packets with AF11 DSCP (001010 - Decimal value 10). + type: bool + af12: + description: + - Match packets with AF12 DSCP (001100 - Decimal value 12). + type: bool + af13: + description: + - Match packets with AF13 DSCP (001110 - Decimal value 14). + type: bool + af21: + description: + - Match packets with AF21 DSCP (010010 - Decimal value 18). + type: bool + af22: + description: + - Match packets with AF22 DSCP (010100 - Decimal value 20). + type: bool + af23: + description: + - Match packets with AF23 DSCP (010110 - Decimal value 22). + type: bool + af31: + description: + - Match packets with AF31 DSCP (011010 - Decimal value 26). + type: bool + af32: + description: + - Match packets with AF32 DSCP (011100 - Decimal value 28). + type: bool + af33: + description: + - Match packets with AF33 DSCP (011110 - Decimal value 30). + type: bool + af41: + description: + - Match packets with AF41 DSCP (100010 - Decimal value 34). + type: bool + af42: + description: + - Match packets with AF42 DSCP (100100 - Decimal value 36). + type: bool + af43: + description: + - Match packets with AF43 DSCP (100110 - Decimal value 38). + type: bool + cs1: + description: + - Match packets with CS1 DSCP (001000 - Decimal value 8). + type: bool + cs2: + description: + - Match packets with CS2 DSCP (010000 - Decimal value 16). + type: bool + cs3: + description: + - Match packets with CS3 DSCP (011000 - Decimal value 24). + type: bool + cs4: + description: + - Match packets with CS4 DSCP (100000 - Decimal value 32). + type: bool + cs5: + description: + - Match packets with CS5 DSCP (101000 - Decimal value 40). + type: bool + cs6: + description: + - Match packets with CS6 DSCP (110000 - Decimal value 48). + type: bool + cs7: + description: + - Match packets with CS7 DSCP (111000 - Decimal value 56). + type: bool + default: + description: + - Match packets with CS0 DSCP (000000 - Decimal value 0). + type: bool + ef: + description: + - Match packets with EF DSCP (101110 - Decimal value 46). + type: bool + voice_admit: + description: + - Match packets with VOICE-ADMIT DSCP (101100 - Decimal value 44). + type: bool + remark: + description: + - Specifies remark for the ACL rule. + type: str + state: + description: + - The state of the configuration after module completion. + - C(merged) - Merges provided L3 ACL configuration with on-device configuration. + - C(replaced) - Replaces on-device configuration of the specified L3 ACLs with provided configuration. + - C(overridden) - Overrides all on-device L3 ACL configurations with the provided configuration. + - C(deleted) - Deletes on-device L3 ACL configuration. + type: str + choices: + - merged + - replaced + - overridden + - deleted + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before State: +# ------------- +# +# sonic# show running-configuration ip access-list +# ! +# ip access-list test +# seq 1 permit ip host 192.168.1.2 any +# sonic# +# sonic# show running-configuration ipv6 access-list +# ! +# ipv6 access-list testv6 +# seq 1 permit ipv6 host 192:168:1::2 any +# sonic# + + - name: Merge provided Layer 3 ACL configurations + dellemc.enterprise_sonic.sonic_l3_acls: + config: + - address_family: 'ipv4' + acls: + - name: 'test' + rules: + - sequence_num: 2 + action: 'permit' + protocol: + name: 'icmp' + source: + any: true + destination: + host: '192.168.1.2' + protocol_options: + icmp: + type: 8 + - sequence_num: 3 + action: 'deny' + protocol: + number: 2 + source: + any: true + destination: + any: true + - sequence_num: 4 + action: 'deny' + protocol: + name: 'ip' + source: + any: true + destination: + any: true + vlan_id: 10 + remark: 'Vlan10' + - name: 'test1' + remark: 'test_ip_acl' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'tcp' + source: + prefix: '10.0.0.0/8' + destination: + any: true + - sequence_num: 2 + action: 'deny' + protocol: + name: 'udp' + source: + any: true + destination: + prefix: '20.1.0.0/16' + port_number: + gt: 1024 + - sequence_num: 3 + action: 'deny' + protocol: + name: 'ip' + source: + any: true + destination: + any: true + dscp: + value: 63 + - address_family: 'ipv6' + acls: + - name: 'testv6' + rules: + - sequence_num: 2 + action: 'deny' + protocol: + name: 'icmpv6' + source: + any: true + destination: + any: true + - name: 'testv6-1' + remark: 'test_ipv6_acl' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'ipv6' + source: + prefix: '1000::/16' + destination: + any: true + dscp: + af22: true + - sequence_num: 2 + action: 'deny' + protocol: + name: 'tcp' + source: + any: true + destination: + prefix: '2000::1000:0/112' + port_number: + range: + begin: 100 + end: 1000 + - sequence_num: 3 + action: 'permit' + protocol: + name: 'tcp' + source: + any: true + destination: + any: true + protocol_options: + tcp: + established: true + - sequence_num: 4 + action: 'deny' + protocol: + name: 'udp' + source: + any: true + port_number: + eq: 3000 + destination: + any: true + state: merged + +# After State: +# ------------ +# +# sonic# show running-configuration ip access-list +# ! +# ip access-list test +# seq 1 permit ip host 192.168.1.2 any +# seq 2 permit icmp any host 192.168.1.2 type 8 +# seq 3 deny 2 any any +# seq 4 deny ip any any vlan 10 remark Vlan10 +# ! +# ip access-list test1 +# remark test_ip_acl +# seq 1 permit tcp 10.0.0.0/8 any +# seq 2 deny udp any 20.1.0.0/16 gt 1024 +# seq 3 deny ip any any dscp 63 +# sonic# +# sonic# show running-configuration ipv6 access-list +# ! +# ipv6 access-list testv6 +# seq 1 permit ipv6 host 192:168:1::2 any +# seq 2 deny icmpv6 any any +# ! +# ipv6 access-list testv6-1 +# remark test_ipv6_acl +# seq 1 permit ipv6 1000::/16 any dscp af22 +# seq 2 deny tcp any 2000::1000:0/112 range 100 1000 +# seq 3 permit tcp any any established +# seq 4 deny udp any eq 3000 any +# sonic# + + +# Using replaced +# +# Before State: +# ------------- +# +# sonic# show running-configuration ip access-list +# ! +# ip access-list test +# seq 1 permit ip host 192.168.1.2 any +# seq 2 permit icmp any host 192.168.1.2 type 8 +# seq 3 deny 2 any any +# seq 4 deny ip any any vlan 10 remark Vlan10 +# ! +# ip access-list test1 +# remark test_ip_acl +# seq 1 permit tcp 10.0.0.0/8 any +# seq 2 deny udp any 20.1.0.0/16 gt 1024 +# seq 3 deny ip any any dscp 63 +# sonic# +# sonic# show running-configuration ipv6 access-list +# ! +# ipv6 access-list testv6 +# seq 1 permit tcp host 3000::1 any established +# seq 2 permit udp any any +# seq 3 deny icmpv6 any any +# ! +# ipv6 access-list testv6-1 +# remark test_ipv6_acl +# seq 1 permit ipv6 1000::/16 any dscp af22 +# seq 2 deny tcp any 2000::1000:0/112 range 100 1000 +# seq 3 permit tcp any any established +# seq 4 deny udp any eq 3000 any +# sonic# + + - name: Replace device configuration of specified Layer 3 ACLs with provided configuration + dellemc.enterprise_sonic.sonic_l3_acls: + config: + - address_family: 'ipv4' + acls: + - name: 'test2' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'tcp' + source: + prefix: '192.168.1.0/24' + destination: + any: true + - address_family: 'ipv6' + acls: + - name: 'testv6' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'tcp' + source: + host: '3000::1' + destination: + any: true + protocol_options: + tcp: + ack: true + syn: true + fin: true + - sequence_num: 2 + action: 'deny' + protocol: + name: 'ipv6' + source: + any: true + destination: + any: true + state: replaced + +# After State: +# ------------ +# +# sonic# show running-configuration ip access-list +# ! +# ip access-list test +# seq 1 permit ip host 192.168.1.2 any +# seq 2 permit icmp any host 192.168.1.3 type 8 +# seq 3 deny 2 any any +# seq 4 deny ip any any vlan 10 remark Vlan10 +# ! +# ip access-list test1 +# remark test_ip_acl +# seq 1 permit tcp 10.0.0.0/8 any +# seq 2 deny udp any 20.1.0.0/16 gt 1024 +# seq 3 deny ip any any dscp 63 +# ! +# ip access-list test2 +# seq 1 permit tcp 192.168.1.0/24 any +# sonic# +# sonic# show running-configuration ipv6 access-list +# ! +# ipv6 access-list testv6 +# seq 1 permit tcp host 3000::1 any fin syn ack +# seq 2 deny ipv6 any any +# ! +# ipv6 access-list testv6-1 +# remark test_ipv6_acl +# seq 1 permit ipv6 1000::/16 any dscp af22 +# seq 2 deny tcp any 2000::1000:0/112 range 100 1000 +# seq 3 permit tcp any any established +# seq 4 deny udp any eq 3000 any +# sonic# + + +# Using overridden +# +# Before State: +# ------------- +# +# sonic# show running-configuration ip access-list +# ! +# ip access-list test +# seq 1 permit ip host 192.168.1.2 any +# seq 2 permit icmp any host 192.168.1.3 type 8 +# seq 3 deny 2 any any +# seq 4 deny ip any any vlan 10 remark Vlan10 +# ! +# ip access-list test1 +# remark test_ip_acl +# seq 1 permit tcp 10.0.0.0/8 any +# seq 2 deny udp any 20.1.0.0/16 gt 1024 +# seq 3 deny ip any any dscp 63 +# ! +# ip access-list test2 +# seq 1 permit tcp 192.168.1.0/24 any +# sonic# +# sonic# show running-configuration ipv6 access-list +# ! +# ipv6 access-list testv6 +# seq 1 permit tcp 3000::/16 any +# seq 2 deny ipv6 any any +# ! +# ipv6 access-list testv6-1 +# remark test_ipv6_acl +# seq 1 permit ipv6 1000::/16 any dscp af22 +# seq 2 deny tcp any 2000::1000:0/112 range 100 1000 +# seq 3 permit tcp any any established +# seq 4 deny udp any eq 3000 any +# sonic# + + - name: Override device configuration of all Layer 3 ACLs with provided configuration + dellemc.enterprise_sonic.sonic_l3_acls: + config: + - address_family: 'ipv4' + acls: + - name: 'test_acl' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'ip' + source: + prefix: '100.1.1.0/24' + destination: + prefix: '100.1.2.0/24' + - sequence_num: 2 + action: 'deny' + protocol: + name: 'udp' + source: + any: true + destination: + any: true + state: overridden + +# After State: +# ------------ +# +# sonic# show running-configuration ip access-list +# ! +# ip access-list test_acl +# seq 1 permit ip 100.1.1.0/24 100.1.2.0/24 +# seq 2 deny udp any any +# sonic# +# sonic# show running-configuration ipv6 access-list +# sonic# + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show running-configuration ip access-list +# ! +# ip access-list test +# seq 1 permit ip host 192.168.1.2 any +# seq 2 permit icmp any host 192.168.1.3 type 8 +# seq 3 deny 2 any any +# seq 4 deny ip any any vlan 10 remark Vlan10 +# ! +# ip access-list test1 +# remark test_ip_acl +# seq 1 permit tcp 10.0.0.0/8 any +# seq 2 deny udp any 20.1.0.0/16 gt 1024 +# seq 3 deny ip any any dscp 63 +# ! +# ip access-list test2 +# seq 1 permit tcp 192.168.1.0/24 any +# sonic# +# sonic# show running-configuration ipv6 access-list +# ! +# ipv6 access-list testv6 +# seq 1 permit tcp 3000::/16 any +# seq 2 deny ipv6 any any +# ! +# ipv6 access-list testv6-1 +# remark test_ipv6_acl +# seq 1 permit ipv6 1000::/16 any dscp af22 +# seq 2 deny tcp any 2000::1000:0/112 range 100 1000 +# seq 3 permit tcp any any established +# seq 4 deny udp any eq 3000 any +# sonic# + + - name: Delete specified Layer 3 ACLs, ACL remark and ACL rule entries + dellemc.enterprise_sonic.sonic_l3_acls: + config: + - address_family: 'ipv4' + acls: + - name: 'test' + rules: + - sequence_num: 2 + - name: 'test2' + - address_family: 'ipv6' + acls: + - name: 'testv6-1' + remark: 'test_ipv6_acl' + rules: + - sequence_num: 3 + state: deleted + +# After State: +# ------------ +# +# sonic# show running-configuration ip access-list +# ! +# ip access-list test +# seq 1 permit ip host 192.168.1.2 any +# seq 3 deny 2 any any +# seq 4 deny ip any any vlan 10 remark Vlan10 +# ! +# ip access-list test1 +# remark test_ip_acl +# seq 1 permit tcp 10.0.0.0/8 any +# seq 2 deny udp any 20.1.0.0/16 gt 1024 +# seq 3 deny ip any any dscp 63 +# sonic# +# sonic# show running-configuration ipv6 access-list +# ! +# ipv6 access-list testv6 +# seq 1 permit tcp 3000::/16 any +# seq 2 deny ipv6 any any +# ! +# ipv6 access-list testv6-1 +# seq 1 permit ipv6 1000::/16 any dscp af22 +# seq 2 deny tcp any 2000::1000:0/112 range 100 1000 +# seq 4 deny udp any eq 3000 any +# sonic# + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show running-configuration ip access-list +# ! +# ip access-list test +# seq 1 permit ip host 192.168.1.2 any +# seq 2 permit icmp any host 192.168.1.3 type 8 +# seq 3 deny 2 any any +# seq 4 deny ip any any vlan 10 remark Vlan10 +# ! +# ip access-list test1 +# remark test_ip_acl +# seq 1 permit tcp 10.0.0.0/8 any +# seq 2 deny udp any 20.1.0.0/16 gt 1024 +# seq 3 deny ip any any dscp 63 +# ! +# ip access-list test2 +# seq 1 permit tcp 192.168.1.0/24 any +# sonic# +# sonic# show running-configuration ipv6 access-list +# ! +# ipv6 access-list testv6 +# seq 1 permit tcp 3000::/16 any +# seq 2 deny ipv6 any any +# ! +# ipv6 access-list testv6-1 +# remark test_ipv6_acl +# seq 1 permit ipv6 1000::/16 any dscp af22 +# seq 2 deny tcp any 2000::1000:0/112 range 100 1000 +# seq 3 permit tcp any any established +# seq 4 deny udp any eq 3000 any +# sonic# + + - name: Delete all Layer 3 ACLs for an address-family + dellemc.enterprise_sonic.sonic_l3_acls: + config: + - address_family: 'ipv4' + state: deleted + +# After State: +# ------------ +# +# sonic# show running-configuration ip access-list +# sonic# +# sonic# show running-configuration ipv6 access-list +# ! +# ipv6 access-list testv6 +# seq 1 permit tcp 3000::/16 any +# seq 2 deny ipv6 any any +# ! +# ipv6 access-list testv6-1 +# remark test_ipv6_acl +# seq 1 permit ipv6 1000::/16 any dscp af22 +# seq 2 deny tcp any 2000::1000:0/112 range 100 1000 +# seq 3 permit tcp any any established +# seq 4 deny udp any eq 3000 any +# sonic# + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show running-configuration ip access-list +# ! +# ip access-list test +# seq 1 permit ip host 192.168.1.2 any +# seq 2 permit icmp any host 192.168.1.3 type 8 +# seq 3 deny 2 any any +# seq 4 deny ip any any vlan 10 remark Vlan10 +# ! +# ip access-list test1 +# remark test_ip_acl +# seq 1 permit tcp 10.0.0.0/8 any +# seq 2 deny udp any 20.1.0.0/16 gt 1024 +# seq 3 deny ip any any dscp 63 +# ! +# ip access-list test2 +# seq 1 permit tcp 192.168.1.0/24 any +# sonic# +# sonic# show running-configuration ipv6 access-list +# ! +# ipv6 access-list testv6 +# seq 1 permit tcp 3000::/16 any +# seq 2 deny ipv6 any any +# ! +# ipv6 access-list testv6-1 +# remark test_ipv6_acl +# seq 1 permit ipv6 1000::/16 any dscp af22 +# seq 2 deny tcp any 2000::1000:0/112 range 100 1000 +# seq 3 permit tcp any any established +# seq 4 deny udp any eq 3000 any +# sonic# + + - name: Delete all Layer 3 ACL configurations + dellemc.enterprise_sonic.sonic_l3_acls: + config: + state: deleted + +# After State: +# ------------ +# +# sonic# show running-configuration ip access-list +# sonic# +# sonic# show running-configuration ipv6 access-list +# sonic# + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after(generated): + description: The generated configuration model invocation. + returned: when C(check_mode) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.l3_acls.l3_acls import L3_aclsArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.l3_acls.l3_acls import L3_acls + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=L3_aclsArgs.argument_spec, + supports_check_mode=True) + + result = L3_acls(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l3_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l3_interfaces.py index e796897a5..1ebc11994 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l3_interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l3_interfaces.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -29,6 +29,13 @@ The module file for sonic_l3_interfaces from __future__ import absolute_import, division, print_function __metaclass__ = type +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community', + 'license': 'Apache 2.0' +} + DOCUMENTATION = """ --- module: sonic_l3_interfaces @@ -44,7 +51,8 @@ description: author: Kumaraguru Narayanan (@nkumaraguru) options: config: - description: A list of l3_interfaces configurations. + description: + - A list of l3_interfaces configurations. type: list elements: dict suboptions: @@ -101,11 +109,13 @@ options: type: bool state: description: - - The state that the configuration should be left in. + - The state of the configuration after module completion. type: str choices: - - merged - - deleted + - merged + - deleted + - replaced + - overridden default: merged """ EXAMPLES = """ @@ -328,7 +338,181 @@ EXAMPLES = """ # ip anycast-address 11.12.13.14/12 #! # +# Using replaced +# +# Before state: +# ------------- +# +#rno-dctor-1ar01c01sw02# show running-configuration interface +#! +#interface Ethernet20 +# mtu 9100 +# speed 100000 +# shutdown +# ip address 83.1.1.1/16 +# ip address 84.1.1.1/16 secondary +# ipv6 address 83::1/16 +# ipv6 address 84::1/16 +# ipv6 enable +#! +#interface Ethernet24 +# mtu 9100 +# speed 100000 +# shutdown +# ip address 91.1.1.1/16 +# ipv6 address 90::1/16 +# ipv6 address 91::1/16 +# ipv6 address 92::1/16 +# ipv6 address 93::1/16 +#! +# +- name: Replace l3 interface + dellemc.enterprise_sonic.sonic_l3_interfaces: + config: + - name: Ethernet20 + ipv4: + - address: 81.1.1.1/16 + state: replaced + +# After state: +# ------------ # +#rno-dctor-1ar01c01sw02# show running-configuration interface +#! +#interface Ethernet20 +# mtu 9100 +# speed 100000 +# shutdown +# ip address 81.1.1.1/16 +#! +#interface Ethernet24 +# mtu 9100 +# speed 100000 +# shutdown +# ip address 91.1.1.1/16 +# ipv6 address 90::1/16 +# ipv6 address 91::1/16 +# ipv6 address 92::1/16 +# ipv6 address 93::1/16 +#! +# +# Using replaced +# +# Before state: +# ------------- +# +#rno-dctor-1ar01c01sw02# show running-configuration interface +#! +#interface Ethernet20 +# mtu 9100 +# speed 100000 +# shutdown +# ip address 83.1.1.1/16 +# ip address 84.1.1.1/16 secondary +# ipv6 address 83::1/16 +# ipv6 address 84::1/16 +# ipv6 enable +#! +#interface Ethernet24 +# mtu 9100 +# speed 100000 +# shutdown +# ip address 91.1.1.1/16 +# ipv6 address 90::1/16 +# ipv6 address 91::1/16 +# ipv6 address 92::1/16 +# ipv6 address 93::1/16 +#! +- name: Replace l3 interface + dellemc.enterprise_sonic.sonic_l3_interfaces: + config: + - name: Ethernet20 + state: replaced + +# After state: +# ------------ +# +#rno-dctor-1ar01c01sw02# show running-configuration interface +#! +#interface Ethernet20 +# mtu 9100 +# speed 100000 +# shutdown +#! +#interface Ethernet24 +# mtu 9100 +# speed 100000 +# shutdown +# ip address 91.1.1.1/16 +# ipv6 address 90::1/16 +# ipv6 address 91::1/16 +# ipv6 address 92::1/16 +# ipv6 address 93::1/16 +#! +# +# Using overridden +# +# Before state: +# ------------- +# +#rno-dctor-1ar01c01sw02# show running-configuration interface +#! +#interface Ethernet20 +# mtu 9100 +# speed 100000 +# shutdown +# ip address 83.1.1.1/16 +# ip address 84.1.1.1/16 secondary +# ipv6 address 83::1/16 +# ipv6 address 84::1/16 +# ipv6 enable +#! +#interface Ethernet24 +# mtu 9100 +# speed 100000 +# shutdown +# ip address 91.1.1.1/16 +# ipv6 address 90::1/16 +# ipv6 address 91::1/16 +# ipv6 address 92::1/16 +# ipv6 address 93::1/16 +#! +# +- name: Override l3 interface + dellemc.enterprise_sonic.sonic_l3_interfaces: + config: + - name: Ethernet24 + ipv4: + - address: 81.1.1.1/16 + - name: Vlan100 + ipv4: + anycast_addresses: + - 83.1.1.1/24 + - 85.1.1.12/24 + state: overridden + +# After state: +# ------------ +# +#rno-dctor-1ar01c01sw02# show running-configuration interface +#! +#interface Ethernet20 +# mtu 9100 +# speed 100000 +# shutdown +#! +#interface Ethernet24 +# mtu 9100 +# speed 100000 +# shutdown +# ip address 81.1.1.1/16 +#! +#interface Vlan100 +# ip anycast-address 83.1.1.1/24 +# ip anycast-address 85.1.1.12/24 +#! + + """ RETURN = """ before: @@ -336,14 +520,14 @@ before: returned: always type: list sample: > - The configuration returned is always in the same format + The configuration returned will always be in the same format of the parameters above. after: description: The resulting configuration model invocation. returned: when changed type: list sample: > - The configuration returned is always in the same format + The configuration returned will always be in the same format of the parameters above. commands: description: The set of commands pushed to the remote device. diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_lag_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_lag_interfaces.py index 630db7985..0fd0d8b7e 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_lag_interfaces.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_lag_interfaces.py @@ -80,6 +80,8 @@ options: type: str choices: - merged + - replaced + - overridden - deleted default: merged """ @@ -124,6 +126,111 @@ EXAMPLES = """ # speed 100000 # no shutdown # +# Using replaced +# +# Before state: +# ------------- +# +# interface Eth1/5 +# channel-group 10 +# mtu 9100 +# speed 100000 +# no shutdown +# +# interface Eth1/6 +# channel-group 20 +# mtu 9100 +# speed 100000 +# no shutdown +# +# interface Eth1/7 +# no channel-group +# mtu 9100 +# speed 100000 +# no shutdown +# +- name: Replace device configuration of specified LAG attributes + dellemc.enterprise_sonic.sonic_lag_interfaces: + config: + - name: PortChannel10 + members: + interfaces: + - member: Eth1/7 + state: replaced +# +# After state: +# ------------ +# +# interface Eth1/5 +# no channel-group +# mtu 9100 +# speed 100000 +# no shutdown +# +# interface Eth1/6 +# channel-group 20 +# mtu 9100 +# speed 100000 +# no shutdown +# +# interface Eth1/7 +# channel-group 10 +# mtu 9100 +# speed 100000 +# no shutdown +# +# Using overridden +# +# Before state: +# ------------- +# +# interface Eth1/5 +# channel-group 10 +# mtu 9100 +# speed 100000 +# no shutdown +# +# interface Eth1/6 +# no channel-group +# mtu 9100 +# speed 100000 +# no shutdown +# +# interface Eth1/7 +# channel-group 2 +# mtu 9100 +# speed 100000 +# no shutdown +# +- name: Override device configuration of all LAG attributes + dellemc.enterprise_sonic.sonic_lag_interfaces: + config: + - name: PortChannel20 + members: + interfaces: + - member: Eth1/6 + state: overridden +# +# After state: +# ------------ +# interface Eth1/5 +# no channel-group +# mtu 9100 +# speed 100000 +# no shutdown +# +# interface Eth1/6 +# channel-group 20 +# mtu 9100 +# speed 100000 +# no shutdown +# +# interface Eth1/7 +# no channel-group +# mtu 9100 +# speed 100000 +# no shutdown +# # Using deleted # # Before state: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_lldp_global.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_lldp_global.py new file mode 100644 index 000000000..6577d21d0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_lldp_global.py @@ -0,0 +1,301 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_lldp_global +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = """ +--- +module: sonic_lldp_global +version_added: '2.1.0' +short_description: Manage Global LLDP configurations on SONiC +description: + - This module provides configuration management of global LLDP parameters + for use on LLDP enabled Layer 2 interfaces of devices running SONiC. + - It is intended for use in conjunction with LLDP Layer 2 interface + configuration applied on participating interfaces. +author: 'Divya Balasubramanian(@divya-balasubramania)' +options: + config: + description: The set of link layer discovery protocol global attribute configurations + type: dict + suboptions: + enable: + description: + - This argument is a boolean value to enable or disable LLDP. + type: bool + multiplier: + description: + - Multiplier value is used to determine the timeout interval (i.e. hello-time x multiplier value) + - The range is from 1 to 10 + type: int + system_description: + description: + - Description of this system to be sent in LLDP advertisements. + - When configured, this value is used in the advertisements + instead of the default system description. + type: str + system_name: + description: + - Specifying a descriptive system name using this command, user may find it easier to distinguish the device with LLDP. + - By default, the host name is used. + type: str + mode: + description: + - By default both transmit and receive of LLDP frames is enabled. + - This command can be used to configure either in receive only or transmit only mode. + type: str + choices: + - receive + - transmit + hello_time: + description: + - Frequency at which LLDP advertisements are sent (in seconds). + - The range is from 5 to 254 sec + type: int + tlv_select: + description: + - By default, management address and system capabilities TLV are advertised in LLDP frames. + - This configuration option can be used to selectively suppress sending of these TLVs + to the Peer. + type: dict + suboptions: + management_address: + description: + - Enable or disable management address TLV. + type: bool + system_capabilities: + description: + - Enable or disable system capabilities TLV. + type: bool + state: + description: + - The state specifies the type of configuration update to be performed on the device. + - If the state is "merged", merge specified attributes with existing configured attributes. + - For "deleted", delete the specified attributes from existing configuration. + type: str + choices: + - merged + - deleted + default: merged +""" +EXAMPLES = """ +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show running-configuration +# ! +# lldp receive +# lldp timer 200 +# lldp multiplier 1 +# lldp system-name 8999_System +# lldp system-description sonic_system +# ! + + - name: Delete LLDP configurations + dellemc.enterprise_sonic.sonic_lldp_global: + config: + hello_time: 200 + system_description : sonic_system + mode: receive + multiplier: 1 + state: deleted + +# After State: +# ------------ +# sonic# show running-configuration | grep lldp +# ! +# lldp system-name 8999_System +# ! +# sonic# + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show running-configuration | grep lldp +# sonic# + + - name: Delete default LLDP configurations + dellemc.enterprise_sonic.sonic_lldp_global: + config: + tlv_select: + system_capabilities: true + state: deleted + +# After State: +# ------------ +# sonic# show running-configuration +# ! +# no lldp tlv-select system-capabilities +# ! + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show running-configuration | grep lldp +# ! +# lldp receive +# lldp timer 200 +# lldp multiplier 1 +# lldp system-name 8999_System +# lldp system-description sonic_system +# ! + + - name: Delete all LLDP configuration + dellemc.enterprise_sonic.sonic_lldp_global: + config: + state: deleted + +# After State: (No LLDP global configuration present.) +# ------------ +# sonic# show running-configuration | grep lldp +# sonic# + + +# Using Merged +# +# Before State: +# ------------- +# +# sonic# show running-configuration | grep lldp +# sonic# + + - name: Modify LLDP configurations + dellemc.enterprise_sonic.sonic_lldp_global: + config: + enable: false + multiplier: 9 + system_name : CR_sonic + hello_time: 18 + mode: receive + system_description: Sonic_System + tlv_select: + management_address: true + system_capabilities: false + state: merged + +# After State: +# ------------ +# sonic# show running-configuration | grep lldp +# ! +# no lldp enable +# no lldp tlv-select system_capabilities +# lldp receive +# lldp timer 18 +# lldp multiplier 9 +# lldp system-name CR_sonic +# lldp system-description Sonic_System +# ! + + +# Using Merged +# +# Before State: +# ------------- +# +# sonic# show running-configuration | grep lldp +# ! +# lldp receive +# lldp timer 200 +# lldp multiplier 1 +# lldp system-name 8999_System +# lldp system-description sonic_system +# ! + + - name: Modify LLDP configurations + dellemc.enterprise_sonic.sonic_lldp_global: + config: + multiplier: 9 + system_name : CR_sonic + state: merged + +# After State: +# ------------ +# sonic# show running-configuration | grep lldp +# ! +# lldp receive +# lldp timer 200 +# lldp multiplier 9 +# lldp system-name CR_sonic +# lldp system-description sonic_system +# ! + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: list +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: list +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.lldp_global.lldp_global import Lldp_globalArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.lldp_global.lldp_global import Lldp_global + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=Lldp_globalArgs.argument_spec, + supports_check_mode=True) + + result = Lldp_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_logging.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_logging.py new file mode 100644 index 000000000..d8c592874 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_logging.py @@ -0,0 +1,274 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_logging +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = """ +--- +module: sonic_logging +version_added: 2.1.0 +notes: + - Supports C(check_mode). +short_description: Manage logging configuration on SONiC. +description: + - This module provides configuration management of logging for devices running SONiC. +author: "M. Zhang (@mingjunzhang2019)" +options: + config: + description: + - Specifies logging related configurations. + type: dict + suboptions: + remote_servers: + type: list + elements: dict + description: + - Remote logging sever configuration. + suboptions: + host: + type: str + description: + - IPv4/IPv6 address or host name of the remote logging server. + required: true + remote_port: + type: int + description: + - Destination port number for logging messages sent to the server. + - remote_port can not be deleted. + source_interface: + type: str + description: + - Source interface used as source ip for sending logging packets. + - source_interface can not be deleted. + message_type: + type: str + description: + - Type of messages that remote server receives. + - message_type can not be deleted. + choices: + - log + - event + vrf: + type: str + description: + - VRF name used by remote logging server. + state: + description: + - The state of the configuration after module completion. + type: str + choices: + - merged + - replaced + - overridden + - deleted + default: merged +""" +EXAMPLES = """ +# Using deleted +# +# Before state: +# ------------- +# +#sonic# show logging servers +#-------------------------------------------------------------------------------- +#HOST PORT SOURCE-INTERFACE VRF MESSGE-TYPE +#-------------------------------------------------------------------------------- +#10.11.0.2 5 Ethernet24 - event +#10.11.1.1 616 Ethernet8 - log +#log1.dell.com 6 Ethernet28 - log +# +- name: Delete logging server configuration + sonic_logging: + config: + remote_servers: + - host: 10.11.0.2 + - host: log1.dell.com + state: deleted + +# After state: +# ------------ +# +#sonic# show logging servers +#-------------------------------------------------------------------------------- +#HOST PORT SOURCE-INTERFACE VRF MESSGE-TYPE +#-------------------------------------------------------------------------------- +#10.11.1.1 616 Ethernet8 - log +# +# +# Using merged +# +# Before state: +# ------------- +# +#sonic# show logging servers +#-------------------------------------------------------------------------------- +#HOST PORT SOURCE-INTERFACE VRF MESSGE-TYPE +#-------------------------------------------------------------------------------- +#10.11.1.1 616 Ethernet8 - log +# +- name: Merge logging server configuration + sonic_logging: + config: + remote_servers: + - host: 10.11.0.2 + remote_port: 5 + source_interface: Ethernet24 + message_type: event + - host: log1.dell.com + remote_port: 6 + source_interface: Ethernet28 + state: merged + +# After state: +# ------------ +# +#sonic# show logging servers +#-------------------------------------------------------------------------------- +#HOST PORT SOURCE-INTERFACE VRF MESSGE-TYPE +#-------------------------------------------------------------------------------- +#10.11.0.2 5 Ethernet24 - event +#10.11.1.1 616 Ethernet8 - log +#log1.dell.com 6 Ethernet28 - log +# +# +# Using overridden +# +# Before state: +# ------------- +# +#sonic# show logging servers +#-------------------------------------------------------------------------------- +#HOST PORT SOURCE-INTERFACE VRF MESSGE-TYPE +#-------------------------------------------------------------------------------- +#10.11.1.1 616 Ethernet8 - log +#10.11.1.2 626 Ethernet16 - event +# +- name: Replace logging server configuration + sonic_logging: + config: + remote_servers: + - host: 10.11.1.2 + remote_port: 622 + source_interface: Ethernet24 + message_type: event + state: overridden +# +# After state: +# ------------ +# +#sonic# show logging servers +#-------------------------------------------------------------------------------- +#HOST PORT SOURCE-INTERFACE VRF MESSGE-TYPE +#-------------------------------------------------------------------------------- +#10.11.1.2 622 Ethernet24 - event +# +# Using replaced +# +# Before state: +# ------------- +# +#sonic# show logging servers +#-------------------------------------------------------------------------------- +#HOST PORT SOURCE-INTERFACE VRF MESSGE-TYPE +#-------------------------------------------------------------------------------- +#10.11.1.1 616 Ethernet8 - log +#10.11.1.2 626 Ethernet16 - event +# +- name: Replace logging server configuration + sonic_logging: + config: + remote_servers: + - host: 10.11.1.2 + remote_port: 622 + state: replaced +# +# After state: +# ------------ +# +# "MESSAGE-TYPE" has default value of "log" +# +#sonic# show logging servers +#-------------------------------------------------------------------------------- +#HOST PORT SOURCE-INTERFACE VRF MESSGE-TYPE +#-------------------------------------------------------------------------------- +#10.11.1.1 616 Ethernet8 - log +#10.11.1.2 622 - - log +# +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + type: list + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after(generated): + description: The generated configuration model invocation. + returned: when C(check_mode) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.logging.logging import LoggingArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.logging.logging import Logging + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=LoggingArgs.argument_spec, + supports_check_mode=True) + + result = Logging(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_mac.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_mac.py new file mode 100644 index 000000000..42acfd4fb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_mac.py @@ -0,0 +1,319 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_mac +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = """ +--- +module: sonic_mac +version_added: "2.1.0" +short_description: Manage MAC configuration on SONiC +description: + - This module provides configuration management of MAC for devices running SONiC +author: "Shade Talabi (@stalabi1)" +options: + config: + description: + - A list of MAC configurations. + type: list + elements: dict + suboptions: + vrf_name: + description: + - Specifies the VRF name. + type: str + default: 'default' + mac: + description: + - Configuration attributes for MAC. + type: dict + suboptions: + aging_time: + description: + - Time in seconds of inactivity before the MAC entry is timed out. + type: int + default: 600 + dampening_interval: + description: + - Interval for which mac movements are observed before disabling MAC learning on a port. + type: int + default: 5 + dampening_threshold: + description: + - Number of MAC movements allowed per second before disabling MAC learning on a port. + type: int + default: 5 + mac_table_entries: + description: + - Configuration attributes for MAC table entries. + type: list + elements: dict + suboptions: + mac_address: + description: + - MAC address for the dynamic or static MAC table entry. + type: str + required: True + vlan_id: + description: + - ID number of VLAN on which the MAC address is present. + type: int + required: True + interface: + description: + - Specifies the interface for the MAC table entry. + type: str + state: + description: + - The state of the configuration after module completion + type: str + choices: ['merged', 'deleted', 'replaced', 'overridden'] + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# sonic# show mac dampening +# MAC Move Dampening Threshold : 5 +# MAC Move Dampening Interval : 5 +# sonic# show running-configuration | grep mac +# (No mac configuration pressent) + + - name: Merge MAC configurations + dellemc.enterprise_sonic.sonic_mac: + config: + - vrf_name: 'default' + mac: + aging_time: 50 + dampening_interval: 20 + dampening_threshold: 30 + mac_table_entries: + - mac_address: '00:00:5e:00:53:af' + vlan_id: 1 + interface: 'Ethernet20' + - mac_address: '00:33:33:33:33:33' + vlan_id: 2 + interface: 'Ethernet24' + - mac_address: '00:00:4e:00:24:af' + vlan_id: 3 + interface: 'Ethernet28' + state: merged + +# After state: +# ------------ +# +# sonic# show mac dampening +# MAC Move Dampening Threshold : 30 +# MAC Move Dampening Interval : 20 +# sonic# show running-configuration | grep mac +# mac address-table 00:00:5e:00:53:af Vlan1 Ethernet20 +# mac address-table 00:33:33:33:33:33 Vlan2 Ethernet24 +# mac address-table 00:00:4e:00:24:af Vlan3 Ethernet28 +# mac address-table aging-time 50 +# +# +# Using replaced +# +# Before state: +# ------------- +# +# sonic# show mac dampening +# MAC Move Dampening Threshold : 30 +# MAC Move Dampening Interval : 20 +# sonic# show running-configuration | grep mac +# mac address-table 00:00:5e:00:53:af Vlan1 Ethernet20 +# mac address-table 00:33:33:33:33:33 Vlan2 Ethernet24 +# mac address-table 00:00:4e:00:24:af Vlan3 Ethernet28 +# mac address-table aging-time 50 + + - name: Replace MAC configurations + dellemc.enterprise_sonic.sonic_mac: + config: + - vrf_name: 'default' + mac: + aging_time: 45 + dampening_interval: 30 + dampening_threshold: 60 + mac_table_entries: + - mac_address: '00:00:5e:00:53:af' + vlan_id: 3 + interface: 'Ethernet24' + - mac_address: '00:44:44:44:44:44' + vlan_id: 2 + interface: 'Ethernet20' + state: replaced + +# sonic# show mac dampening +# MAC Move Dampening Threshold : 60 +# MAC Move Dampening Interval : 30 +# sonic# show running-configuration | grep mac +# mac address-table 00:00:5e:00:53:af Vlan3 Ethernet24 +# mac address-table 00:33:33:33:33:33 Vlan2 Ethernet24 +# mac address-table 00:00:4e:00:24:af Vlan3 Ethernet28 +# mac address-table 00:44:44:44:44:44 Vlan2 Ethernet20 +# mac address-table aging-time 45 +# +# +# Using overridden +# +# Before state: +# ------------- +# +# sonic# show mac dampening +# MAC Move Dampening Threshold : 60 +# MAC Move Dampening Interval : 30 +# sonic# show running-configuration | grep mac +# mac address-table 00:00:5e:00:53:af Vlan3 Ethernet24 +# mac address-table 00:33:33:33:33:33 Vlan2 Ethernet24 +# mac address-table 00:00:4e:00:24:af Vlan3 Ethernet28 +# mac address-table 00:44:44:44:44:44 Vlan2 Ethernet20 +# mac address-table aging-time 45 + + - name: Override MAC cofigurations + dellemc.enterprise_sonic.sonic_mac: + config: + - vrf_name: 'default' + mac: + aging_time: 10 + dampening_interval: 20 + dampening_threshold: 30 + mac_table_entries: + - mac_address: '00:11:11:11:11:11' + vlan_id: 1 + interface: 'Ethernet20' + - mac_address: '00:22:22:22:22:22' + vlan_id: 2 + interface: 'Ethernet24' + state: overridden + +# After state: +# ------------ +# +# sonic# show mac dampening +# MAC Move Dampening Threshold : 30 +# MAC Move Dampening Interval : 20 +# sonic# show running-configuration | grep mac +# mac address-table 00:11:11:11:11:11 Vlan1 Ethernet20 +# mac address-table 00:22:22:22:22:22 Vlan2 Ethernet24 +# mac address-table aging-time 10 +# +# +# Using deleted +# +# Before state: +# ------------- +# +# sonic# show mac dampening +# MAC Move Dampening Threshold : 30 +# MAC Move Dampening Interval : 20 +# sonic# show running-configuration | grep mac +# mac address-table 00:11:11:11:11:11 Vlan1 Ethernet20 +# mac address-table 00:22:22:22:22:22 Vlan2 Ethernet24 +# mac address-table aging-time 10 + + - name: Delete MAC cofigurations + dellemc.enterprise_sonic.sonic_mac: + config: + - vrf_name: 'default' + mac: + aging_time: 10 + dampening_interval: 20 + dampening_threshold: 30 + mac_table_entries: + - mac_address: '00:11:11:11:11:11' + vlan_id: 1 + interface: 'Ethernet20' + - mac_address: '00:22:22:22:22:22' + vlan_id: 2 + interface: 'Ethernet24' + state: deleted + +# After state: +# ------------ +# +# sonic# show mac dampening +# MAC Move Dampening Threshold : 5 +# MAC Move Dampening Interval : 5 +# sonic# show running-configuration | grep mac +# (No mac configuration pressent) + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.mac.mac import MacArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.mac.mac import Mac + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=MacArgs.argument_spec, + supports_check_mode=True) + + result = Mac(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_mclag.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_mclag.py index 28d3dbb5b..e17fe080c 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_mclag.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_mclag.py @@ -34,11 +34,11 @@ DOCUMENTATION = """ module: sonic_mclag version_added: 1.0.0 notes: -- Tested against Enterprise SONiC Distribution by Dell Technologies. -- Supports C(check_mode). + - Tested against Enterprise SONiC Distribution by Dell Technologies. + - Supports C(check_mode). short_description: Manage multi chassis link aggregation groups domain (MCLAG) and its parameters description: - - Manage multi chassis link aggregation groups domain (MCLAG) and its parameters + - Manage multi chassis link aggregation groups domain (MCLAG) and its parameters. author: Abirami N (@abirami-n) options: @@ -65,7 +65,7 @@ options: type: str system_mac: description: - - Mac address of MCLAG. + - MAC address of MCLAG. type: str keepalive: description: @@ -75,17 +75,45 @@ options: description: - MCLAG session timeout value in secs. type: int + delay_restore: + description: + - MCLAG delay restore time in secs. + type: int + gateway_mac: + description: + - Gateway MAC address for router ports over MCLAG. + - Configured gateway MAC address can be modified only when I(state=replaced) or I(state=overridden). + type: str unique_ip: - description: Holds Vlan dictionary for mclag unique ip. + description: Holds Vlan dictionary for MCLAG unique IP. + suboptions: + vlans: + description: + - Holds a list of VLANs and VLAN ranges for which a separate IP address is enabled for Layer 3 protocol support over MCLAG. + type: list + elements: dict + suboptions: + vlan: + description: + - Holds a VLAN name or VLAN range. + - Specify a single VLAN eg. Vlan10. + - Specify a range of VLANs eg. Vlan10-20. + type: str + type: dict + peer_gateway: + description: Holds Vlan dictionary for MCLAG peer gateway. suboptions: vlans: description: - - Holds list of VLANs for which a separate IP addresses is enabled for Layer 3 protocol support over MCLAG. + - Holds a list of VLANs and VLAN ranges for which MCLAG peer gateway functionality is enabled. type: list elements: dict suboptions: vlan: - description: Holds a VLAN ID. + description: + - Holds a VLAN name or VLAN range. + - Specify a single VLAN eg. Vlan10. + - Specify a range of VLANs eg. Vlan10-20. type: str type: dict members: @@ -106,8 +134,10 @@ options: - The state that the configuration should be left in. type: str choices: - - merged - - deleted + - merged + - deleted + - replaced + - overridden default: merged """ EXAMPLES = """ @@ -118,7 +148,7 @@ EXAMPLES = """ # # sonic# show mclag brief # MCLAG Not Configured -# + - name: Merge provided configuration with device configuration dellemc.enterprise_sonic.sonic_mclag: config: @@ -128,14 +158,22 @@ EXAMPLES = """ peer_link: 'Portchannel1' keepalive: 1 session_timeout: 3 + delay_restore: 240 + system_mac: '00:00:00:11:11:11' + gateway_mac: '00:00:00:12:12:12' unique_ip: - vlans: - - vlan: Vlan4 + vlans: + - vlan: Vlan4 + - vlan: Vlan21-25 + peer_gateway: + vlans: + - vlan: Vlan4 + - vlan: Vlan21-25 members: - portchannles: - - lag: PortChannel10 + portchannels: + - lag: PortChannel10 state: merged -# + # After state: # ------------ # @@ -150,7 +188,10 @@ EXAMPLES = """ # Peer Link : PortChannel1 # Keepalive Interval : 1 secs # Session Timeout : 3 secs +# Delay Restore : 240 secs # System Mac : 20:04:0f:37:bd:c9 +# Mclag System Mac : 00:00:00:11:11:11 +# Gateway Mac : 00:00:00:12:12:12 # # # Number of MLAG Interfaces:1 @@ -159,18 +200,34 @@ EXAMPLES = """ #----------------------------------------------------------- # PortChannel10 down/down # -# admin@sonic:~$ show runningconfiguration all -# { -# ... -# "MCLAG_UNIQUE_IP": { -# "Vlan4": { -# "unique_ip": "enable" -# } -# }, -# ... -# } -# -# +# sonic# show mclag separate-ip-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan21 +# Vlan22 +# Vlan23 +# Vlan24 +# Vlan25 +# ============== +# Total count : 6 +# ============== +# sonic# +# sonic# show mclag peer-gateway-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan21 +# Vlan22 +# Vlan23 +# Vlan24 +# Vlan25 +# ============== +# Total count : 6 +# ============== +# sonic# + + # Using merged # # Before state: @@ -187,7 +244,10 @@ EXAMPLES = """ # Peer Link : PortChannel1 # Keepalive Interval : 1 secs # Session Timeout : 3 secs +# Delay Restore : 240 secs # System Mac : 20:04:0f:37:bd:c9 +# Mclag System Mac : 00:00:00:11:11:11 +# Gateway Mac : 00:00:00:12:12:12 # # # Number of MLAG Interfaces:1 @@ -196,18 +256,33 @@ EXAMPLES = """ #----------------------------------------------------------- # PortChannel10 down/down # -# admin@sonic:~$ show runningconfiguration all -# { -# ... -# "MCLAG_UNIQUE_IP": { -# "Vlan4": { -# "unique_ip": "enable" -# } -# }, -# ... -# } -# -# +# sonic# show mclag separate-ip-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan21 +# Vlan22 +# Vlan23 +# Vlan24 +# Vlan25 +# ============== +# Total count : 6 +# ============== +# sonic# +# sonic# show mclag peer-gateway-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan21 +# Vlan22 +# Vlan23 +# Vlan24 +# Vlan25 +# ============== +# Total count : 6 +# ============== +# sonic# + - name: Merge device configuration with the provided configuration dellemc.enterprise_sonic.sonic_mclag: config: @@ -215,14 +290,20 @@ EXAMPLES = """ source_address: 3.3.3.3 keepalive: 10 session_timeout: 30 + delay_restore: 360 unique_ip: vlans: - vlan: Vlan5 + - vlan: Vlan26-28 + peer_gateway: + vlans: + - vlan: Vlan5 + - vlan: Vlan26-28 members: portchannels: - lag: PortChannel12 state: merged -# + # After state: # ------------ # @@ -237,7 +318,10 @@ EXAMPLES = """ # Peer Link : PortChannel1 # Keepalive Interval : 10 secs # Session Timeout : 30 secs +# Delay Restore : 360 secs # System Mac : 20:04:0f:37:bd:c9 +# Mclag System Mac : 00:00:00:11:11:11 +# Gateway Mac : 00:00:00:12:12:12 # # # Number of MLAG Interfaces:2 @@ -247,21 +331,41 @@ EXAMPLES = """ # PortChannel10 down/down # PortChannel12 down/down # -# admin@sonic:~$ show runningconfiguration all -# { -# ... -# "MCLAG_UNIQUE_IP": { -# "Vlan4": { -# "unique_ip": "enable" -# }, -# "Vlan5": { -# "unique_ip": "enable" -# } -# }, -# ... -# } -# -# +# sonic# show mclag separate-ip-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan5 +# Vlan21 +# Vlan22 +# Vlan23 +# Vlan24 +# Vlan25 +# Vlan26 +# Vlan27 +# Vlan28 +# ============== +# Total count : 10 +# ============== +# sonic# show mclag peer-gateway-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan5 +# Vlan21 +# Vlan22 +# Vlan23 +# Vlan24 +# Vlan25 +# Vlan26 +# Vlan27 +# Vlan28 +# ============== +# Total count : 10 +# ============== +# sonic# + + # Using deleted # # Before state: @@ -278,7 +382,10 @@ EXAMPLES = """ # Peer Link : PortChannel1 # Keepalive Interval : 10 secs # Session Timeout : 30 secs +# Delay Restore : 360 secs # System Mac : 20:04:0f:37:bd:c9 +# Mclag System Mac : 00:00:00:11:11:11 +# Gateway Mac : 00:00:00:12:12:12 # # # Number of MLAG Interfaces:1 @@ -287,28 +394,52 @@ EXAMPLES = """ #----------------------------------------------------------- # PortChannel10 down/down # -# admin@sonic:~$ show runningconfiguration all -# { -# ... -# "MCLAG_UNIQUE_IP": { -# "Vlan4": { -# "unique_ip": "enable" -# } -# }, -# ... -# } -# +# sonic# show mclag separate-ip-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan21 +# Vlan22 +# Vlan23 +# Vlan24 +# Vlan25 +# ============== +# Total count : 6 +# ============== +# sonic# +# sonic# show mclag peer-gateway-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan21 +# Vlan22 +# Vlan23 +# Vlan24 +# Vlan25 +# ============== +# Total count : 6 +# ============== +# sonic# + - name: Delete device configuration based on the provided configuration dellemc.enterprise_sonic.sonic_mclag: - config: - domain_id: 1 - source_address: 3.3.3.3 - keepalive: 10 - members: - portchannels: - - lag: PortChannel10 - state: deleted -# + config: + domain_id: 1 + source_address: 3.3.3.3 + keepalive: 10 + unique_ip: + vlans: + - vlan: Vlan22 + - vlan: Vlan24-25 + peer_gateway: + vlans: + - vlan: Vlan22 + - vlan: Vlan24-25 + members: + portchannels: + - lag: PortChannel10 + state: deleted + # After state: # ------------ # @@ -322,25 +453,37 @@ EXAMPLES = """ # Peer Address : 1.1.1.1 # Peer Link : PortChannel1 # Keepalive Interval : 1 secs -# Session Timeout : 15 secs +# Session Timeout : 30 secs +# Delay Restore : 360 secs # System Mac : 20:04:0f:37:bd:c9 +# Mclag System Mac : 00:00:00:11:11:11 +# Gateway Mac : 00:00:00:12:12:12 # # # Number of MLAG Interfaces:0 # -# admin@sonic:~$ show runningconfiguration all -# { -# ... -# "MCLAG_UNIQUE_IP": { -# "Vlan4": { -# "unique_ip": "enable" -# } -# }, -# ... -# } -# -# -# +# sonic# show mclag separate-ip-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan21 +# Vlan23 +# ============== +# Total count : 3 +# ============== +# sonic# +# sonic# show mclag peer-gateway-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan21 +# Vlan23 +# ============== +# Total count : 3 +# ============== +# sonic# + + # Using deleted # # Before state: @@ -357,7 +500,10 @@ EXAMPLES = """ # Peer Link : PortChannel1 # Keepalive Interval : 10 secs # Session Timeout : 30 secs +# Delay Restore : 360 secs # System Mac : 20:04:0f:37:bd:c9 +# Mclag System Mac : 00:00:00:11:11:11 +# Gateway Mac : 00:00:00:12:12:12 # # # Number of MLAG Interfaces:1 @@ -366,32 +512,40 @@ EXAMPLES = """ #----------------------------------------------------------- # PortChannel10 down/down # -# admin@sonic:~$ show runningconfiguration all -# { -# ... -# "MCLAG_UNIQUE_IP": { -# "Vlan4": { -# "unique_ip": "enable" -# } -# }, -# ... -# } -# +# sonic# show mclag separate-ip-interfaces +# Interface Name +# ============== +# Vlan4 +# ============== +# Total count : 1 +# ============== +# sonic# +# sonic# show mclag peer-gateway-interfaces +# Interface Name +# ============== +# Vlan4 +# ============== +# Total count : 1 +# ============== +# sonic# + - name: Delete all device configuration dellemc.enterprise_sonic.sonic_mclag: config: state: deleted -# + # After state: # ------------ # # sonic# show mclag brief # MCLAG Not Configured -# -# admin@sonic:~$ show runningconfiguration all | grep MCLAG_UNIQUE_IP -# admin@sonic:~$ -# -# +# sonic# show mclag separate-ip-interfaces +# MCLAG separate IP interface not configured +# sonic# show mclag peer-gateway-interfaces +# MCLAG Peer Gateway interface not configured +# sonic# + + # Using deleted # # Before state: @@ -408,7 +562,10 @@ EXAMPLES = """ # Peer Link : PortChannel1 # Keepalive Interval : 10 secs # Session Timeout : 30 secs +# Delay Restore : 360 secs # System Mac : 20:04:0f:37:bd:c9 +# Mclag System Mac : 00:00:00:11:11:11 +# Gateway Mac : 00:00:00:12:12:12 # # # Number of MLAG Interfaces:2 @@ -416,29 +573,37 @@ EXAMPLES = """ # MLAG Interface Local/Remote Status #----------------------------------------------------------- # PortChannel10 down/down -# PortChannel12 down/sown -# -# admin@sonic:~$ show runningconfiguration all -# { -# ... -# "MCLAG_UNIQUE_IP": { -# "Vlan4": { -# "unique_ip": "enable" -# } -# }, -# ... -# } +# PortChannel12 down/down +# +# sonic# show mclag separate-ip-interfaces +# Interface Name +# ============== +# Vlan4 +# ============== +# Total count : 1 +# ============== +# sonic# +# sonic# show mclag peer-gateway-interfaces +# Interface Name +# ============== +# Vlan4 +# ============== +# Total count : 1 +# ============== +# sonic# + - name: Delete device configuration based on the provided configuration dellemc.enterprise_sonic.sonic_mclag: config: domain_id: 1 source_address: 3.3.3.3 keepalive: 10 + peer_gateway: + vlans: members: portchannels: - - lag: PortChannel10 state: deleted -# + # After state: # ------------ # @@ -452,24 +617,283 @@ EXAMPLES = """ # Peer Address : 1.1.1.1 # Peer Link : PortChannel1 # Keepalive Interval : 1 secs -# Session Timeout : 15 secs +# Session Timeout : 30 secs +# Delay Restore : 360 secs # System Mac : 20:04:0f:37:bd:c9 +# Mclag System Mac : 00:00:00:11:11:11 +# Gateway Mac : 00:00:00:12:12:12 # # # Number of MLAG Interfaces:0 # -# admin@sonic:~$ show runningconfiguration all -# { -# ... -# "MCLAG_UNIQUE_IP": { -# "Vlan4": { -# "unique_ip": "enable" -# } -# }, -# ... -# } +# sonic# show mclag separate-ip-interfaces +# Interface Name +# ============== +# Vlan4 +# ============== +# Total count : 1 +# ============== +# sonic# +# sonic# show mclag peer-gateway-interfaces +# MCLAG Peer Gateway interface not configured +# sonic# + + +# Using replaced +# +# Before state: +# ------------ +# +# sonic# show mclag brief +# +# Domain ID : 1 +# Role : standby +# Session Status : down +# Peer Link Status : down +# Source Address : 2.2.2.2 +# Peer Address : 1.1.1.1 +# Peer Link : PortChannel1 +# Keepalive Interval : 1 secs +# Session Timeout : 3 secs +# Delay Restore : 240 secs +# System Mac : 20:04:0f:37:bd:c9 +# Mclag System Mac : 00:00:00:11:11:11 +# Gateway Mac : 00:00:00:12:12:12 +# +# +# Number of MLAG Interfaces:2 +#----------------------------------------------------------- +# MLAG Interface Local/Remote Status +#----------------------------------------------------------- +# PortChannel10 down/down +# PortChannel11 down/down +# +# sonic# show mclag separate-ip-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan21 +# Vlan22 +# Vlan23 +# Vlan24 +# Vlan25 +# ============== +# Total count : 6 +# ============== +# sonic# +# sonic# show mclag peer-gateway-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan21 +# Vlan22 +# Vlan23 +# Vlan24 +# Vlan25 +# ============== +# Total count : 6 +# ============== +# sonic# + +- name: Replace device configuration with the provided configuration + dellemc.enterprise_sonic.sonic_mclag: + config: + domain_id: 1 + unique_ip: + vlans: + - vlan: Vlan5 + - vlan: Vlan24-28 + peer_gateway: + vlans: + - vlan: Vlan5 + - vlan: Vlan24-28 + members: + portchannels: + - lag: PortChannel10 + - lag: PortChannel12 + state: replaced + +# After state: +# ------------ +# +# sonic# show mclag brief +# +# Domain ID : 1 +# Role : standby +# Session Status : down +# Peer Link Status : down +# Source Address : 2.2.2.2 +# Peer Address : 1.1.1.1 +# Peer Link : PortChannel1 +# Keepalive Interval : 1 secs +# Session Timeout : 3 secs +# Delay Restore : 240 secs +# System Mac : 20:04:0f:37:bd:c9 +# Mclag System Mac : 00:00:00:11:11:11 +# Gateway Mac : 00:00:00:12:12:12 +# +# +# Number of MLAG Interfaces:2 +#----------------------------------------------------------- +# MLAG Interface Local/Remote Status +#----------------------------------------------------------- +# PortChannel10 down/down +# PortChannel12 down/down +# +# sonic# show mclag separate-ip-interfaces +# Interface Name +# ============== +# Vlan5 +# Vlan24 +# Vlan25 +# Vlan26 +# Vlan27 +# Vlan28 +# ============== +# Total count : 6 +# ============== +# sonic# show mclag peer-gateway-interfaces +# Interface Name +# ============== +# Vlan5 +# Vlan24 +# Vlan25 +# Vlan26 +# Vlan27 +# Vlan28 +# ============== +# Total count : 6 +# ============== +# sonic# + + +# Using overridden +# +# Before state: +# ------------ +# +# sonic# show mclag brief +# +# Domain ID : 1 +# Role : standby +# Session Status : down +# Peer Link Status : down +# Source Address : 2.2.2.2 +# Peer Address : 1.1.1.1 +# Peer Link : PortChannel1 +# Keepalive Interval : 1 secs +# Session Timeout : 3 secs +# Delay Restore : 240 secs +# System Mac : 20:04:0f:37:bd:c9 +# Mclag System Mac : 00:00:00:11:11:11 +# Gateway Mac : 00:00:00:12:12:12 +# +# +# Number of MLAG Interfaces:2 +#----------------------------------------------------------- +# MLAG Interface Local/Remote Status +#----------------------------------------------------------- +# PortChannel10 down/down +# PortChannel11 down/down +# +# sonic# show mclag separate-ip-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan21 +# Vlan22 +# Vlan23 +# Vlan24 +# Vlan25 +# ============== +# Total count : 6 +# ============== +# sonic# +# sonic# show mclag peer-gateway-interfaces +# Interface Name +# ============== +# Vlan4 +# Vlan21 +# Vlan22 +# Vlan23 +# Vlan24 +# Vlan25 +# ============== +# Total count : 6 +# ============== +# sonic# + +- name: Override device configuration with the provided configuration + dellemc.enterprise_sonic.sonic_mclag: + config: + domain_id: 1 + peer_address: 1.1.1.1 + source_address: 3.3.3.3 + peer_link: 'Portchannel1' + system_mac: '00:00:00:11:11:11' + gateway_mac: '00:00:00:12:12:12' + unique_ip: + vlans: + - vlan: Vlan24-28 + peer_gateway: + vlans: + - vlan: Vlan24-28 + members: + portchannels: + - lag: PortChannel10 + - lag: PortChannel12 + state: overridden + +# After state: +# ------------ +# +# sonic# show mclag brief # +# Domain ID : 1 +# Role : standby +# Session Status : down +# Peer Link Status : down +# Source Address : 3.3.3.3 +# Peer Address : 1.1.1.1 +# Peer Link : PortChannel1 +# Keepalive Interval : 1 secs +# Session Timeout : 30 secs +# Delay Restore : 300 secs +# System Mac : 20:04:0f:37:bd:c9 +# Mclag System Mac : 00:00:00:11:11:11 +# Gateway Mac : 00:00:00:12:12:12 +# +# +# Number of MLAG Interfaces:2 +#----------------------------------------------------------- +# MLAG Interface Local/Remote Status +#----------------------------------------------------------- +# PortChannel10 down/down +# PortChannel12 down/down # +# sonic# show mclag separate-ip-interfaces +# Interface Name +# ============== +# Vlan24 +# Vlan25 +# Vlan26 +# Vlan27 +# Vlan28 +# ============== +# Total count : 5 +# ============== +# sonic# show mclag peer-gateway-interfaces +# Interface Name +# ============== +# Vlan24 +# Vlan25 +# Vlan26 +# Vlan27 +# Vlan28 +# ============== +# Total count : 5 +# ============== +# sonic# """ RETURN = """ before: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_ntp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_ntp.py index 87db8bb06..c04e437c1 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_ntp.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_ntp.py @@ -34,6 +34,8 @@ DOCUMENTATION = """ --- module: sonic_ntp version_added: 2.0.0 +notes: + - Supports C(check_mode). short_description: Manage NTP configuration on SONiC. description: - This module provides configuration management of NTP for devices running SONiC. @@ -67,6 +69,7 @@ options: elements: dict description: - List of NTP servers. + - minpoll and maxpoll are required to be configured together. suboptions: address: type: str @@ -88,6 +91,11 @@ options: description: - Maximum poll interval to poll NTP server. - maxpoll can not be deleted. + prefer: + type: bool + description: + - Indicates whether this server should be preferred. + - prefer can not be deleted. ntp_keys: type: list elements: dict @@ -127,8 +135,10 @@ options: - The state of the configuration after module completion. type: str choices: - - merged - - deleted + - merged + - replaced + - overridden + - deleted default: merged """ EXAMPLES = """ @@ -138,16 +148,16 @@ EXAMPLES = """ # ------------- # #sonic# show ntp server -#---------------------------------------------------------------------- -#NTP Servers minpoll maxpoll Authentication key ID -#---------------------------------------------------------------------- -#10.11.0.1 6 10 -#10.11.0.2 5 9 -#dell.com 6 9 -#dell.org 7 10 +#---------------------------------------------------------------------------- +#NTP Servers minpoll maxpoll Prefer Authentication key ID +#---------------------------------------------------------------------------- +#10.11.0.1 6 10 False +#10.11.0.2 5 9 False +#dell.com 6 9 False +#dell.org 7 10 True # - name: Delete NTP server configuration - ntp: + sonic_ntp: config: servers: - address: 10.11.0.2 @@ -158,11 +168,11 @@ EXAMPLES = """ # ------------ # #sonic# show ntp server -#---------------------------------------------------------------------- -#NTP Servers minpoll maxpoll Authentication key ID -#---------------------------------------------------------------------- -#10.11.0.1 6 10 -#dell.com 6 9 +#---------------------------------------------------------------------------- +#NTP Servers minpoll maxpoll Prefer Authentication key ID +#---------------------------------------------------------------------------- +#10.11.0.1 6 10 False +#dell.com 6 9 False # # # Using deleted @@ -177,7 +187,7 @@ EXAMPLES = """ #NTP source-interfaces: Ethernet0, Ethernet4, Ethernet8, Ethernet16 # - name: Delete NTP source-interface configuration - ntp: + sonic_ntp: config: source_interfaces: - Ethernet8 @@ -205,7 +215,7 @@ EXAMPLES = """ #ntp authentication-key 20 sha2-256 U2FsdGVkX1/eAzKj1teKhYWD7tnzOsYOijGeFAT0rKM= encrypted # - name: Delete NTP key configuration - ntp: + sonic_ntp: config: ntp_keys: - key_id: 10 @@ -225,14 +235,14 @@ EXAMPLES = """ # ------------- # #sonic# show ntp server -#---------------------------------------------------------------------- -#NTP Servers minpoll maxpoll Authentication key ID -#---------------------------------------------------------------------- -#10.11.0.1 6 10 -#dell.com 6 9 +#---------------------------------------------------------------------------- +#NTP Servers minpoll maxpoll Prefer Authentication key ID +#---------------------------------------------------------------------------- +#10.11.0.1 6 10 False +#dell.com 6 9 False # - name: Merge NTP server configuration - ntp: + sonic_ntp: config: servers: - address: 10.11.0.2 @@ -240,19 +250,20 @@ EXAMPLES = """ - address: dell.org minpoll: 7 maxpoll: 10 + prefer: true state: merged # After state: # ------------ # #sonic# show ntp server -#---------------------------------------------------------------------- -#NTP Servers minpoll maxpoll Authentication key ID -#---------------------------------------------------------------------- -#10.11.0.1 6 10 -#10.11.0.2 5 9 -#dell.com 6 9 -#dell.org 7 10 +#---------------------------------------------------------------------------- +#NTP Servers minpoll maxpoll Prefer Authentication key ID +#---------------------------------------------------------------------------- +#10.11.0.1 6 10 Flase +#10.11.0.2 5 10 Flase +#dell.com 6 9 Flase +#dell.org 7 10 True # # # Using merged @@ -267,7 +278,7 @@ EXAMPLES = """ #NTP source-interfaces: Ethernet0, Ethernet4 # - name: Merge NTP source-interface configuration - ntp: + sonic_ntp: config: source_interfaces: - Ethernet8 @@ -293,7 +304,7 @@ EXAMPLES = """ #ntp authentication-key 8 sha1 U2FsdGVkX1/NpJrdOeyMeUHEkSohY6azY9VwbAqXRTY= encrypted # - name: Merge NTP key configuration - ntp: + sonic_ntp: config: ntp_keys: - key_id: 10 @@ -314,6 +325,87 @@ EXAMPLES = """ #ntp authentication-key 10 md5 U2FsdGVkX1/Gxds/5pscCvIKbVngGaKka4SQineS51Y= encrypted #ntp authentication-key 20 sha2-256 U2FsdGVkX1/eAzKj1teKhYWD7tnzOsYOijGeFAT0rKM= encrypted # +# Using replaced +# +# Before state: +# ------------- +# +#sonic# show ntp server +#---------------------------------------------------------------------------- +#NTP Servers minpoll maxpoll Prefer Authentication key ID +#---------------------------------------------------------------------------- +#10.11.0.1 6 10 False +#dell.com 6 9 False +# +- name: Replace NTP server configuration + sonic_ntp: + config: + servers: + - address: 10.11.0.2 + minpoll: 5 + maxpoll: 9 + - address: dell.com + minpoll: 7 + maxpoll: 10 + prefer: true + state: replaced +# +# After state: +# ------------ +# +#sonic# show ntp server +#---------------------------------------------------------------------------- +#NTP Servers minpoll maxpoll Prefer Authentication key ID +#---------------------------------------------------------------------------- +#10.11.0.1 6 10 False +#10.11.0.2 5 9 False +#dell.com 7 10 True +# +# Using overridden +# +# Before state: +# ------------- +# +#sonic# show ntp server +#---------------------------------------------------------------------------- +#NTP Servers minpoll maxpoll Prefer Authentication key ID +#---------------------------------------------------------------------------- +#10.11.0.1 6 10 False +#dell.com 6 9 False +# +#sonic# show ntp global +#---------------------------------------------- +#NTP Global Configuration +#---------------------------------------------- +#NTP source-interfaces: Ethernet0, Ethernet4 +# +- name: Overridden NTP configuration + sonic_ntp: + config: + servers: + - address: 10.11.0.2 + minpoll: 5 + - address: dell.com + minpoll: 7 + maxpoll: 10 + prefer: true + state: overridden +# +# After state: +# ------------ +# +# After state: +# ------------ +# +#sonic# show ntp server +#---------------------------------------------------------------------------- +#NTP Servers minpoll maxpoll Prefer Authentication key ID +#---------------------------------------------------------------------------- +#10.11.0.2 5 10 False +#dell.com 7 10 True +# +#sonic# show ntp global +# """ RETURN = """ before: @@ -330,6 +422,13 @@ after: sample: > The configuration returned will always be in the same format of the parameters above. +after(generated): + description: The generated configuration model invocation. + returned: when C(check_mode) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_pki.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_pki.py new file mode 100644 index 000000000..559935fef --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_pki.py @@ -0,0 +1,301 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Dell EMC +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_pki +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: sonic_pki +version_added: 2.3.0 +short_description: 'Manages PKI attributes of Enterprise Sonic' +description: 'Manages PKI attributes of Enterprise Sonic' +author: Eric Seifert (@seiferteric) +notes: + - 'Tested against Dell Enterprise SONiC 4.1.0' +options: + config: + description: The provided configuration + type: dict + suboptions: + trust_stores: + description: Store of CA Certificates + type: list + elements: dict + suboptions: + name: + type: str + required: True + description: The name of the Trust Store + ca_name: + type: list + elements: str + description: List of CA certificates in the trust store. + security_profiles: + description: Application Security Profiles + type: list + elements: dict + suboptions: + profile_name: + type: str + required: True + description: Profile Name + certificate_name: + type: str + description: Host Certificate Name + trust_store: + type: str + description: Name of associated trust_store + revocation_check: + description: Require certificate revocation check succeeds + type: bool + peer_name_check: + description: Require peer name is verified + type: bool + key_usage_check: + description: Require key usage is enforced + type: bool + cdp_list: + description: Global list of CDP's + type: list + elements: str + ocsp_responder_list: + description: Global list of OCSP responders + type: list + elements: str + state: + description: + - The state of the configuration after module completion. + type: str + choices: ['merged', 'deleted', 'replaced', 'overridden'] + default: merged +""" +EXAMPLES = """ +# Using "merged" state for initial config +# +# Before state: +# ------------- +# +# sonic# show running-configuration | grep crypto +# sonic# +# +- name: PKI Config Test + hosts: datacenter + gather_facts: false + connection: httpapi + collections: + - dellemc.enterprise_sonic + tasks: + - name: "Initial Config" + sonic_pki: + config: + security_profiles: + - profile_name: rest + ocsp_responder_list: + - http://example.com/ocspa + - http://example.com/ocspb + certificate_name: host + trust_store: default-ts + trust_stores: + - name: default-ts + ca_name: + - CA2 + state: merged + +# After state: +# ------------ +# +# sonic# show running-configuration | grep crypto +# crypto trust_store default-ts ca-cert CA2 +# crypto security-profile rest +# crypto security-profile trust_store rest default-ts +# crypto security-profile certificate rest host +# crypto security-profile ocsp-list rest http://example.com/ocspa,http://example.com/ocspb + +# Using "deleted" state to remove configuration +# +# Before state: +# ------------ +# +# sonic# show running-configuration | grep crypto +# crypto trust_store default-ts ca-cert CA2 +# crypto security-profile rest +# crypto security-profile trust_store rest default-ts +# crypto security-profile certificate rest host +# crypto security-profile ocsp-list rest http://example.com/ocsp +# +- name: PKI Delete Test + hosts: datacenter + gather_facts: true + connection: httpapi + collections: + - dellemc.enterprise_sonic + tasks: + - name: Remove trust_store from security-profile + sonic_pki: + config: + security_profiles: + - profile_name: rest + trust_store: default-ts + state: deleted +# After state: +# ------------ +# +# sonic# show running-configuration | grep crypto +# crypto trust_store default-ts ca-cert CA2 +# crypto security-profile rest +# crypto security-profile certificate rest host +# crypto security-profile ocsp-list rest http://example.com/ocsp + +# Using "overridden" state + +# Before state: +# ------------ +# +# sonic# show running-configuration | grep crypto +# crypto trust_store default-ts ca-cert CA2 +# crypto security-profile rest +# crypto security-profile trust_store rest default-ts +# crypto security-profile certificate rest host +# crypto security-profile ocsp-list rest http://example.com/ocspa,http://example.com/ocspb +# +- name: PKI Overridden Test + hosts: datacenter + gather_facts: false + connection: httpapi + collections: + - dellemc.enterprise_sonic + tasks: + - name: "Overridden Config" + sonic_pki: + config: + security_profiles: + - profile_name: telemetry + ocsp_responder_list: + - http://example.com/ocspb + revocation_check: true + trust_store: telemetry-ts + certificate_name: host + trust_stores: + - name: telemetry-ts + ca_name: CA + state: overridden +# After state: +# ----------- +# +# sonic# show running-configuration | grep crypto +# crypto trust_store telemetry-ts ca-cert CA +# crypto security-profile telemetry revocation_check true +# crypto security-profile trust_store telemetry telemetry-ts +# crypto security-profile certificate telemetry host +# crypto security-profile ocsp-list telemetry http://example.com/ocspb + +# Using "replaced" state to update config + +# Before state: +# ------------ +# +# sonic# show running-configuration | grep crypto +# crypto trust_store default-ts ca-cert CA2 +# crypto security-profile rest +# crypto security-profile trust_store rest default-ts +# crypto security-profile certificate rest host +# crypto security-profile ocsp-list rest http://example.com/ocspa,http://example.com/ocspb +# +- name: PKI Replace Test + hosts: datacenter + gather_facts: false + connection: httpapi + collections: + - dellemc.enterprise_sonic + tasks: + - name: "Replace Config" + sonic_pki: + config: + security_profiles: + - profile_name: rest + ocsp_responder_list: + - http://example.com/ocsp + revocation_check: false + trust_store: default-ts + certificate_name: host + state: replaced +# After state: +# ----------- +# +# sonic# show running-configuration | grep crypto +# crypto trust_store default-ts ca-cert CA2 +# crypto security-profile rest +# crypto security-profile trust_store rest default-ts +# crypto security-profile certificate rest host +# crypto security-profile ocsp-list rest http://example.com/ocsp + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: dict + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: dict + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.pki.pki import PkiArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.pki.pki import Pki + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=PkiArgs.argument_spec, + supports_check_mode=True) + + result = Pki(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_port_breakout.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_port_breakout.py index 66ea00476..3de7dfb17 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_port_breakout.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_port_breakout.py @@ -57,23 +57,35 @@ options: - Specifies the mode of the port breakout. type: str choices: + - 1x10G + - 1x25G + - 1x40G + - 1x50G - 1x100G + - 1x200G - 1x400G - - 1x40G + - 2x10G + - 2x25G + - 2x40G + - 2x50G - 2x100G - 2x200G - - 2x50G - - 4x100G - 4x10G - 4x25G - 4x50G + - 4x100G + - 8x10G + - 8x25G + - 8x50G state: description: - Specifies the operation to be performed on the port breakout configured on the device. - In case of merged, the input mode configuration will be merged with the existing port breakout configuration on the device. - - In case of deleted the existing port breakout mode configuration will be removed from the device. + - In case of deleted, the existing port breakout mode configuration will be removed from the device. + - In case of replaced, on-device port breakout configuration of the specified interfaces is replaced with provided configuration. + - In case of overridden, all on-device port breakout configurations are overridden with the provided configuration. default: merged - choices: ['merged', 'deleted'] + choices: ['merged', 'deleted', 'replaced', 'overridden'] type: str """ EXAMPLES = """ @@ -82,18 +94,18 @@ EXAMPLES = """ # Before state: # ------------- # -#do show interface breakout -#----------------------------------------------- -#Port Breakout Mode Status Interfaces -#----------------------------------------------- -#1/1 4x10G Completed Eth1/1/1 -# Eth1/1/2 -# Eth1/1/3 -# Eth1/1/4 -#1/11 1x100G Completed Eth1/11 +# sonic# show interface breakout +# ----------------------------------------------- +# Port Breakout Mode Status Interfaces +# ----------------------------------------------- +# 1/1 4x10G Completed Eth1/1/1 +# Eth1/1/2 +# Eth1/1/3 +# Eth1/1/4 +# 1/11 1x100G Completed Eth1/11/1 # -- name: Merge users configurations +- name: Delete interface port breakout configuration dellemc.enterprise_sonic.sonic_port_breakout: config: - name: 1/11 @@ -103,15 +115,16 @@ EXAMPLES = """ # After state: # ------------ # -#do show interface breakout -#----------------------------------------------- -#Port Breakout Mode Status Interfaces -#----------------------------------------------- -#1/1 4x10G Completed Eth1/1/1 -# Eth1/1/2 -# Eth1/1/3 -# Eth1/1/4 -#1/11 Default Completed Ethernet40 +# sonic# show interface breakout +# ----------------------------------------------- +# Port Breakout Mode Status Interfaces +# ----------------------------------------------- +# 1/1 4x10G Completed Eth1/1/1 +# Eth1/1/2 +# Eth1/1/3 +# Eth1/1/4 +# 1/11 Default Completed Eth1/11 +# # Using deleted @@ -119,31 +132,31 @@ EXAMPLES = """ # Before state: # ------------- # -#do show interface breakout -#----------------------------------------------- -#Port Breakout Mode Status Interfaces -#----------------------------------------------- -#1/1 4x10G Completed Eth1/1/1 -# Eth1/1/2 -# Eth1/1/3 -# Eth1/1/4 -#1/11 1x100G Completed Eth1/11 -# -- name: Merge users configurations +# sonic# show interface breakout +# ----------------------------------------------- +# Port Breakout Mode Status Interfaces +# ----------------------------------------------- +# 1/1 4x10G Completed Eth1/1/1 +# Eth1/1/2 +# Eth1/1/3 +# Eth1/1/4 +# 1/11 1x100G Completed Eth1/11/1 +# + +- name: Delete all port breakout configurations dellemc.enterprise_sonic.sonic_port_breakout: config: state: deleted - # After state: # ------------ # -#do show interface breakout -#----------------------------------------------- -#Port Breakout Mode Status Interfaces -#----------------------------------------------- -#1/1 Default Completed Ethernet0 -#1/11 Default Completed Ethernet40 +# sonic# show interface breakout +# ----------------------------------------------- +# Port Breakout Mode Status Interfaces +# ----------------------------------------------- +# 1/1 Default Completed Eth1/1 +# 1/11 Default Completed Eth1/11 # Using merged @@ -151,35 +164,111 @@ EXAMPLES = """ # Before state: # ------------- # -#do show interface breakout -#----------------------------------------------- -#Port Breakout Mode Status Interfaces -#----------------------------------------------- -#1/1 4x10G Completed Eth1/1/1 -# Eth1/1/2 -# Eth1/1/3 -# Eth1/1/4 +# sonic# show interface breakout +# ----------------------------------------------- +# Port Breakout Mode Status Interfaces +# ----------------------------------------------- +# 1/1 4x10G Completed Eth1/1/1 +# Eth1/1/2 +# Eth1/1/3 +# Eth1/1/4 # -- name: Merge users configurations + +- name: Merge port breakout configurations dellemc.enterprise_sonic.sonic_port_breakout: config: - name: 1/11 mode: 1x100G state: merged +# After state: +# ------------ +# +# sonic# show interface breakout +# ----------------------------------------------- +# Port Breakout Mode Status Interfaces +# ----------------------------------------------- +# 1/1 4x10G Completed Eth1/1/1 +# Eth1/1/2 +# Eth1/1/3 +# Eth1/1/4 +# 1/11 1x100G Completed Eth1/11/1 + + +# Using replaced +# +# Before state: +# ------------- +# +# sonic# show interface breakout +# ----------------------------------------------- +# Port Breakout Mode Status Interfaces +# ----------------------------------------------- +# 1/49 4x25G Completed Eth1/49/1 +# Eth1/49/2 +# Eth1/49/3 +# Eth1/49/4 +# + +- name: Replace port breakout configurations + dellemc.enterprise_sonic.sonic_port_breakout: + config: + - name: 1/49 + mode: 4x10G + state: replaced + +# After state: +# ------------ +# +# sonic# show interface breakout +# ----------------------------------------------- +# Port Breakout Mode Status Interfaces +# ----------------------------------------------- +# 1/49 4x10G Completed Eth1/49/1 +# Eth1/49/2 +# Eth1/49/3 +# Eth1/49/4 + + +# Using overridden +# +# Before state: +# ------------- +# +# sonic# show interface breakout +# ---------------------------------------------- +# Port Breakout Mode Status Interfaces +# ----------------------------------------------- +# 1/49 4x10G Completed Eth1/49/1 +# Eth1/49/2 +# Eth1/49/3 +# Eth1/49/4 +# 1/50 2x50G Completed Eth1/50/1 +# Eth1/50/2 +# 1/51 1x100G Completed Eth1/51/1 +# + +- name: Override port breakout configurations + dellemc.enterprise_sonic.sonic_port_breakout: + config: + - name: 1/52 + mode: 4x10G + state: overridden # After state: # ------------ # -#do show interface breakout -#----------------------------------------------- -#Port Breakout Mode Status Interfaces -#----------------------------------------------- -#1/1 4x10G Completed Eth1/1/1 -# Eth1/1/2 -# Eth1/1/3 -# Eth1/1/4 -#1/11 1x100G Completed Eth1/11 +# sonic# show interface breakout +# ----------------------------------------------- +# Port Breakout Mode Status Interfaces +# ----------------------------------------------- +# 1/49 Default Completed Eth1/49 +# 1/50 Default Completed Eth1/50 +# 1/51 Default Completed Eth1/51 +# 1/52 4x10G Completed Eth1/52/1 +# Eth1/52/2 +# Eth1/52/3 +# Eth1/52/4 """ diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_port_group.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_port_group.py new file mode 100644 index 000000000..d31c19cd3 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_port_group.py @@ -0,0 +1,370 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# © Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_port_group +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = """ +--- +module: sonic_port_group +version_added: 2.1.0 +notes: + - Supports C(check_mode). +short_description: Manages port group configuration on SONiC. +description: + - This module provides configuration management of port group for devices running SONiC. +author: 'M. Zhang (@mingjunzhang2019)' +options: + config: + description: + - A list of port group configurations. + type: list + elements: dict + suboptions: + id: + type: str + description: + - The index of the port group. + required: true + speed: + description: + - Speed for the port group. + - This configures the speed for all the memebr ports of the prot group. + - Supported speeds are dependent on the type of switch. + type: str + choices: + - SPEED_10MB + - SPEED_100MB + - SPEED_1GB + - SPEED_2500MB + - SPEED_5GB + - SPEED_10GB + - SPEED_20GB + - SPEED_25GB + - SPEED_40GB + - SPEED_50GB + - SPEED_100GB + - SPEED_400GB + state: + description: + - The state of the configuration after module completion. + type: str + choices: + - merged + - replaced + - overridden + - deleted + default: merged +""" +EXAMPLES = """ +# +# Using deleted +# +# Before state: +# ------------- +# +#sonic# show port-group +#------------------------------------------------------------------------------------- +#Port-group Interface range Valid speeds Default Speed Current Speed +#------------------------------------------------------------------------------------- +#1 Ethernet0 - Ethernet3 10G, 25G 25G 10G +#2 Ethernet4 - Ethernet7 10G, 25G 25G 25G +#3 Ethernet8 - Ethernet11 10G, 25G 25G 25G +#4 Ethernet12 - Ethernet15 10G, 25G 25G 25G +#5 Ethernet16 - Ethernet19 10G, 25G 25G 25G +#6 Ethernet20 - Ethernet23 10G, 25G 25G 25G +#7 Ethernet24 - Ethernet27 10G, 25G 25G 25G +#8 Ethernet28 - Ethernet31 10G, 25G 25G 25G +#9 Ethernet32 - Ethernet35 10G, 25G 25G 10G +#10 Ethernet36 - Ethernet39 10G, 25G 25G 25G +# +- name: Configure port group speed + sonic_port_group: + config: + - id: 1 + - id: 10 + state: deleted +# +# +# After state: +# ------------ +# +#sonic# show port-group +#------------------------------------------------------------------------------------- +#Port-group Interface range Valid speeds Default Speed Current Speed +#------------------------------------------------------------------------------------- +#1 Ethernet0 - Ethernet3 10G, 25G 25G 25G +#2 Ethernet4 - Ethernet7 10G, 25G 25G 25G +#3 Ethernet8 - Ethernet11 10G, 25G 25G 25G +#4 Ethernet12 - Ethernet15 10G, 25G 25G 25G +#5 Ethernet16 - Ethernet19 10G, 25G 25G 25G +#6 Ethernet20 - Ethernet23 10G, 25G 25G 25G +#7 Ethernet24 - Ethernet27 10G, 25G 25G 25G +#8 Ethernet28 - Ethernet31 10G, 25G 25G 25G +#9 Ethernet32 - Ethernet35 10G, 25G 25G 10G +#10 Ethernet36 - Ethernet39 10G, 25G 25G 25G +# +# Using deleted +# +# Before state: +# ------------- +# +#sonic# show port-group +#------------------------------------------------------------------------------------- +#Port-group Interface range Valid speeds Default Speed Current Speed +#------------------------------------------------------------------------------------- +#1 Ethernet0 - Ethernet3 10G, 25G 25G 10G +#2 Ethernet4 - Ethernet7 10G, 25G 25G 25G +#3 Ethernet8 - Ethernet11 10G, 25G 25G 25G +#4 Ethernet12 - Ethernet15 10G, 25G 25G 25G +#5 Ethernet16 - Ethernet19 10G, 25G 25G 25G +#6 Ethernet20 - Ethernet23 10G, 25G 25G 25G +#7 Ethernet24 - Ethernet27 10G, 25G 25G 25G +#8 Ethernet28 - Ethernet31 10G, 25G 25G 25G +#9 Ethernet32 - Ethernet35 10G, 25G 25G 10G +#10 Ethernet36 - Ethernet39 10G, 25G 25G 25G +# +- name: Configure port group speed + sonic_port_group: + config: + - id: + state: deleted +# +# +# After state: +# ------------ +# +#sonic# show port-group +#------------------------------------------------------------------------------------- +#Port-group Interface range Valid speeds Default Speed Current Speed +#------------------------------------------------------------------------------------- +#1 Ethernet0 - Ethernet3 10G, 25G 25G 25G +#2 Ethernet4 - Ethernet7 10G, 25G 25G 25G +#3 Ethernet8 - Ethernet11 10G, 25G 25G 25G +#4 Ethernet12 - Ethernet15 10G, 25G 25G 25G +#5 Ethernet16 - Ethernet19 10G, 25G 25G 25G +#6 Ethernet20 - Ethernet23 10G, 25G 25G 25G +#7 Ethernet24 - Ethernet27 10G, 25G 25G 25G +#8 Ethernet28 - Ethernet31 10G, 25G 25G 25G +#9 Ethernet32 - Ethernet35 10G, 25G 25G 25G +#10 Ethernet36 - Ethernet39 10G, 25G 25G 25G +# +# Using merged +# +# Before state: +# ------------- +# +#sonic# show port-group +#------------------------------------------------------------------------------------- +#Port-group Interface range Valid speeds Default Speed Current Speed +#------------------------------------------------------------------------------------- +#1 Ethernet0 - Ethernet3 10G, 25G 25G 25G +#2 Ethernet4 - Ethernet7 10G, 25G 25G 25G +#3 Ethernet8 - Ethernet11 10G, 25G 25G 25G +#4 Ethernet12 - Ethernet15 10G, 25G 25G 25G +#5 Ethernet16 - Ethernet19 10G, 25G 25G 25G +#6 Ethernet20 - Ethernet23 10G, 25G 25G 25G +#7 Ethernet24 - Ethernet27 10G, 25G 25G 25G +#8 Ethernet28 - Ethernet31 10G, 25G 25G 25G +#9 Ethernet32 - Ethernet35 10G, 25G 25G 25G +#10 Ethernet36 - Ethernet39 10G, 25G 25G 25G +# +- name: Configure port group speed + sonic_port_group: + config: + - id: 1 + speed: SPEED_10GB + - id: 9 + speed: SPEED_10GB + state: merged +# +# +# After state: +# ------------ +# +#sonic# show port-group +#------------------------------------------------------------------------------------- +#Port-group Interface range Valid speeds Default Speed Current Speed +#------------------------------------------------------------------------------------- +#1 Ethernet0 - Ethernet3 10G, 25G 25G 10G +#2 Ethernet4 - Ethernet7 10G, 25G 25G 25G +#3 Ethernet8 - Ethernet11 10G, 25G 25G 25G +#4 Ethernet12 - Ethernet15 10G, 25G 25G 25G +#5 Ethernet16 - Ethernet19 10G, 25G 25G 25G +#6 Ethernet20 - Ethernet23 10G, 25G 25G 25G +#7 Ethernet24 - Ethernet27 10G, 25G 25G 25G +#8 Ethernet28 - Ethernet31 10G, 25G 25G 25G +#9 Ethernet32 - Ethernet35 10G, 25G 25G 10G +#10 Ethernet36 - Ethernet39 10G, 25G 25G 25G +# +# Using replaced +# +# Before state: +# ------------- +# +#sonic# show port-group +#------------------------------------------------------------------------------------- +#Port-group Interface range Valid speeds Default Speed Current Speed +#------------------------------------------------------------------------------------- +#1 Ethernet0 - Ethernet3 10G, 25G 25G 25G +#2 Ethernet4 - Ethernet7 10G, 25G 25G 25G +#3 Ethernet8 - Ethernet11 10G, 25G 25G 25G +#4 Ethernet12 - Ethernet15 10G, 25G 25G 10G +#5 Ethernet16 - Ethernet19 10G, 25G 25G 25G +#6 Ethernet20 - Ethernet23 10G, 25G 25G 25G +#7 Ethernet24 - Ethernet27 10G, 25G 25G 25G +#8 Ethernet28 - Ethernet31 10G, 25G 25G 25G +#9 Ethernet32 - Ethernet35 10G, 25G 25G 25G +#10 Ethernet36 - Ethernet39 10G, 25G 25G 25G +# +- name: Replace port group speed + sonic_port_group: + config: + - id: 1 + speed: SPEED_10GB + - id: 9 + speed: SPEED_10GB + state: replaced +# +# After state: +# ------------ +# +#sonic# show port-group +#------------------------------------------------------------------------------------- +#Port-group Interface range Valid speeds Default Speed Current Speed +#------------------------------------------------------------------------------------- +#1 Ethernet0 - Ethernet3 10G, 25G 25G 10G +#2 Ethernet4 - Ethernet7 10G, 25G 25G 25G +#3 Ethernet8 - Ethernet11 10G, 25G 25G 25G +#4 Ethernet12 - Ethernet15 10G, 25G 25G 10G +#5 Ethernet16 - Ethernet19 10G, 25G 25G 25G +#6 Ethernet20 - Ethernet23 10G, 25G 25G 25G +#7 Ethernet24 - Ethernet27 10G, 25G 25G 25G +#8 Ethernet28 - Ethernet31 10G, 25G 25G 25G +#9 Ethernet32 - Ethernet35 10G, 25G 25G 10G +#10 Ethernet36 - Ethernet39 10G, 25G 25G 25G +# +# Using overridden +# +# Before state: +# ------------- +# +#sonic# show port-group +#------------------------------------------------------------------------------------- +#Port-group Interface range Valid speeds Default Speed Current Speed +#------------------------------------------------------------------------------------- +#1 Ethernet0 - Ethernet3 10G, 25G 25G 25G +#2 Ethernet4 - Ethernet7 10G, 25G 25G 10G +#3 Ethernet8 - Ethernet11 10G, 25G 25G 10G +#4 Ethernet12 - Ethernet15 10G, 25G 25G 25G +#5 Ethernet16 - Ethernet19 10G, 25G 25G 10G +#6 Ethernet20 - Ethernet23 10G, 25G 25G 25G +#7 Ethernet24 - Ethernet27 10G, 25G 25G 10G +#8 Ethernet28 - Ethernet31 10G, 25G 25G 10G +#9 Ethernet32 - Ethernet35 10G, 25G 25G 10G +#10 Ethernet36 - Ethernet39 10G, 25G 25G 10G +# +- name: Override port group speed + sonic_port_group: + config: + - id: 1 + speed: SPEED_10GB + - id: 9 + speed: SPEED_10GB + state: overridden +# +# After state: +# ------------ +# +#sonic# show port-group +#------------------------------------------------------------------------------------- +#Port-group Interface range Valid speeds Default Speed Current Speed +#------------------------------------------------------------------------------------- +#1 Ethernet0 - Ethernet3 10G, 25G 25G 10G +#2 Ethernet4 - Ethernet7 10G, 25G 25G 25G +#3 Ethernet8 - Ethernet11 10G, 25G 25G 25G +#4 Ethernet12 - Ethernet15 10G, 25G 25G 25G +#5 Ethernet16 - Ethernet19 10G, 25G 25G 25G +#6 Ethernet20 - Ethernet23 10G, 25G 25G 25G +#7 Ethernet24 - Ethernet27 10G, 25G 25G 25G +#8 Ethernet28 - Ethernet31 10G, 25G 25G 25G +#9 Ethernet32 - Ethernet35 10G, 25G 25G 10G +#10 Ethernet36 - Ethernet39 10G, 25G 25G 25G +# +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after(generated): + description: The generated configuration model invocation. + returned: when C(check_mode) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.port_group.port_group import Port_groupArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.port_group.port_group import Port_group + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=Port_groupArgs.argument_spec, + supports_check_mode=True) + + result = Port_group(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_prefix_lists.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_prefix_lists.py index 5a734e8b2..b3389b6ad 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_prefix_lists.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_prefix_lists.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright 2019 Red Hat +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -87,11 +87,15 @@ options: description: - Specifies the type of configuration update to be performed on the device. - For "merged", merge specified attributes with existing configured attributes. - - For "deleted", delete the specified attributes from exiting configuration. + - For "deleted", delete the specified attributes from existing configuration. + - For "replaced", replace the specified existing configuration with the provided configuration. + - For "overridden", override the existing configuration with the provided configuration. type: str choices: - merged - deleted + - replaced + - overridden default: merged """ EXAMPLES = """ @@ -227,6 +231,95 @@ EXAMPLES = """ # sonic# # (no IPv6 prefix-list configuration present) # +# *************************************************************** +# Using "overriden" state to override configuration +# +# Before state: +# ------------ +# +# sonic# show running-configuration ip prefix-list +# ! +# ip prefix-list pfx1 seq 10 permit 1.2.3.4/24 ge 26 le 30 +# ip prefix-list pfx3 seq 20 deny 1.2.3.12/26 +# ip prefix-list pfx4 seq 30 permit 7.8.9.0/24 +# +# sonic# show running-configuration ipv6 prefix-list +# ! +# ipv6 prefix-list pfx6 seq 25 permit 40::300/124 +# +# ------------ +# +- name: Override prefix-list configuration + dellemc.enterprise_sonic.sonic_prefix_lists: + config: + - name: pfx2 + afi: "ipv4" + prefixes: + - sequence: 10 + prefix: "10.20.30.128/24" + action: "deny" + ge: 25 + le: 30 + state: overridden + +# After state: +# ------------ +# +# sonic# show running-configuration ip prefix-list +# ! +# ip prefix-list pfx2 seq 10 deny 10.20.30.128/24 ge 25 le 30 +# +# sonic# show running-configuration ipv6 prefix-list +# sonic# +# (no IPv6 prefix-list configuration present) +# +# *************************************************************** +# Using "replaced" state to replace configuration +# +# Before state: +# ------------ +# +# sonic# show running-configuration ip prefix-list +# ! +# ip prefix-list pfx2 seq 10 deny 10.20.30.128/24 ge 25 le 30 +# +# sonic# show running-configuration ipv6 prefix-list +# sonic# +# (no IPv6 prefix-list configuration present) +# +# ------------ +# +- name: Replace prefix-list configuration + dellemc.enterprise_sonic.sonic_prefix_lists: + config: + - name: pfx2 + afi: "ipv4" + prefixes: + - sequence: 10 + prefix: "10.20.30.128/24" + action: "permit" + ge: 25 + le: 30 + - name: pfx3 + afi: "ipv6" + prefixes: + - sequence: 20 + action: "deny" + prefix: "60::70/124" + state: replaced + +# After state: +# ------------ +# +# sonic# show running-configuration ip prefix-list +# ! +# ip prefix-list pfx2 seq 10 permit 10.20.30.128/24 ge 25 le 30 +# +# sonic# show running-configuration ipv6 prefix-list +# sonic# +# ! +# ipv6 prefix-list pfx3 seq 20 deny 60::70/124 +# """ RETURN = """ before: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_radius_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_radius_server.py index 1df4aff61..bc1f81d39 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_radius_server.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_radius_server.py @@ -71,6 +71,7 @@ options: description: - Specifies the timeout of the radius server. type: int + default: 5 retransmit: description: - Specifies the re-transmit value of the radius server. @@ -110,6 +111,7 @@ options: description: - Specifies the port of the radius server host. type: int + default: 1812 timeout: description: - Specifies the timeout of the radius server host. @@ -131,8 +133,10 @@ options: - Specifies the operation to be performed on the radius server configured on the device. - In case of merged, the input mode configuration will be merged with the existing radius server configuration on the device. - In case of deleted the existing radius server mode configuration will be removed from the device. + - In case of replaced, the existing radius server configuration will be replaced with provided configuration. + - In case of overridden, the existing radius server configuration will be overridden with the provided configuration. default: merged - choices: ['merged', 'deleted'] + choices: ['merged', 'replaced', 'overridden', 'deleted'] type: str """ EXAMPLES = """ @@ -280,8 +284,106 @@ EXAMPLES = """ #--------------------------------------------------------- #RADIUS Statistics #--------------------------------------------------------- - - +# +# Using replaced +# +# Before state: +# ------------- +# +#sonic(config)# do show radius-server +#--------------------------------------------------------- +#RADIUS Global Configuration +#--------------------------------------------------------- +#timeout : 10 +#auth-type : pap +#key configured : Yes +#-------------------------------------------------------------------------------------- +#HOST AUTH-TYPE KEY-CONFIG AUTH-PORT PRIORITY TIMEOUT RTSMT VRF SI +#-------------------------------------------------------------------------------------- +#1.2.3.4 pap No 49 1 5 - - Ethernet0 +# +- name: Replace radius configurations + sonic_radius_server: + config: + auth_type: mschapv2 + timeout: 20 + servers: + - host: + name: 1.2.3.4 + auth_type: mschapv2 + key: mschapv2 + source_interface: Ethernet12 + state: replaced +# +# After state: +# ------------ +# +#sonic(config)# do show radius-server +#--------------------------------------------------------- +#RADIUS Global Configuration +#--------------------------------------------------------- +#timeout : 20 +#auth-type : mschapv2 +#key configured : No +#-------------------------------------------------------------------------------------- +#HOST AUTH-TYPE KEY-CONFIG AUTH-PORT PRIORITY TIMEOUT RTSMT VRF SI +#-------------------------------------------------------------------------------------- +#1.2.3.4 mschapv2 Yes 1812 - - - - Ethernet12 +# +# Using overridden +# +# Before state: +# ------------- +# +#sonic(config)# do show radius-server +#--------------------------------------------------------- +#RADIUS Global Configuration +#--------------------------------------------------------- +#timeout : 10 +#auth-type : pap +#key configured : Yes +#-------------------------------------------------------------------------------------- +#HOST AUTH-TYPE KEY-CONFIG AUTH-PORT PRIORITY TIMEOUT RTSMT VRF SI +#-------------------------------------------------------------------------------------- +#1.2.3.4 pap No 49 1 5 - - Ethernet0 +#11.12.13.14 chap Yes 49 10 5 3 - - +# +- name: Override radius configurations + sonic_radius_server: + config: + auth_type: mschapv2 + key: mschapv2 + timeout: 20 + servers: + - host: + name: 1.2.3.4 + auth_type: mschapv2 + key: mschapv2 + source_interface: Ethernet12 + - host: + name: 10.10.11.12 + auth_type: chap + timeout: 30 + priority: 2 + port: 49 + state: overridden +# +# After state: +# ------------ +# +#sonic(config)# do show radius-server +#--------------------------------------------------------- +#RADIUS Global Configuration +#--------------------------------------------------------- +#timeout : 20 +#auth-type : mschapv2 +#key configured : Yes +#-------------------------------------------------------------------------------------- +#HOST AUTH-TYPE KEY-CONFIG AUTH-PORT PRIORITY TIMEOUT RTSMT VRF SI +#-------------------------------------------------------------------------------------- +#1.2.3.4 mschapv2 Yes 1812 - - - - Ethernet12 +#10.10.11.12 chap No 49 2 30 - - - +# """ RETURN = """ before: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_route_maps.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_route_maps.py new file mode 100644 index 000000000..01327572e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_route_maps.py @@ -0,0 +1,1606 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_route_maps +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = """ +--- +module: sonic_route_maps +version_added: "2.1.0" +author: "Kerry Meyer (@kerry-meyer)" +short_description: route map configuration handling for SONiC +description: + - This module provides configuration management for route map parameters on devices running SONiC. +options: + config: + description: + - Specifies a list of route map configuration dictionaries + type: list + elements: dict + suboptions: + map_name: + description: + - Name of a route map + type: str + required: true + action: + description: + - action type for the route map (permit or deny) + - This value is required for creation and modification of a route + - map or route map attributes as well as for deletion of route map + - attributes. It can be omitted only when requesting deletion of a + - route map statement or all route map statements for a given route + - map map_name. + type: str + choices: + - permit + - deny + sequence_num: + description: + - unique number in the range 1-66535 to specify priority of the map + - This value is required for creation and modification of a route + - map or route map attributes as well as for deletion of route map + - attributes. It can be omitted only when requesting deletion of all + - route map "statements" for a given route map "map_name". + type: int + match: + description: Criteria for matching the route map to a route + type: dict + suboptions: + as_path: + description: + - Name of a configured BGP AS path list to be checked for + - a match with the target route + type: str + community: + description: + - Name of a configured BGP "community" to be checked for + - a match with the target route + type: str + evpn: + description: + - BGP Ethernet Virtual Private Network to be checked for + - a match with the target route + type: dict + suboptions: + default_route: + description: + - Default EVPN type-5 route + type: bool + route_type: + description: + - "Non-default route type: One of the following:" + - mac-ip route, EVPN Type 3 Inclusive Multicast Ethernet + - Tag (IMET) route, or prefix route + type: str + choices: + - macip + - multicast + - prefix + vni: + description: + - VNI ID to be checked for a match; specified by a value in the + - range 1-16777215 + type: int + ext_comm: + description: + - Name of a configured BGP 'extended community' to be checked for + - a match with the target route + type: str + interface: + description: + - Next hop interface name (type and number) to be checked for a + - match with the target route. The interface type can be any + - of the following; 'Ethernet/Eth' interface or sub-interface, + - "'Loopback' interface, 'PortChannel' interface or" + - "sub-interface, 'Vlan' interface." + type: str + ip: + description: + - IP addresses or IP next hops to be checked for a match with the + - target route + type: dict + suboptions: + address: + description: + - name of an IPv4 prefix list containing a list of address + - prefixes to be checked for a match with the target route + type: str + next_hop: + description: + - name of a prefix list containing a list of next-hop + - prefixes to be checked for a match with the target route + type: str + ipv6: + description: + - IPv6 addresses to be checked for a match with the + - target route + type: dict + suboptions: + address: + description: + - name of an IPv6 prefix list containing a list of address + - prefixes to be checked for a match with the target route + type: str + required: true + local_preference: + description: + - local-preference value to be checked for a match with the + - target route. This is a value in the range 0-4294967295. + type: int + metric: + description: + - metric value to be checked for a match with the target route. + - This is a value in the range 0-4294967295. + type: int + origin: + description: + - BGP origin to be checked for a match with the target route + type: str + choices: + - egp + - igp + - incomplete + peer: + description: + - BGP routing peer/neighbor required for a matching route. + - I(ip), I(ipv6), and I(interface) are mutually exclusive. + type: dict + suboptions: + ip: + description: IPv4 address of a BGP peer + type: str + ipv6: + description: IPv6 address of a BGP peer + type: str + interface: + description: + - Name (type and number) of a BGP peer interface. + - Allowed interface types are Ethernet or Eth (depending + - on the configured interface-naming mode), + - Vlan, and Portchannel + type: str + source_protocol: + description: Source protocol required for a matching route + type: str + choices: + - bgp + - connected + - ospf + - static + source_vrf: + description: Name of the source VRF required for a matching route + type: str + tag: + description: + - Tag value required for a matching route + - The value must be in the range 1-4294967295 + type: int + set: + description: Information to set into a matching route for re-distribution + type: dict + suboptions: + as_path_prepend: + description: + - String specifying a comma-separated list of AS-path numbers + - "to prepend to the BGP AS-path attribute in a matched route." + - AS-path values in the list must be in the range + - "1-4294967295; for example, 2000,3000" + type: str + comm_list_delete: + description: + - String specifying the name of a BGP community list containing + - BGP Community values to be deleted from matching routes. + type: str + community: + description: + - BGP community attributes to add to or replace the BGP + - community attributes in a matching route. Specifying the + - "'additive' attribute is allowed only if one of" + - the other attributes (other than 'none') is specified. + - It causes the specified 'set community' attributes + - to be added to the already existing community + - "attributes in the matching route. If the 'additive' attribute" + - is not specified, the previously existing community attributes + - in the matching route are replaced by the configured + - "'set community' attributes. Specifying a 'set community' attribute" + - of 'none' is mutually exclusive with setting of other community + - attributes and causes any community attributes in the matching + - route to be removed. + type: dict + suboptions: + community_number: + description: + - A list of one or more BGP community numbers in the + - "form AA:NN where AA and NN are integers in the range" + - "0-65535." + - "Note: Each community number in the list must be enclosed" + - in double quotes to avoid YAML parsing errors due to the + - "list values containing an embedded ':' character." + type: list + elements: str + community_attributes: + description: + - A list of one or more BGP community attributes. The allowed + - "values are the following:" + - local_as + - Do not send outside local AS (well-known community) + - no_advertise + - Do not advertise to any peer (well-known community) + - no_export + - Do not export to next AS (well-known community) + - no_peer + - "The route does not need to be advertised to peers." + - (Advertisement of the route can be suppressed based + - on other criteria.) + - additive + - Add the configured 'set community' attributes to + - "the matching route (if set to 'true'); Previously existing" + - attributes in the matching route are, instead, replaced + - by the configured attributes if this attribute is + - not specified or if it is set to 'false'. + - none + - Do not send any community attribute. This attribute + - is mutually exclusive with all other 'set community' + - attributes. It causes all attributes to be removed + - from the matching route. + - "I(none) is mutually exclusive with all of the other attributes:" + - I(local_as), I(no_advertise), I(no_export), I(no_peer), I(additive), + - and I(additive). + type: list + elements: str + choices: + - local_as + - no_advertise + - no_export + - no_peer + - additive + - none + extcommunity: + description: + - BGP extended community attributes to set into a matching route. + type: dict + suboptions: + rt: + description: + - Route Target VPN extended communities in the format + - "ASN:NN or IP-ADDRESS:NN" + - "Note: Each rt value in the list must be enclosed" + - in double quotes to avoid YAML parsing errors due to the + - "list values containing an embedded ':' character." + type: list + elements: str + soo: + description: + - "Site-of-Origin VPN extended communities in the format" + - "ASN:NN or IP-ADDRESS:NN" + - "Note: Each rt value in the list must be enclosed" + - in double quotes to avoid YAML parsing errors due to the + - "list values containing an embedded ':' character." + type: list + elements: str + ip_next_hop: + description: + - IPv4 next hop address to set into a matching route in the + - dotted decimal format A.B.C.D + type: str + ipv6_next_hop: + description: + - IPv6 next hop address attributes to set into a matching route + type: dict + suboptions: + global_addr: + description: + - IPv6 global next hop address to set into a matching + - "route in the format A::B" + type: str + prefer_global: + description: + - Set the corresponding attribute into a matching route + - if the value of this Ansible attribute is 'true'. + - The attribute indicates that the routing algorithm must + - prefer the global next-hop address over the link-local + - address if both exist. + type: bool + local_preference: + description: + - "BGP local preference path attribute; integer value in" + - the range 0-4294967295 + type: int + metric: + description: + - route metric value actions + - I(value) and I(rtt_action) are mutually exclusive. + type: dict + suboptions: + value: + description: + - "metric value to be set into a matching route;" + - value in the range 0-4294967295 + type: int + rtt_action: + description: + - Action to take for modifying the metric for a matched + - "route using the Round Trip Time (rtt);" + - C(set) causes the route metric to be set to the + - rtt value. + - C(add) causes the rtt value to be added + - to the route metric. + - C(subtract) causes the rtt value to be + - subtracted from route metric. + type: str + choices: + - set + - add + - subtract + origin: + description: + - "BGP route origin; One of the following must be selected." + - "egp (External; remote EGP)" + - "igp (Internal; local IGP)" + - incomplete (Unknown origin) + type: str + choices: + - egp + - igp + - incomplete + weight: + description: + - BGP weight for the routing table. The weight must be an + - integer in the range 0-4294967295 + type: int + call: + description: + - Name of a route map to jump to after executing 'match' and 'set' + - statements for the current route map. + type: str + + state: + description: + - Specifies the type of configuration update to be performed on the device. + - For C(merged), merge specified attributes with existing configured attributes. + - For C(deleted), delete the specified attributes from existing configuration. + - For C(replaced), replace each modified list or dictionary with the + - specified items. + - For C(overridden), replace all current configuration for this resource + - module with the specified configuration. + type: str + choices: + - merged + - deleted + - replaced + - overridden + default: merged +""" +EXAMPLES = """ +# Using "merged" state to create initial configuration +# +# Before state: +# ------------- +# +# sonic# show running-configuration route-map +# sonic# +# (No configuration present) +# +# ------------- +# +- name: Merge initial route_maps configuration + dellemc.enterprise_sonic.sonic_route_maps: + config: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + as_path: bgp_as1 + community: bgp_comm_list1 + evpn: + default_route: true + vni: 735 + ext_comm: bgp_ext_comm1 + interface: Ethernet4 + ip: + address: ip_pfx_list1 + ipv6: + address: ipv6_pfx_list1 + local_preference: 8000 + metric: 400 + origin: egp + peer: + ip: 10.20.30.40 + source_protocol: bgp + source_vrf: Vrf1 + tag: 7284 + set: + as_path_prepend: 200,315,7135 + comm_list_delete: bgp_comm_list2 + community: + community_number: + - "35:58" + - "79:150" + - "308:650" + community_attributes: + - local_as + - no_advertise + - no_export + - no_peer + - additive + extcommunity: + rt: + - "30:40" + soo: + - "10.73.14.9:78" + ip_next_hop: 10.48.16.18 + ipv6_next_hop: + global_addr: 30::30 + prefer_global: true + local_preference: 635 + metric: + metric_value: 870 + origin: egp + weight: 93471 + - map_name: rm1 + action: deny + sequence_num: 3047 + match: + evpn: + route_type: multicast + origin: incomplete + peer: + interface: Ethernet6 + source_protocol: ospf + set: + metric: + rtt_action: add + origin: incomplete + - map_name: rm3 + action: deny + sequence_num: 285 + match: + evpn: + route_type: macip + origin: igp + peer: + ipv6: 87:95:15::53 + source_protocol: connected + set: + community: + community_attributes: + - none + metric: + rtt_action: set + origin: igp + call: rm1 + - map_name: rm4 + action: permit + sequence_num: 480 + match: + evpn: + route_type: prefix + source_protocol: static + set: + metric: + rtt_action: subtract + state: merged + +# After state: +# ------------ +# +# sonic# show running-configuration route-map +# ! +# route-map rm1 permit 80 +# match as-path bgp_as1 +# match evpn default-route +# match evpn vni 735 +# match ip address prefix-list ip_pfx_list1 +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Ethernet4 +# match community bgp_comm_list1 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match peer 10.20.30.40 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set as-path prepend 200,315,7135 +# set community 35:58 79:150 308:650 local-AS no-advertise no-export no-peer additive +# set extcommunity rt 30:40 +# set extcommunity soo 10.73.14.9:78 +# set comm-list bgp_comm_list2 delete +# set metric 870 +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set ipv6 next-hop prefer-global +# set local-preference 635 +# set origin egp +# set weight 93471 +# ! +# route-map rm1 deny 3047 +# match evpn route-type multicast +# match peer Ethernet6 +# match source-protocol ospf +# match origin incomplete +# set metric +rtt +# set origin incomplete +# ! +# route-map rm3 deny 285 +# match evpn route-type macip +# call rm1 +# match peer 87:95:15::53 +# match source-protocol connected +# match origin igp +# set community none +# set metric rtt +# set origin igp +# ! +# route-map rm4 permit 480 +# match evpn route-type prefix +# match source-protocol static +# set metric -rtt +# ------------ + + +# Using "merged" state to update and add configuration +# +# Before state: +# ------------ +# +# sonic# show running-configuration route-map +# ! +# route-map rm1 permit 80 +# match as-path bgp_as1 +# match evpn default-route +# match evpn vni 735 +# match ip address prefix-list ip_pfx_list1 +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Ethernet4 +# match community bgp_comm_list1 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match peer 10.20.30.40 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set as-path prepend 200,315,7135 +# set community 35:58 79:150 308:650 local-AS no-advertise no-export no-peer additive +# set extcommunity rt 30:40 +# set extcommunity soo 10.73.14.9:78 +# set comm-list bgp_comm_list2 delete +# set metric 870 +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set ipv6 next-hop prefer-global +# set local-preference 635 +# set origin egp +# set weight 93471 +# ! +# route-map rm1 deny 3047 +# match evpn route-type multicast +# match peer Ethernet6 +# match source-protocol ospf +# match origin incomplete +# set metric +rtt +# set origin incomplete +# ! +# route-map rm3 deny 285 +# match evpn route-type macip +# call rm1 +# match peer 87:95:15::53 +# match source-protocol connected +# match origin igp +# set community none +# set metric rtt +# set origin igp +# ! +# route-map rm4 permit 480 +# match evpn route-type prefix +# match source-protocol static +# set metric -rtt +# ------------ +# +- name: Merge additional and modified route map configuration + dellemc.enterprise_sonic.sonic_route_maps: + config: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + as_path: bgp_as2 + community: bgp_comm_list3 + evpn: + route_type: prefix + vni: 850 + interface: Vlan7 + ip: + address: ip_pfx_list2 + next_hop: ip_pfx_list3 + peer: + interface: Portchannel14 + set: + as_path_prepend: 188,257 + community: + community_number: + - "45:736" + ipv6_next_hop: + prefer_global: false + metric: + rtt_action: add + - map_name: rm1 + action: deny + sequence_num: 3047 + match: + as_path: bgp_as3 + ext_comm: bgp_ext_comm2 + origin: igp + set: + metric: + rtt_action: subtract + - map_name: rm2 + action: permit + sequence_num: 100 + match: + interface: Ethernet16 + set: + as_path_prepend: 200,300,400 + ipv6_next_hop: + global_addr: 37::58 + prefer_global: true + metric: 8000 + - map_name: rm3 + action: deny + sequence_num: 285 + match: + local_preference: 14783 + source_protocol: bgp + set: + community: + community_attributes: + - no_advertise + state: merged + +# After state: +# ------------ +# +# sonic# show running-configuration route-map +# ! +# route-map rm1 permit 80 +# match as-path bgp_as2 +# match evpn default-route +# match evpn route-type prefix +# match evpn vni 850 +# match ip address prefix-list ip_pfx_list2 +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Vlan7 +# match community bgp_comm_list3 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match ip next-hop prefix-list ip_pfx_list3 +# match peer PortChannel 14 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set as-path prepend 188,257 +# set community 35:58 79:150 308:650 45:736 local-AS no-advertise no-export no-peer additive +# set extcommunity rt 30:40 +# set extcommunity soo 10.73.14.9:78 +# set comm-list bgp_comm_list2 delete +# set metric +rtt +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set local-preference 635 +# set origin egp +# set weight 93471 +# ! +# route-map rm1 deny 3047 +# match as-path bgp_as3 +# match evpn route-type multicast +# match ext-community bgp_ext_comm2 +# match peer Ethernet6 +# match source-protocol ospf +# match origin igp +# set metric -rtt +# set origin incomplete +# ! +# route-map rm2 permit 100 +# match interface Ethernet16 +# set as-path prepend 200,300,400 +# set ipv6 next-hop global 37::58 +# set ipv6 next-hop prefer-global +# set metric 8000 +# ! +# route-map rm3 deny 285 +# match evpn route-type macip +# match local-preference 14783 +# call rm1 +# match peer 87:95:15::53 +# match source-protocol bgp +# match origin igp +# set community no-advertise +# set metric rtt +# set origin igp +# ! +# route-map rm4 permit 480 +# match evpn route-type prefix +# match source-protocol static +# set metric -rtt + + +# Using "replaced" state to replace the contents of a list +# +# Before state: +# ------------ +# +# sonic(config-route-map)# do show running-configuration route-map rm1 80 +# ! +# route-map rm1 permit 80 +# match as-path bgp_as2 +# match evpn default-route +# match evpn route-type prefix +# match evpn vni 850 +# match ip address prefix-list ip_pfx_list2 +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Vlan7 +# match community bgp_comm_list3 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match ip next-hop prefix-list ip_pfx_list3 +# match peer PortChannel 14 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set as-path prepend 188,257 +# set community 35:58 79:150 308:650 45:736 local-AS no-export no-peer additive +# set extcommunity rt 30:40 +# set extcommunity soo 10.73.14.9:78 +# set comm-list bgp_comm_list2 delete +# set metric +rtt +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set local-preference 635 +# set origin egp +# set weight 93471 +# ------------ +- name: Replace a list + dellemc.enterprise_sonic.sonic_route_maps: + config: + - map_name: rm1 + action: permit + sequence_num: 80 + set: + community: + community_number: + - "15:30" + - "26:54" + state: replaced + +# After state: +# ------------ +# +# sonic#show running-configuration route-map rm1 80 +# ! +# route-map rm1 permit 80 +# match as-path bgp_as2 +# match evpn default-route +# match evpn route-type prefix +# match evpn vni 850 +# match ip address prefix-list ip_pfx_list2 +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Vlan7 +# match community bgp_comm_list3 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match ip next-hop prefix-list ip_pfx_list3 +# match peer PortChannel 14 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set as-path prepend 188,257 +# set community 15:30 26:54 local-AS no-export no-peer additive +# set extcommunity rt 30:40 +# set extcommunity soo 10.73.14.9:78 +# set comm-list bgp_comm_list2 delete +# set metric +rtt +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set local-preference 635 +# set origin egp +# set weight 93471 + + +# Using "replaced" state to replace the contents of dictionaries +# +# Before state: +# ------------ +# sonic# show running-configuration route-map +# ! +# route-map rm1 permit 80 +# match as-path bgp_as2 +# match evpn default-route +# match evpn route-type prefix +# match evpn vni 850 +# match ip address prefix-list ip_pfx_list2 +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Vlan7 +# match community bgp_comm_list3 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match ip next-hop prefix-list ip_pfx_list3 +# match peer PortChannel 14 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set as-path prepend 188,257 +# set community 15:30 26:54 local-AS no-export no-peer additive +# set extcommunity rt 30:40 +# set extcommunity soo 10.73.14.9:78 +# set comm-list bgp_comm_list2 delete +# set metric +rtt +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set local-preference 635 +# set origin egp +# set weight 93471 +# ! +# route-map rm1 deny 3047 +# match as-path bgp_as3 +# match evpn route-type multicast +# match ext-community bgp_ext_comm2 +# match peer Ethernet6 +# match source-protocol ospf +# match origin igp +# set metric -rtt +# set origin incomplete +# ! +# route-map rm2 permit 100 +# match interface Ethernet16 +# set as-path prepend 200,300,400 +# set ipv6 next-hop global 37::58 +# set ipv6 next-hop prefer-global +# set metric 8000 +# ! +# route-map rm3 deny 285 +# match evpn route-type macip +# match local-preference 14783 +# call rm1 +# match peer 87:95:15::53 +# match source-protocol bgp +# match origin igp +# set community no-advertise +# set metric rtt +# set origin igp +# ! +# route-map rm4 permit 480 +# match evpn route-type prefix +# match source-protocol static +# set metric -rtt +# ------------ +- name: Replace dictionaries + dellemc.enterprise_sonic.sonic_route_maps: + config: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + evpn: + route_type: multicast + ip: + address: ip_pfx_list1 + set: + community: + community_attributes: + - no_advertise + extcommunity: + rt: + - "20:20" + + - map_name: rm2 + action: permit + sequence_num: 100 + set: + ipv6_next_hop: + global_addr: 45::90 + state: replaced + +# After state: +# ------------ +# +# sonic# show running-configuration route-map +# ! +# route-map rm1 permit 80 +# match as-path bgp_as2 +# match evpn route-type multicast +# match ip address prefix-list ip_pfx_list1 +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Vlan7 +# match community bgp_comm_list3 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match peer PortChannel 14 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set as-path prepend 188,257 +# set community no-advertise +# set extcommunity rt 20:20 +# set comm-list bgp_comm_list2 delete +# set metric +rtt +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set local-preference 635 +# set origin egp +# set weight 93471 +# ! +# route-map rm1 deny 3047 +# match as-path bgp_as3 +# match evpn route-type multicast +# match ext-community bgp_ext_comm2 +# match peer Ethernet6 +# match source-protocol ospf +# match origin igp +# set metric -rtt +# set origin incomplete +# ! +# route-map rm2 permit 100 +# match interface Ethernet16 +# set as-path prepend 200,300,400 +# set metric 8000 +# set ipv6 next-hop global 45::90 +# ! +# route-map rm3 deny 285 +# match evpn route-type macip +# match local-preference 14783 +# call rm1 +# match peer 87:95:15::53 +# match source-protocol bgp +# match origin igp +# set community no-advertise +# set metric rtt +# set origin igp +# ! +# route-map rm4 permit 480 +# match evpn route-type prefix +# match source-protocol static +# set metric -rtt + + +# Using "overridden" state to override all existing configuration with new +# configuration +# +# Before state: +# ------------ +# +# sonic# show running-configuration route-map +# ! +# route-map rm1 permit 80 +# match as-path bgp_as2 +# match evpn route-type multicast +# match ip address prefix-list ip_pfx_list1 +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Vlan7 +# match community bgp_comm_list3 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match peer PortChannel 14 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set as-path prepend 188,257 +# set community no-advertise +# set extcommunity rt 30:40 +# set extcommunity rt 20:20 +# set comm-list bgp_comm_list2 delete +# set metric +rtt +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set local-preference 635 +# set origin egp +# set weight 93471 +# ! +# route-map rm1 deny 3047 +# match as-path bgp_as3 +# match evpn route-type multicast +# match ext-community bgp_ext_comm2 +# match peer Ethernet6 +# match source-protocol ospf +# match origin igp +# set metric -rtt +# set origin incomplete +# ! +# route-map rm2 permit 100 +# match interface Ethernet16 +# set as-path prepend 200,300,400 +# set metric 8000 +# set ipv6 next-hop global 45::90 +# ! +# route-map rm3 deny 285 +# match evpn route-type macip +# match local-preference 14783 +# call rm1 +# match peer 87:95:15::53 +# match source-protocol bgp +# match origin igp +# set community no-advertise +# set metric rtt +# set origin igp +# ! +# route-map rm4 permit 480 +# match evpn route-type prefix +# match source-protocol static +# set metric -rtt +# ------------ +- name: Override all route map configuration with new configuration + dellemc.enterprise_sonic.sonic_route_maps: + config: + - map_name: rm5 + action: permit + sequence_num: 250 + match: + interface: Ethernet28 + set: + as_path_prepend: 150,275 + metric: 7249 + state: overridden + +# After state: +# ------------ +# +# sonic# show running-configuration route-map +# ! +# route-map rm5 permit 250 +# match interface Ethernet28 +# set as-path prepend 150,275 +# set metric 7249 + + +# Using "overridden" state to override all existing configuration with new +# configuration. (Restore previous configuration.) +# +# Before state: +# ------------ +# +# sonic# show running-configuration route-map +# ! +# route-map rm5 permit 250 +# match interface Ethernet28 +# set as-path prepend 150,275 +# set metric 7249 +# ------------ +- name: Override (restore) all route map configuration with older configuration + dellemc.enterprise_sonic.sonic_route_maps: + config: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + as_path: bgp_as2 + community: bgp_comm_list3 + evpn: + default_route: true + route_type: prefix + vni: 850 + ext_comm: bgp_ext_comm1 + interface: Vlan7 + ip: + address: ip_pfx_list2 + next_hop: ip_pfx_list3 + ipv6: + address: ipv6_pfx_list1 + local_preference: 8000 + metric: 400 + origin: egp + peer: + interface: Portchannel14 + source_protocol: bgp + source_vrf: Vrf1 + tag: 7284 + set: + as_path_prepend: 188,257 + comm_list_delete: bgp_comm_list2 + community: + community_number: + - "35:58" + - "79:150" + - "308:650" + - "45:736" + community_attributes: + - local_as + - no_export + - no_peer + - additive + extcommunity: + rt: + - "30:40" + soo: + - "10.73.14.9:78" + ip_next_hop: 10.48.16.18 + ipv6_next_hop: + global_addr: 30::30 + local_preference: 635 + metric: + rtt_action: add + origin: egp + weight: 93471 + - map_name: rm1 + action: deny + sequence_num: 3047 + match: + as_path: bgp_as3 + evpn: + route_type: multicast + ext_comm: bgp_ext_comm2 + origin: igp + peer: + interface: Ethernet6 + source_protocol: ospf + set: + metric: + rtt_action: subtract + origin: incomplete + - map_name: rm2 + action: permit + sequence_num: 100 + match: + interface: Ethernet16 + set: + as_path_prepend: 200,300,400 + ipv6_next_hop: + global_addr: 37::58 + prefer_global: true + metric: 8000 + - map_name: rm3 + action: deny + sequence_num: 285 + match: + evpn: + route_type: macip + origin: igp + peer: + ipv6: 87:95:15::53 + local_preference: 14783 + source_protocol: bgp + set: + community: + community_attributes: + - no_advertise + metric: + rtt_action: set + origin: igp + call: rm1 + - map_name: rm4 + action: permit + sequence_num: 480 + match: + evpn: + route_type: prefix + source_protocol: static + set: + metric: + rtt_action: subtract + state: overridden + +# After state: +# ------------ +# +# sonic# show running-configuration route-map +# ! +# route-map rm1 permit 80 +# match as-path bgp_as2 +# match evpn default-route +# match evpn route-type prefix +# match evpn vni 850 +# match ip address prefix-list ip_pfx_list2 +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Vlan7 +# match community bgp_comm_list3 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match ip next-hop prefix-list ip_pfx_list3 +# match peer PortChannel 14 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set as-path prepend 188,257 +# set community 35:58 79:150 308:650 45:736 local-AS no-export no-peer additive +# set extcommunity rt 30:40 +# set extcommunity soo 10.73.14.9:78 +# set comm-list bgp_comm_list2 delete +# set metric +rtt +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set local-preference 635 +# set origin egp +# set weight 93471 +# ! +# route-map rm1 deny 3047 +# match as-path bgp_as3 +# match evpn route-type multicast +# match ext-community bgp_ext_comm2 +# match peer Ethernet6 +# match source-protocol ospf +# match origin igp +# set metric -rtt +# set origin incomplete +# ! +# route-map rm2 permit 100 +# match interface Ethernet16 +# set as-path prepend 200,300,400 +# set ipv6 next-hop global 37::58 +# set ipv6 next-hop prefer-global +# set metric 8000 +# ! +# route-map rm3 deny 285 +# match evpn route-type macip +# match local-preference 14783 +# call rm1 +# match peer 87:95:15::53 +# match source-protocol bgp +# match origin igp +# set community no-advertise +# set metric rtt +# set origin igp +# ! +# route-map rm4 permit 480 +# match evpn route-type prefix +# match source-protocol static +# set metric -rtt + + +# Using "deleted" state to remove configuration +# +# Before state: +# ------------ +# +# sonic# show running-configuration route-map rm1 80 +# ! +# route-map rm1 permit 80 +# match as-path bgp_as2 +# match evpn default-route +# match evpn route-type prefix +# match evpn vni 850 +# match ip address prefix-list ip_pfx_list2 +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Vlan7 +# match community bgp_comm_list3 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match ip next-hop prefix-list ip_pfx_list3 +# match peer PortChannel 14 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set as-path prepend 188,257 +# set community 35:58 79:150 308:650 45:736 local-AS no-export no-peer additive +# set extcommunity rt 30:40 +# set extcommunity soo 10.73.14.9:78 +# set comm-list bgp_comm_list2 delete +# set metric +rtt +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set local-preference 635 +# set origin egp +# set weight 93471 +# ------------ +- name: Delete selected route map configuration + dellemc.enterprise_sonic.sonic_route_maps: + config: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + as_path: bgp_as2 + community: bgp_comm_list3 + evpn: + vni: 850 + ip: + address: ip_pfx_list2 + set: + as_path_prepend: 188,257 + community: + community_number: + - "35:58" + community_attributes: + - local_as + extcommunity: + rt: + - "30:40" + state: deleted + +# After state: +# ------------ +# +# sonic# show running-configuration route-map rm1 80 +# ! +# route-map rm1 permit 80 +# match evpn default-route +# match evpn route-type prefix +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Vlan7 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match ip next-hop prefix-list ip_pfx_list3 +# match peer PortChannel 14 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set community 79:150 308:650 45:736 no-export no-peer additive +# set extcommunity soo 10.73.14.9:78 +# set comm-list bgp_comm_list2 delete +# set metric +rtt +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set local-preference 635 +# set origin egp +# set weight 93471 + + +# Using "deleted" state to remove a route map or route map subset +# +# Before state: +# ------------ +# +# sonic# show running-configuration route-map +# ! +# route-map rm1 permit 80 +# match evpn default-route +# match evpn route-type prefix +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Vlan7 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match ip next-hop prefix-list ip_pfx_list3 +# match peer PortChannel 14 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set community 79:150 308:650 45:736 no-export no-peer additive +# set extcommunity soo 10.73.14.9:78 +# set comm-list bgp_comm_list2 delete +# set metric +rtt +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set local-preference 635 +# set origin egp +# set weight 93471 +# ! +# route-map rm1 deny 3047 +# match as-path bgp_as3 +# match evpn route-type multicast +# match ext-community bgp_ext_comm2 +# match peer Ethernet6 +# match source-protocol ospf +# match origin igp +# set metric -rtt +# set origin incomplete +# ! +# route-map rm2 permit 100 +# match interface Ethernet16 +# set as-path prepend 200,300,400 +# set metric 8000 +# set ipv6 next-hop prefer-global +# set ipv6 next-hop global 37::58 +# ! +# route-map rm3 deny 285 +# match evpn route-type macip +# match local-preference 14783 +# call rm1 +# match peer 87:95:15::53 +# match source-protocol bgp +# match origin igp +# set community no-advertise +# set metric rtt +# set origin igp +# ! +# route-map rm4 permit 480 +# match evpn route-type prefix +# match source-protocol static +# set metric -rtt +# ------------ +- name: Delete a route map or route map subset + dellemc.enterprise_sonic.sonic_route_maps: + config: + - map_name: rm1 + sequence_num: 3047 + - map_name: rm2 + sequence_num: 100 + state: deleted + +# After state: +# ------------ +# +# sonic# show running-configuration route-map +# ! +# route-map rm1 permit 80 +# match evpn default-route +# match evpn route-type prefix +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Vlan7 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match ip next-hop prefix-list ip_pfx_list3 +# match peer PortChannel 14 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set community 79:150 308:650 45:736 no-export no-peer additive +# set extcommunity soo 10.73.14.9:78 +# set comm-list bgp_comm_list2 delete +# set metric +rtt +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set local-preference 635 +# set origin egp +# set weight 93471 +# ! +# route-map rm3 deny 285 +# match evpn route-type macip +# match local-preference 14783 +# call rm1 +# match peer 87:95:15::53 +# match source-protocol bgp +# match origin igp +# set community no-advertise +# set metric rtt +# set origin igp +# ! +# route-map rm4 permit 480 +# match evpn route-type prefix +# match source-protocol static +# set metric -rtt + + +# Using "deleted" state to remove all route map configuration +# +# Before state: +# ------------ +# +# sonic# show running-configuration route-map +# ! +# route-map rm1 permit 80 +# match evpn default-route +# match evpn route-type prefix +# match ipv6 address prefix-list ipv6_pfx_list1 +# match interface Vlan7 +# match ext-community bgp_ext_comm1 +# match tag 7284 +# match local-preference 8000 +# match source-vrf Vrf1 +# match ip next-hop prefix-list ip_pfx_list3 +# match peer PortChannel 14 +# match source-protocol bgp +# match metric 400 +# match origin egp +# set community 79:150 308:650 45:736 no-export no-peer additive +# set extcommunity soo 10.73.14.9:78 +# set comm-list bgp_comm_list2 delete +# set metric +rtt +# set ip next-hop 10.48.16.18 +# set ipv6 next-hop global 30::30 +# set local-preference 635 +# set origin egp +# set weight 93471 +# ! +# route-map rm3 deny 285 +# match evpn route-type macip +# match local-preference 14783 +# call rm1 +# match peer 87:95:15::53 +# match source-protocol bgp +# match origin igp +# set community no-advertise +# set metric rtt +# set origin igp +# ! +# route-map rm4 permit 480 +# match evpn route-type prefix +# match source-protocol static +# set metric -rtt +# ------------ +- name: Delete all route map configuration + dellemc.enterprise_sonic.sonic_route_maps: + config: [] + state: deleted + +# After state: +# ------------ +# +# sonic# show running-configuration route-map +# sonic# +# (no route map configuration present) + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + as the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + as the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.route_maps.route_maps import Route_mapsArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.route_maps.route_maps import Route_maps + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=Route_mapsArgs.argument_spec, + supports_check_mode=True) + + result = Route_maps(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_static_routes.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_static_routes.py index 7a528cdf0..b6f8be3b7 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_static_routes.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_static_routes.py @@ -33,6 +33,8 @@ DOCUMENTATION = """ --- module: sonic_static_routes version_added: 2.0.0 +notes: + - Supports C(check_mode). short_description: Manage static routes configuration on SONiC description: - This module provides configuration management of static routes for devices running SONiC @@ -108,6 +110,8 @@ options: choices: - merged - deleted + - overridden + - replaced default: merged """ EXAMPLES = """ @@ -137,13 +141,13 @@ EXAMPLES = """ metric: 2 tag: 4 track: 8 - - vrf_name: '{{vrf_1}}' + - vrf_name: 'VrfReg1' static_list: - prefix: '3.0.0.0/8' next_hops: - index: interface: 'eth0' - nexthop_vrf: '{{vrf_2}}' + nexthop_vrf: 'VrfReg2' next_hop: '4.0.0.0' metric: 4 tag: 5 @@ -162,7 +166,7 @@ EXAMPLES = """ # ip route 2.0.0.0/8 3.0.0.0 tag 4 track 8 2 # ip route 2.0.0.0/8 interface Ethernet4 tag 2 track 3 1 # ip route vrf VrfReg1 3.0.0.0/8 4.0.0.0 interface Management 0 nexthop-vrf VrfReg2 tag 5 track 6 4 -# ip route vrf VrfREg1 3.0.0.0/8 blackhole tag 20 track 30 10 +# ip route vrf VrfReg1 3.0.0.0/8 blackhole tag 20 track 30 10 # # # Modifying previous merge @@ -170,7 +174,7 @@ EXAMPLES = """ - name: Modify static routes configurations dellemc.enterprise_sonic.sonic_static_routes: config: - - vrf_name: '{{vrf_1}}' + - vrf_name: 'VrfReg1' static_list: - prefix: '3.0.0.0/8' next_hops: @@ -188,7 +192,65 @@ EXAMPLES = """ # ip route 2.0.0.0/8 3.0.0.0 tag 4 track 8 2 # ip route 2.0.0.0/8 interface Ethernet4 tag 2 track 3 1 # ip route vrf VrfReg1 3.0.0.0/8 4.0.0.0 interface Management 0 nexthop-vrf VrfReg2 tag 5 track 6 4 -# ip route vrf VrfREg1 3.0.0.0/8 blackhole tag 22 track 33 11 +# ip route vrf VrfReg1 3.0.0.0/8 blackhole tag 22 track 33 11 + + +# Using overridden +# +# Before State: +# ------------- +# +# sonic# show running-configuration | grep "ip route" +# ip route 4.0.0.0/8 2.0.0.0 tag 4 track 8 2 + + - name: Override static routes configurations + dellemc.enterprise_sonic.sonic_static_routes: + config: + - vrf_name: 'VrfReg2' + static_list: + - prefix: '3.0.0.0/8' + next_hops: + - index: + blackhole: True + metric: 10 + tag: 20 + track: 30 + state: overridden + +# After State: +# ------------ +# +# sonic# show running-configuration | grep "ip route" +# ip route vrf VrfReg2 3.0.0.0/8 blackhole tag 20 track 30 10 + + +# Using Replaced +# +# Before State: +# ------------- +# +# sonic# show running-configuration | grep "ip route" +# ip route 4.0.0.0/8 2.0.0.0 tag 4 track 8 2 + + - name: Replace static routes configurations + dellemc.enterprise_sonic.sonic_static_routes: + config: + - vrf_name: 'default' + static_list: + - prefix: '4.0.0.0/8' + next_hops: + - index: + blackhole: True + metric: 5 + tag: 10 + track: 15 + state: replaced + +# After State: +# ------------ +# +# sonic# show running-configuration | grep "ip route" +# ip route 4.0.0.0/8 blackhole tag 10 track 15 5 # Using deleted @@ -200,7 +262,7 @@ EXAMPLES = """ # ip route 2.0.0.0/8 3.0.0.0 tag 4 track 8 2 # ip route 2.0.0.0/8 interface Ethernet4 tag 2 track 3 1 # ip route vrf VrfReg1 3.0.0.0/8 4.0.0.0 interface Management 0 nexthop-vrf VrfReg2 tag 5 track 6 4 -# ip route vrf VrfREg1 3.0.0.0/8 blackhole tag 22 track 33 11 +# ip route vrf VrfReg1 3.0.0.0/8 blackhole tag 22 track 33 11 - name: Delete static routes configurations dellemc.enterprise_sonic.sonic_static_routes: @@ -211,7 +273,7 @@ EXAMPLES = """ next_hops: - index: interface: 'Ethernet4' - - vrf_name: '{{vrf_1}}' + - vrf_name: 'VrfReg1' state: deleted # After State: @@ -237,6 +299,13 @@ after: sample: > The configuration returned will always be in the same format of the parameters above. +after(generated): + description: The generated configuration model invocation. + returned: when C(check_mode) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_stp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_stp.py new file mode 100644 index 000000000..a25252547 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_stp.py @@ -0,0 +1,677 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_stp +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = """ +--- +module: sonic_stp +version_added: "2.3.0" +short_description: Manage STP configuration on SONiC +description: + - This module provides configuration management of STP for devices running SONiC +author: "Shade Talabi (@stalabi1)" +options: + config: + description: + - Specifies STP configurations + - I(mstp), I(pvst) and I(rapid_pvst) are mutually exclusive. + type: dict + suboptions: + global: + description: + - Global STP configuration + type: dict + suboptions: + enabled_protocol: + description: + - Specifies the type of STP enabled on the device + type: str + choices: ['mst', 'pvst', 'rapid_pvst'] + loop_guard: + description: + - The loop guard default setting for the bridge + type: bool + default: False + bpdu_filter: + description: + - Enables edge port BPDU filter + type: bool + default: False + disabled_vlans: + description: + - List of disabled STP VLANs. The value of a list item can be a single VLAN ID or a range of VLAN IDs + - separated by '-' or '..'; for example 70-100 or 70..100. + type: list + elements: str + root_guard_timeout: + description: + - Specifies root guard recovery timeout in seconds before the port is moved back to forwarding state + - Range 5-600 + type: int + portfast: + description: + - Enables PortFast globally on all access ports + - Configurable for pvst protocol + type: bool + default: False + hello_time: + description: + - Interval in seconds between periodic transmissions of configuration messages by designated ports + - Range 1-10 + type: int + default: 2 + max_age: + description: + - Maximum age in seconds of the information transmitted by the bridge when it is the root bridge + - Range 6-40 + type: int + default: 20 + fwd_delay: + description: + - Delay in seconds used by STP bridges to transition root and designated ports to forwarding + - Range 4-30 + type: int + default: 15 + bridge_priority: + description: + - The manageable component of the bridge identifier + - Value must be a multiple of 4096 in the range of 0-61440 + type: int + default: 32768 + interfaces: + description: + - Interfaces STP configuration + type: list + elements: dict + suboptions: + intf_name: + description: + - Name of interface + type: str + required: True + edge_port: + description: + - Configure interface as an STP edge port + type: bool + default: False + link_type: + description: + - Specifies the interface's link type + type: str + choices: ['point-to-point', 'shared'] + guard: + description: + - Enables root guard or loop guard + type: str + choices: ['loop', 'root', 'none'] + bpdu_guard: + description: + - Enable edge port BPDU guard + type: bool + default: False + bpdu_filter: + description: + - Enables edge port BPDU filter + type: bool + default: False + portfast: + description: + - Enable/Disable portfast on specified interface + - Configurable for pvst protocol + type: bool + default: False + uplink_fast: + description: + - Enables uplink fast + type: bool + default: False + shutdown: + description: + - Port to be shutdown when it receives a BPDU + type: bool + default: False + cost: + description: + - The port's contribution, when it is the root port, to the root path cost for the bridge + type: int + port_priority: + description: + - The manageable component of the port identifier + - Range 0-240 + type: int + stp_enable: + description: + - Enables STP on the interface + type: bool + default: True + mstp: + description: + - Multi STP configuration + type: dict + suboptions: + mst_name: + description: + - Name of the MST configuration identifier + type: str + revision: + description: + - Revision level of the MST configuration identifier + type: int + max_hop: + description: + - Number of bridges in an MST region that a BPDU can traverse before it is discarded + type: int + hello_time: + description: + - Interval in seconds between periodic transmissions of configuration messages by designated ports + - Range 1-10 + type: int + max_age: + description: + - Maximum age in seconds of the information transmitted by the bridge when it is the root bridge + - Range 6-40 + type: int + fwd_delay: + description: + - Delay in seconds used by STP bridges to transition root and designated ports to forwarding + - Range 4-30 + type: int + mst_instances: + description: + - Configuration for MST instances + type: list + elements: dict + suboptions: + mst_id: + description: + - Value used to identify MST instance + type: int + required: True + bridge_priority: + description: + - The manageable component of the bridge identifier + - Value must be a multiple of 4096 + type: int + vlans: + description: + - List of VLANs mapped to the MST instance. The value of a list item can be a single VLAN ID or a range of VLAN IDs + - separated by '-' or '..'; for example 70-100 or 70..100. + type: list + elements: str + interfaces: + description: + - List of STP enabled interfaces + type: list + elements: dict + suboptions: + intf_name: + description: + - Reference to the STP interface + type: str + required: True + cost: + description: + - The port's contribution, when it is the root port, to the root path cost for the bridge + type: int + port_priority: + description: + - The manageable component of the port identifier + type: int + pvst: + description: + - Per VLAN STP configuration + type: list + elements: dict + suboptions: + vlan_id: + description: + - VLAN identifier + type: int + required: True + hello_time: + description: + - Interval in seconds between periodic transmissions of configuration messages by designated ports + - Range 1-10 + type: int + max_age: + description: + - Maximum age in seconds of the information transmitted by the bridge when it is the root bridge + - Range 6-40 + type: int + fwd_delay: + description: + - Delay in seconds used by STP bridges to transition root and designated ports to forwarding + - Range 4-30 + type: int + bridge_priority: + description: + - The manageable component of the bridge identifier + - Value must be a multiple of 4096 + type: int + interfaces: + description: + - List of STP enabled interfaces + type: list + elements: dict + suboptions: + intf_name: + description: + - Reference to the STP interface + type: str + required: True + cost: + description: + - The port's contribution, when it is the root port, to the root path cost for the bridge + type: int + port_priority: + description: + - The manageable component of the port identifier + type: int + rapid_pvst: + description: + - Rapid per VLAN STP configuration + type: list + elements: dict + suboptions: + vlan_id: + description: + - VLAN identifier + type: int + required: True + hello_time: + description: + - Interval in seconds between periodic transmissions of configuration messages by designated ports + - Range 1-10 + type: int + max_age: + description: + - Maximum age in seconds of the information transmitted by the bridge when it is the root bridge + - Range 6-40 + type: int + fwd_delay: + description: + - Delay in seconds used by STP bridges to transition root and designated ports to forwarding + - Range 4-30 + type: int + bridge_priority: + description: + - The manageable component of the bridge identifier + - Value must be a multiple of 4096 + type: int + interfaces: + description: + - List of STP enabled interfaces + type: list + elements: dict + suboptions: + intf_name: + description: + - Reference to the STP interface + type: str + required: True + cost: + description: + - The port's contribution, when it is the root port, to the root path cost for the bridge + type: int + port_priority: + description: + - The manageable component of the port identifier + type: int + state: + description: + - The state of the configuration after module completion + type: str + choices: ['merged', 'deleted', 'replaced', 'overridden'] + default: merged +""" +EXAMPLES = """ + +# Using merged +# +# Before State: +# ------------- +# +# sonic# show running-configuration spanning-tree +# (No spanning-tree configuration present) + +- name: Merge STP configurations + dellemc.enterprise_sonic.sonic_stp: + config: + global: + enabled_protocol: mst + loop_guard: true + bpdu_filter: true + disabled_vlans: + - 4-6 + hello_time: 5 + max_age: 10 + fwd_delay: 20 + bridge_priority: 4096 + interfaces: + - intf_name: Ethernet20 + edge_port: true + link_type: shared + guard: loop + bpdu_guard: true + bpdu_filter: true + uplink_fast: true + shutdown: true + cost: 20 + port_priority: 30 + stp_enable: true + mstp: + mst_name: mst1 + revision: 1 + max_hop: 3 + hello_time: 6 + max_age: 9 + fwd_delay: 12 + mst_instances: + - mst_id: 1 + bridge_priority: 2048 + vlans: + - 1 + interfaces: + - intf_name: Ethernet20 + cost: 60 + port_priority: 65 + state: merged + +# After State: +# ------------ +# +# sonic# show running-configuration spanning-tree +# no spanning-tree vlan 4-6 +# spanning-tree mode mst +# spanning-tree edge-port bpdufilter default +# spanning-tree forward-time 20 +# spanning-tree hello-time 5 +# spanning-tree max-age 10 +# spanning-tree loopguard default +# spanning-tree mst hello-time 6 +# spanning-tree mst forward-time 12 +# spanning-tree mst max-age 9 +# spanning-tree mst max-hops 3 +# spanning-tree mst 1 priority 2048 +# ! +# spanning-tree mst configuration +# name mst1 +# revision 1 +# instance 1 vlan 1 +# activate +# ! +# interface Ethernet20 +# spanning-tree bpdufilter enable +# spanning-tree guard loop +# spanning-tree bpduguard port-shutdown +# spanning-tree cost 20 +# spanning-tree link-type shared +# spanning-tree port-priority 30 +# spanning-tree port type edge +# spanning-tree uplinkfast +# spanning-tree mst 1 cost 60 +# spanning-tree mst 1 port-priority 65 + + +# Using replaced +# +# Before State: +# ------------- +# +# sonic# show running-configuration spanning-tree +# no spanning-tree vlan 4-6 +# spanning-tree mode mst +# spanning-tree edge-port bpdufilter default +# spanning-tree loopguard default +# spanning-tree mst hello-time 6 +# spanning-tree mst forward-time 12 +# spanning-tree mst max-age 9 +# spanning-tree mst max-hops 3 +# spanning-tree mst 1 priority 2048 +# ! +# spanning-tree mst configuration +# name mst1 +# revision 1 +# instance 1 vlan 1 +# activate +# ! +# interface Ethernet20 +# spanning-tree bpdufilter enable +# spanning-tree guard loop +# spanning-tree bpduguard port-shutdown +# spanning-tree cost 20 +# spanning-tree link-type shared +# spanning-tree port-priority 30 +# spanning-tree port type edge +# spanning-tree uplinkfast +# spanning-tree mst 1 cost 60 +# spanning-tree mst 1 port-priority 65 + +- name: Replace STP configurations + dellemc.enterprise_sonic.sonic_stp: + config: + interfaces: + - intf_name: Ethernet20 + cost: 25 + port_priority: 35 + mstp: + mst_name: mst2 + revision: 2 + max_hop: 4 + hello_time: 7 + max_age: 10 + fwd_delay: 13 + state: replaced + +# After State: +# ------------ +# +# sonic# show running-configuration spanning-tree +# no spanning-tree vlan 4-6 +# spanning-tree mode mst +# spanning-tree edge-port bpdufilter default +# spanning-tree loopguard default +# spanning-tree mst hello-time 7 +# spanning-tree mst forward-time 13 +# spanning-tree mst max-age 10 +# spanning-tree mst max-hops 4 +# ! +# spanning-tree mst configuration +# name mst2 +# revision 2 +# activate +# ! +# interface Ethernet20 +# spanning-tree cost 25 +# spanning-tree port-priority 35 + + +# Using overridden +# +# Before State: +# ------------- +# +# sonic# show running-configuration spanning-tree +# no spanning-tree vlan 4-6 +# spanning-tree mode mst +# spanning-tree edge-port bpdufilter default +# spanning-tree loopguard default +# spanning-tree mst hello-time 7 +# spanning-tree mst forward-time 13 +# spanning-tree mst max-age 10 +# spanning-tree mst max-hops 4 +# ! +# spanning-tree mst configuration +# name mst2 +# revision 2 +# activate +# ! +# interface Ethernet20 +# spanning-tree cost 25 +# spanning-tree port-priority 35 + +- name: Override STP configurations + dellemc.enterprise_sonic.sonic_stp: + config: + global: + enabled_protocol: pvst + bpdu_filter: true + root_guard_timeout: 25 + portfast: true + hello_time: 5 + max_age: 10 + fwd_delay: 20 + bridge_priority: 4096 + pvst: + - vlan_id: 1 + hello_time: 4 + max_age: 6 + fwd_delay: 8 + bridge_priority: 4096 + interfaces: + - intf_name: Ethernet20 + cost: 10 + port_priority: 50 + state: overridden + +# After State: +# ------------ +# +# sonic# show running-configuration spanning-tree +# spanning-tree mode pvst +# spanning-tree edge-port bpdufilter default +# spanning-tree forward-time 20 +# spanning-tree guard root timeout 25 +# spanning-tree hello-time 5 +# spanning-tree max-age 10 +# spanning-tree priority 4096 +# spanning-tree portfast default +# spanning-tree vlan 1 hello-time 4 +# spanning-tree vlan 1 forward-time 8 +# spanning-tree vlan 1 max-age 6 +# sonic# show running-configuration interface Ethernet 20 | grep spanning-tree +# spanning-tree vlan 1 cost 10 +# spanning-tree vlan 1 port-priority 50 + + +# Using deleted +# +# Before State: +# ------------- +# +# sonic# show running-configuration spanning-tree +# spanning-tree mode pvst +# spanning-tree edge-port bpdufilter default +# spanning-tree forward-time 20 +# spanning-tree guard root timeout 25 +# spanning-tree hello-time 5 +# spanning-tree max-age 10 +# spanning-tree priority 4096 +# spanning-tree portfast default +# spanning-tree vlan 1 hello-time 4 +# spanning-tree vlan 1 forward-time 8 +# spanning-tree vlan 1 max-age 6 +# sonic# show running-configuration interface Ethernet 20 | grep spanning-tree +# spanning-tree vlan 1 cost 10 +# spanning-tree vlan 1 port-priority 50 + +- name: Delete STP configurations + dellemc.enterprise_sonic.sonic_stp: + config: + global: + bpdu_filter: true + root_guard_timeout: 25 + pvst: + - vlan_id: 1 + interfaces: + - intf_name: Ethernet20 + state: deleted + +# After State: +# ------------ +# +# sonic# show running-configuration spanning-tree +# spanning-tree mode pvst +# spanning-tree forward-time 20 +# spanning-tree hello-time 5 +# spanning-tree max-age 10 +# spanning-tree priority 4096 +# spanning-tree portfast default +# spanning-tree vlan 1 hello-time 4 +# spanning-tree vlan 1 forward-time 8 +# spanning-tree vlan 1 max-age 6 +# sonic# show running-configuration interface Ethernet 20 | grep spanning-tree +# (No spanning-tree configuration present) + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.stp.stp import StpArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.stp.stp import Stp + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=StpArgs.argument_spec, + supports_check_mode=True) + + result = Stp(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_system.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_system.py index efb285a11..8b4d29ae1 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_system.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_system.py @@ -80,7 +80,7 @@ options: - In case of merged, the input configuration will be merged with the existing system configuration on the device. - In case of deleted the existing system configuration will be removed from the device. default: merged - choices: ['merged', 'deleted'] + choices: ['merged', 'replaced', 'overridden', 'deleted'] type: str """ EXAMPLES = """ @@ -167,6 +167,89 @@ EXAMPLES = """ #ipv6 anycast-address enable #interface-naming standard +# Using replaced +# +# Before state: +# ------------- +#! +#sonic(config)#do show running-configuration +#! +#ip anycast-mac-address aa:bb:cc:dd:ee:ff +#ip anycast-address enable +#ipv6 anycast-address enable + +- name: Replace system configuration. + sonic_system: + config: + hostname: sonic + interface_naming: standard + state: replaced + +# After state: +# ------------ +#! +#SONIC(config)#do show running-configuration +#! +#interface-naming standard + +# Using replaced +# +# Before state: +# ------------- +#! +#sonic(config)#do show running-configuration +#! +#ip anycast-mac-address aa:bb:cc:dd:ee:ff +#interface-naming standard + +- name: Replace system device configuration. + sonic_system: + config: + hostname: sonic + interface_naming: standard + anycast_address: + ipv6: true + ipv4: true + state: replaced + +# After state: +# ------------ +#! +#SONIC(config)#do show running-configuration +#! +#ip anycast-address enable +#ipv6 anycast-address enable +#interface-naming standard + +# Using overridden +# +# Before state: +# ------------- +#! +#sonic(config)#do show running-configuration +#! +#ip anycast-mac-address aa:bb:cc:dd:ee:ff +#ip anycast-address enable +#ipv6 anycast-address enable + +- name: Override system configuration. + sonic_system: + config: + hostname: sonic + interface_naming: standard + anycast_address: + ipv4: true + mac_address: bb:aa:cc:dd:ee:ff + state: overridden + +# After state: +# ------------ +#! +#SONIC(config)#do show running-configuration +#! +#ip anycast-mac-address bb:aa:cc:dd:ee:ff +#ip anycast-address enable +#interface-naming standard """ RETURN = """ diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_tacacs_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_tacacs_server.py index 3295e11ba..3361345f5 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_tacacs_server.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_tacacs_server.py @@ -64,6 +64,7 @@ options: description: - Specifies the timeout of the tacacs server. type: int + default: 5 source_interface: description: - Specifies the source interface of the tacacs server. @@ -122,8 +123,10 @@ options: - Specifies the operation to be performed on the tacacs server configured on the device. - In case of merged, the input mode configuration will be merged with the existing tacacs server configuration on the device. - In case of deleted the existing tacacs server mode configuration will be removed from the device. + - In case of replaced, the existing tacacs server configuration will be replaced with provided configuration. + - In case of overridden, the existing tacacs server configuration will be overridden with the provided configuration. default: merged - choices: ['merged', 'deleted'] + choices: ['merged', 'replaced', 'overridden', 'deleted'] type: str """ EXAMPLES = """ @@ -249,8 +252,110 @@ EXAMPLES = """ #HOST AUTH-TYPE KEY PORT PRIORITY TIMEOUT VRF #------------------------------------------------------------------------------------------------ #1.2.3.4 pap 1234 49 1 5 default - - +# +# Using replaced +# +# Before state: +# ------------- +# +#sonic(config)# do show tacacs-server +#--------------------------------------------------------- +#TACACS Global Configuration +#--------------------------------------------------------- +#source-interface : Ethernet12 +#timeout : 10 +#auth-type : pap +#key configured : Yes +#-------------------------------------------------------------------------------------- +#HOST AUTH-TYPE KEY-CONFIG PORT PRIORITY TIMEOUT VRF +#-------------------------------------------------------------------------------------- +#1.2.3.4 pap No 49 1 5 default +# +- name: Replace tacacs configurations + sonic_tacacs_server: + config: + auth_type: pap + key: pap + source_interface: Ethernet12 + timeout: 10 + servers: + - host: + name: 1.2.3.4 + auth_type: mschap + key: 1234 + state: replaced +# +# After state: +# ------------ +# +#sonic(config)# do show tacacs-server +#--------------------------------------------------------- +#TACACS Global Configuration +#--------------------------------------------------------- +#source-interface : Ethernet12 +#timeout : 10 +#auth-type : pap +#key configured : Yes +#-------------------------------------------------------------------------------------- +#HOST AUTH-TYPE KEY-CONFIG PORT PRIORITY TIMEOUT VRF +#-------------------------------------------------------------------------------------- +#1.2.3.4 mschap Yes 49 1 5 default +# +# Using overridden +# +# Before state: +# ------------- +# +#sonic(config)# do show tacacs-server +#--------------------------------------------------------- +#TACACS Global Configuration +#--------------------------------------------------------- +#source-interface : Ethernet12 +#timeout : 10 +#auth-type : pap +#key configured : Yes +#-------------------------------------------------------------------------------------- +#HOST AUTH-TYPE KEY-CONFIG PORT PRIORITY TIMEOUT VRF +#-------------------------------------------------------------------------------------- +#1.2.3.4 pap No 49 1 5 default +#11.12.13.14 chap Yes 49 10 5 default +# +- name: Override tacacs configurations + sonic_tacacs_server: + config: + auth_type: mschap + key: mschap + source_interface: Ethernet12 + timeout: 20 + servers: + - host: + name: 1.2.3.4 + auth_type: mschap + key: mschap + - host: + name: 10.10.11.12 + auth_type: chap + timeout: 30 + priority: 2 + state: overridden +# +# After state: +# ------------ +# +#sonic(config)# do show tacacs-server +#--------------------------------------------------------- +#TACACS Global Configuration +#--------------------------------------------------------- +#source-interface : Ethernet12 +#timeout : 20 +#auth-type : mschap +#key configured : Yes +#-------------------------------------------------------------------------------------- +#HOST AUTH-TYPE KEY-CONFIG PORT PRIORITY TIMEOUT VRF +#-------------------------------------------------------------------------------------- +#1.2.3.4 mschap Yes 49 1 5 default +#10.10.11.12 chap No 49 2 30 default +# """ RETURN = """ before: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_users.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_users.py index 7f0855a94..ac528e88d 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_users.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_users.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright 2019 Red Hat +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -59,6 +59,8 @@ options: choices: - admin - operator + - netadmin + - secadmin password: description: - Specifies the password of the user. @@ -78,8 +80,10 @@ options: - Specifies the operation to be performed on the users configured on the device. - In case of merged, the input configuration will be merged with the existing users configuration on the device. - In case of deleted the existing users configuration will be removed from the device. + - In case of replaced, the existing specified user configuration will be replaced with provided configuration. + - In case of overridden, the existing users configuration will be overridden with the provided configuration. default: merged - choices: ['merged', 'deleted'] + choices: ['merged', 'deleted', 'overridden', 'replaced'] type: str """ EXAMPLES = """ @@ -88,38 +92,44 @@ EXAMPLES = """ # Before state: # ------------- # -#do show running-configuration -#! -#username admin password $6$sdZt2C7F$3oPSRkkJyLZtsKlFNGWdwssblQWBj5dXM6qAJAQl7dgOfqLSpZJ/n6xf8zPRcqPUFCu5ZKpEtynJ9sZ/S8Mgj. role admin -#username sysadmin password $6$3QNqJzpFAPL9JqHA$417xFKw6SRn.CiqMFJkDfQJXKJGjeYwi2A8BIyfuWjGimvunOOjTRunVluudey/W9l8jhzN1oewBW5iLxmq2Q1 role admin -#username sysoperator password $6$s1eTVjcX4Udi69gY$zlYgqwoKRGC6hGL5iKDImN/4BL7LXKNsx9e5PoSsBLs6C80ShYj2LoJAUZ58ia2WNjcHXhTD1p8eU9wyRTCiE0 role operator -# -- name: Merge users configurations +# sonic# show users configured +# ---------------------------------------------------------------------- +# User Role(s) +# ---------------------------------------------------------------------- +# admin admin +# sysadmin admin +# sysoperator operator + +- name: Delete user dellemc.enterprise_sonic.sonic_users: config: - name: sysoperator state: deleted + # After state: # ------------ # -#do show running-configuration -#! -#username admin password $6$sdZt2C7F$3oPSRkkJyLZtsKlFNGWdwssblQWBj5dXM6qAJAQl7dgOfqLSpZJ/n6xf8zPRcqPUFCu5ZKpEtynJ9sZ/S8Mgj. role admin -#username sysadmin password $6$3QNqJzpFAPL9JqHA$417xFKw6SRn.CiqMFJkDfQJXKJGjeYwi2A8BIyfuWjGimvunOOjTRunVluudey/W9l8jhzN1oewBW5iLxmq2Q1 role admin - +# sonic# show users configured +# ---------------------------------------------------------------------- +# User Role(s) +# ---------------------------------------------------------------------- +# admin admin +# sysadmin admin # Using deleted # # Before state: # ------------- # -#do show running-configuration -#! -#username admin password $6$sdZt2C7F$3oPSRkkJyLZtsKlFNGWdwssblQWBj5dXM6qAJAQl7dgOfqLSpZJ/n6xf8zPRcqPUFCu5ZKpEtynJ9sZ/S8Mgj. role admin -#username sysadmin password $6$3QNqJzpFAPL9JqHA$417xFKw6SRn.CiqMFJkDfQJXKJGjeYwi2A8BIyfuWjGimvunOOjTRunVluudey/W9l8jhzN1oewBW5iLxmq2Q1 role admin -#username sysoperator password $6$s1eTVjcX4Udi69gY$zlYgqwoKRGC6hGL5iKDImN/4BL7LXKNsx9e5PoSsBLs6C80ShYj2LoJAUZ58ia2WNjcHXhTD1p8eU9wyRTCiE0 role operator -# -- name: Merge users configurations +# sonic# show users configured +# ---------------------------------------------------------------------- +# User Role(s) +# ---------------------------------------------------------------------- +# admin admin +# sysadmin admin +# sysoperator operator + +- name: Delete all users configurations except admin dellemc.enterprise_sonic.sonic_users: config: state: deleted @@ -127,20 +137,23 @@ EXAMPLES = """ # After state: # ------------ # -#do show running-configuration -#! -#username admin password $6$sdZt2C7F$3oPSRkkJyLZtsKlFNGWdwssblQWBj5dXM6qAJAQl7dgOfqLSpZJ/n6xf8zPRcqPUFCu5ZKpEtynJ9sZ/S8Mgj. role admin - +# sonic# show users configured +# ---------------------------------------------------------------------- +# User Role(s) +# ---------------------------------------------------------------------- +# admin admin # Using merged # # Before state: # ------------- # -#do show running-configuration -#! -#username admin password $6$sdZt2C7F$3oPSRkkJyLZtsKlFNGWdwssblQWBj5dXM6qAJAQl7dgOfqLSpZJ/n6xf8zPRcqPUFCu5ZKpEtynJ9sZ/S8Mgj. role admin -# +# sonic# show users configured +# ---------------------------------------------------------------------- +# User Role(s) +# ---------------------------------------------------------------------- +# admin admin + - name: Merge users configurations dellemc.enterprise_sonic.sonic_users: config: @@ -156,14 +169,83 @@ EXAMPLES = """ # After state: # ------------ -#! -#do show running-configuration -#! -#username admin password $6$sdZt2C7F$3oPSRkkJyLZtsKlFNGWdwssblQWBj5dXM6qAJAQl7dgOfqLSpZJ/n6xf8zPRcqPUFCu5ZKpEtynJ9sZ/S8Mgj. role admin -#username sysadmin password $6$3QNqJzpFAPL9JqHA$417xFKw6SRn.CiqMFJkDfQJXKJGjeYwi2A8BIyfuWjGimvunOOjTRunVluudey/W9l8jhzN1oewBW5iLxmq2Q1 role admin -#username sysoperator password $6$s1eTVjcX4Udi69gY$zlYgqwoKRGC6hGL5iKDImN/4BL7LXKNsx9e5PoSsBLs6C80ShYj2LoJAUZ58ia2WNjcHXhTD1p8eU9wyRTCiE0 role operator +# +# sonic# show users configured +# ---------------------------------------------------------------------- +# User Role(s) +# ---------------------------------------------------------------------- +# admin admin +# sysadmin admin +# sysoperator operator + +# Using Overridden +# +# Before state: +# ------------- +# +# sonic# show users configured +# ---------------------------------------------------------------------- +# User Role(s) +# ---------------------------------------------------------------------- +# admin admin +# sysadmin admin +# sysoperator operator + +- name: Override users configurations + dellemc.enterprise_sonic.sonic_users: + config: + - name: user1 + role: secadmin + password: 123abc + update_password: always + state: overridden + +# After state: +# ------------ +# +# sonic# show users configured +# ---------------------------------------------------------------------- +# User Role(s) +# ---------------------------------------------------------------------- +# admin admin +# user1 secadmin +# Using Replaced +# +# Before state: +# ------------- +# +# sonic# show users configured +# ---------------------------------------------------------------------- +# User Role(s) +# ---------------------------------------------------------------------- +# admin admin +# user1 secadmin +# user2 operator +- name: Replace users configurations + dellemc.enterprise_sonic.sonic_users: + config: + - name: user1 + role: operator + password: 123abc + update_password: always + - name: user2 + role: netadmin + password: 123abc + update_password: always + state: replaced + +# After state: +# ------------ +# +# sonic# show users configured +# ---------------------------------------------------------------------- +# User Role(s) +# ---------------------------------------------------------------------- +# admin admin +# user1 operator +# user2 netadmin """ RETURN = """ before: @@ -180,6 +262,13 @@ after: sample: > The configuration returned will always be in the same format of the parameters above. +after(generated): + description: The generated configuration model invocation. + returned: when C(check_mode) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vlan_mapping.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vlan_mapping.py new file mode 100644 index 000000000..985e0523b --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vlan_mapping.py @@ -0,0 +1,543 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for sonic_vlan_mapping +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community', + 'license': 'Apache 2.0' +} + +DOCUMENTATION = """ +--- +module: sonic_vlan_mapping +author: "Cypher Miller (@Cypher-Miller)" +version_added: "2.1.0" +short_description: Configure vlan mappings on SONiC. +description: + - This module provides configuration management for vlan mappings on devices running SONiC. + - Vlan mappings only available on TD3 and TD4 devices. + - For TD4 devices must enable vlan mapping first (can enable in config-switch-resource). +options: + config: + description: + - Specifies the vlan mapping related configurations. + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface, i.e. Ethernet8, PortChannel2, Eth1/2. + required: true + type: str + mapping: + description: + - Defining a single vlan mapping. + type: list + elements: dict + suboptions: + service_vlan: + description: + - Configure service provider VLAN ID. + - VLAN ID range is 1-4094. + required: true + type: int + vlan_ids: + description: + - Configure customer VLAN IDs. + - If mode is double tagged translation then this VLAN ID represents the outer VLAN ID. + - If mode is set to stacking can pass ranges and/or multiple list entries. + - Individual VLAN ID or (-) separated range of VLAN IDs. + type: list + elements: str + dot1q_tunnel: + description: + - Specify whether it is a vlan stacking or translation (false means translation; true means stacking). + type: bool + default: false + inner_vlan: + description: + - Configure inner customer VLAN ID. + - VLAN IDs range is 1-4094. + - Only available for double tagged translations. + type: int + priority: + description: + - Set priority level of the vlan mapping. + - Priority range is 0-7. + type: int + state: + description: + - Specifies the operation to be performed on the vlan mappings configured on the device. + - In case of merged, the input configuration will be merged with the existing vlan mappings on the device. + - In case of deleted, the existing vlan mapping configuration will be removed from the device. + - In case of overridden, all existing vlan mappings will be deleted and the specified input configuration will be add. + - In case of replaced, the existing vlan mappings on the device will be replaced by the configuration for each vlan mapping. + type: str + default: merged + choices: + - merged + - deleted + - replaced + - overridden +""" +EXAMPLES = """ +# Using deleted +# +# Before State: +# ------------- +# +#sonic# show running-configuration interface +#! +#interface Ethernet8 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 623 2411 +# switchport vlan-mapping 392 inner 590 2755 +#! +#interface Ethernet16 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 400-402,404,406,408,410,412,420,422,430-432 dot1q-tunnel 2436 priority 3 +# switchport vlan-mapping 300 dot1q-tunnel 2567 priority 3 +#! + + + - name: Delete vlan mapping configurations + sonic_vlan_mapping: + config: + - name: Ethernet8 + mapping: + - service_vlan: 2755 + - name: Ethernet16 + mapping: + - service_vlan: 2567 + priority: 3 + - service_vlan: 2436 + vlan_ids: + - 404 + - 401 + - 412 + - 430-431 + priority: 3 + state: deleted + +# After State: +# ------------ +# +#sonic# show running-configuration interface +#! +#interface Ethernet8 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 623 2411 +#! +#interface Ethernet16 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 400,402,406,408,410,420,422,432 dot1q-tunnel 2436 +# switchport vlan-mapping 300 dot1q-tunnel 2567 +#! + + +# Using deleted +# +# Before State: +# ------------- +# +#sonic# show running-configuration interface +#! +#interface Ethernet8 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 623 2411 +# switchport vlan-mapping 392 inner 590 2755 +#! +#interface Ethernet16 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 400-402,404,406,408,410,412,420,422,430-431 dot1q-tunnel 2436 +# switchport vlan-mapping 300 dot1q-tunnel 2567 priority 3 +#! + + + - name: Delete vlan mapping configurations + sonic_vlan_mapping: + config: + - name: Ethernet8 + - name: Ethernet16 + mapping: + - service_vlan: 2567 + state: deleted + +# After State: +# ------------ +# +#sonic# show running-configuration interface +#! +#interface Ethernet8 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdo#! +#interface Ethernet16 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 400-402,406,408,410,420,422,431 dot1q-tunnel 2436 +#! + + +# Using merged +# +# Before State: +# ------------- +# +#sonic# show running-configuration interface +#! +#interface Ethernet8 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 623 2411 +#! +#interface Ethernet16 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +#! +#interface PortChannel 2 +# switchport vlan-mapping 345 2999 priority 0 +# switchport vlan-mapping 500,540 dot1q-tunnel 3000 +# no shutdown +#! + + - name: Add vlan mapping configurations + sonic_vlan_mapping: + config: + - name: Ethernet8 + mapping: + - service_vlan: 2755 + vlan_ids: + - 392 + dot1q_tunnel: false + inner_vlan: 590 + - name: Ethernet16 + mapping: + - service_vlan: 2567 + vlan_ids: + - 300 + dot1q_tunnel: true + priority: 3 + - service_vlan: 2436 + vlan_ids: + - 400-402 + - 404 + - 406 + - 408 + - 410 + - 412 + - 420 + - 422 + - 430-431 + dot1q_tunnel: true + - name: Portchannel 2 + mapping: + - service_vlan: 2999 + priority: 4 + - service_vlan: 3000 + vlan_ids: + - 506-512 + - 561 + priority: 5 + state: merged + +# After State: +# ------------ +# +#sonic# show running-configuration interface +#! +#interface Ethernet8 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 623 2411 +# switchport vlan-mapping 392 inner 590 2755 +#! +#interface Ethernet16 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 400-402,404,406,408,410,412,420,422,430-431 dot1q-tunnel 2436 +# switchport vlan-mapping 300 dot1q-tunnel 2567 priority 3 +#! +#interface PortChannel 2 +# switchport vlan-mapping 345 2999 priority 4 +# switchport vlan-mapping 500,506-512,540,561 dot1q-tunnel 3000 priority 5 +# no shutdown +#! + + +# Using replaced +# +# Before State: +# ------------- +# +#sonic# show running-configuration interface +#! +#interface Ethernet8 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 623 2411 +# switchport vlan-mapping 392 inner 590 2755 +#! +#interface Ethernet16 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 400-402,404,406,408,410,412,420,422,430-431 dot1q-tunnel 2436 +# switchport vlan-mapping 300 dot1q-tunnel 2567 priority 3 +#! +#interface PortChannel 2 +# switchport vlan-mapping 345 2999 priority 0 +# no shutdown +#! + + - name: Replace vlan mapping configurations + sonic_vlan_mapping: + config: + - name: Ethernet8 + mapping: + - service_vlan: 2755 + vlan_ids: + - 390 + dot1q_tunnel: false + inner_vlan: 593 + - name: Ethernet16 + mapping: + - service_vlan: 2567 + vlan_ids: + - 310 + - 330-340 + priority: 5 + - name: Portchannel 2 + mapping: + - service_vlan: 2999 + vlan_ids: + - 345 + dot1q_tunnel: true + priority: 1 + state: replaced + + +# After State: +# ------------ +# +#sonic# show running-configuration interface +#! +#interface Ethernet8 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 623 2411 +# switchport vlan-mapping 390 inner 593 2755 +#! +#interface Ethernet16 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 400-402,404,406,408,410,412,420,422,430-431 dot1q-tunnel 2436 +# switchport vlan-mapping 310,330-340 dot1q-tunnel 2567 priority 5 +#! +#interface PortChannel 2 +# switchport vlan-mapping 345 dot1q_tunnel 2999 priority 1 +# no shutdown +#! + + +# Using overridden +# +# Before State: +# ------------- +# +#sonic# show running-configuration interface +#! +#interface Ethernet8 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 623 2411 +#! +#interface Ethernet16 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 400-402,404,406,408,410,412,420,422,430-431 dot1q-tunnel 2436 +#! + + - name: Override the vlan mapping configurations + sonic_vlan_mapping: + config: + - name: Ethernet8 + mapping: + - service_vlan: 2755 + vlan_ids: + - 392 + dot1q_tunnel: false + inner_vlan: 590 + - name: Ethernet16 + mapping: + - service_vlan: 2567 + vlan_ids: + - 300 + dot1q_tunnel: true + priority: 3 + - name: Portchannel 2 + mapping: + - service_vlan: 2999 + vlan_ids: + - 345 + priority: 0 + state: overridden + +# After State: +# ------------ +# +#sonic# show running-configuration interface +#! +#interface Ethernet8 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 392 inner 590 2755 +#! +#interface Ethernet16 +# mtu 9100 +# speed 400000 +# fec RS +# unreliable-los auto +# shutdown +# switchport vlan-mapping 300 dot1q-tunnel 2567 priority 3 +#! +#interface PortChannel 2 +# switchport vlan-mapping 345 2999 priority 0 +# no shutdown +#! + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.vlan_mapping.vlan_mapping import Vlan_mappingArgs +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.vlan_mapping.vlan_mapping import Vlan_mapping + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=Vlan_mappingArgs.argument_spec, + supports_check_mode=True) + + result = Vlan_mapping(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vlans.py index cfd536c79..cd3d7729d 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vlans.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vlans.py @@ -63,6 +63,8 @@ options: type: str choices: - merged + - replaced + - overridden - deleted default: merged """ @@ -109,6 +111,64 @@ EXAMPLES = """ #sonic# # +# Using replaced + +# Before state: +# ------------- +# +#sonic# show Vlan +#Q: A - Access (Untagged), T - Tagged +#NUM Status Q Ports +#10 Inactive +#30 Inactive +# +#sonic# + +- name: Replace all attributes of specified VLANs with provided configuration + dellemc.enterprise_sonic.sonic_vlans: + config: + - vlan_id: 10 + state: replaced + +# After state: +# ------------ +# +#sonic# show Vlan +#Q: A - Access (Untagged), T - Tagged +#NUM Status Q Ports +#10 Inactive +#30 Inactive +# +#sonic# + +# Using overridden + +# Before state: +# ------------- +# +#sonic# show Vlan +#Q: A - Access (Untagged), T - Tagged +#NUM Status Q Ports +#10 Inactive +#30 Inactive +# +#sonic# + +- name: Override device configuration of all VLANs with provided configuration + dellemc.enterprise_sonic.sonic_vlans: + config: + - vlan_id: 10 + state: overridden + +# After state: +# ------------ +# +#sonic# show Vlan +#Q: A - Access (Untagged), T - Tagged +#NUM Status Q Ports +#10 Inactive +# +#sonic# # Using deleted diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vrfs.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vrfs.py index 4c881aee6..84233145a 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vrfs.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vrfs.py @@ -66,6 +66,8 @@ options: type: str choices: - merged + - replaced + - overridden - deleted default: merged """ @@ -158,6 +160,88 @@ EXAMPLES = """ #Vrfcheck4 Eth1/5 # Eth1/6 # +# Using overridden +# +# Before state: +# ------------- +# +#show ip vrf +#VRF-NAME INTERFACES +#---------------------------------------------------------------- +#Vrfcheck1 +#Vrfcheck2 +#Vrfcheck3 Eth1/7 +# Eth1/8 +# +- name: Overridden VRF configuration + dellemc.enterprise_sonic.sonic_vrfs: + sonic_vrfs: + config: + - name: Vrfcheck1 + members: + interfaces: + - name: Eth1/3 + - name: Eth1/14 + - name: Vrfcheck3 + members: + interfaces: + - name: Eth1/5 + - name: Eth1/6 + state: overridden +# +# After state: +# ------------ +# +#show ip vrf +#VRF-NAME INTERFACES +#---------------------------------------------------------------- +#Vrfcheck1 Eth1/3 +# Eth1/14 +#Vrfcheck2 +#Vrfcheck3 Eth1/5 +# Eth1/6 +# +# Using replaced +# +# Before state: +# ------------- +# +#show ip vrf +#VRF-NAME INTERFACES +#---------------------------------------------------------------- +#Vrfcheck1 Eth1/3 +#Vrfcheck2 +#Vrfcheck3 Eth1/5 +# Eth1/6 +# +- name: Replace VRF configuration + dellemc.enterprise_sonic.sonic_vrfs: + sonic_vrfs: + config: + - name: Vrfcheck1 + members: + interfaces: + - name: Eth1/3 + - name: Eth1/14 + - name: Vrfcheck3 + members: + interfaces: + - name: Eth1/5 + - name: Eth1/6 + state: replaced +# +# After state: +# ------------ +# +#show ip vrf +#VRF-NAME INTERFACES +#---------------------------------------------------------------- +#Vrfcheck1 Eth1/3 +# Eth1/14 +#Vrfcheck2 +#Vrfcheck3 Eth1/5 +# Eth1/6 +# """ RETURN = """ before: diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vxlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vxlans.py index e6613ba24..0500db79e 100644 --- a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vxlans.py +++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vxlans.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# © Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -43,7 +43,6 @@ options: config: description: - A list of VxLAN configurations. - - source_ip and evpn_nvo are required together. type: list elements: dict suboptions: @@ -90,6 +89,8 @@ options: choices: - merged - deleted + - replaced + - overridden default: merged """ EXAMPLES = """ @@ -173,7 +174,7 @@ EXAMPLES = """ - name: vteptest1 source_ip: 1.1.1.1 primary_ip: 2.2.2.2 - evpn_nvo_name: nvo1 + evpn_nvo: nvo1 vlan_map: - vni: 101 vlan: 11 @@ -199,7 +200,87 @@ EXAMPLES = """ # map vni 101 vrf Vrfcheck1 # map vni 102 vrf Vrfcheck2 #! +# +# Using overridden +# +# Before state: +# ------------- +# +# do show running-configuration +# +#interface vxlan vteptest1 +# source-ip 1.1.1.1 +# primary-ip 2.2.2.2 +# map vni 101 vlan 11 +# map vni 102 vlan 12 +# map vni 101 vrf Vrfcheck1 +# map vni 102 vrf Vrfcheck2 +#! +# +- name: "Test vxlans overridden state 01" + dellemc.enterprise_sonic.sonic_vxlans: + config: + - name: vteptest2 + source_ip: 3.3.3.3 + primary_ip: 4.4.4.4 + evpn_nvo: nvo2 + vlan_map: + - vni: 101 + vlan: 11 + vrf_map: + - vni: 101 + vrf: Vrfcheck1 + state: overridden +# +# After state: +# ------------ +# +# do show running-configuration +# +#interface vxlan vteptest2 +# source-ip 3.3.3.3 +# primary-ip 4.4.4.4 +# map vni 101 vlan 11 +# map vni 101 vrf Vrfcheck1 +#! +# +# Using replaced +# +# Before state: +# ------------- +# +# do show running-configuration +# +#interface vxlan vteptest2 +# source-ip 3.3.3.3 +# primary-ip 4.4.4.4 +# map vni 101 vlan 11 +# map vni 101 vrf Vrfcheck +#! +# +- name: "Test vxlans replaced state 01" + dellemc.enterprise_sonic.sonic_vxlans: + config: + - name: vteptest2 + source_ip: 5.5.5.5 + vlan_map: + - vni: 101 + vlan: 12 + state: replaced +# +# After state: +# ------------ +# +# do show running-configuration +# +#interface vxlan vteptest2 +# source-ip 5.5.5.5 +# primary-ip 4.4.4.4 +# map vni 101 vlan 12 +# map vni 101 vrf Vrfcheck1 +#! # """ + RETURN = """ before: description: The configuration prior to the model invocation. diff --git a/ansible_collections/dellemc/enterprise_sonic/test-requirements.txt b/ansible_collections/dellemc/enterprise_sonic/test-requirements.txt new file mode 100644 index 000000000..6d8843b12 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/test-requirements.txt @@ -0,0 +1,4 @@ +pytest-xdist +coverage==4.5.4 +pytest-forked +git+https://github.com/ansible-community/pytest-ansible-units.git diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/hosts b/ansible_collections/dellemc/enterprise_sonic/tests/regression/hosts index b8ec3e04b..f2fd8da12 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/hosts +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/hosts @@ -8,6 +8,6 @@ sonic2 [datacenter:vars] ansible_network_os=dellemc.enterprise_sonic.sonic -ansible_python_interpreter=/usr/bin/python3 +ansible_python_interpreter=/usr/bin/python3.9 ansible_httpapi_use_ssl=true ansible_httpapi_validate_certs=false diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/defaults/main.yml index 93dd85447..4d08bb767 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/defaults/main.yml @@ -50,6 +50,12 @@ native_eth3: Ethernet28 native_eth4: Ethernet32 native_eth5: Ethernet36 native_eth6: Ethernet40 +native_eth7: Ethernet96 +native_eth8: Ethernet100 +native_eth9: Ethernet104 +native_eth10: Ethernet108 +native_eth11: Ethernet112 +native_eth12: Ethernet116 std_eth1: Eth1/5 std_eth2: Eth1/6 @@ -57,6 +63,12 @@ std_eth3: Eth1/7 std_eth4: Eth1/8 std_eth5: Eth1/9 std_eth6: Eth1/10 +std_eth7: Eth1/97 +std_eth8: Eth1/98 +std_eth9: Eth1/99 +std_eth10: Eth1/100 +std_eth11: Eth1/101 +std_eth12: Eth1/102 interface1: "{{ std_eth1 if std_name in interface_mode else native_eth1 }}" interface2: "{{ std_eth2 if std_name in interface_mode else native_eth2 }}" @@ -64,3 +76,9 @@ interface3: "{{ std_eth3 if std_name in interface_mode else native_eth3 }}" interface4: "{{ std_eth4 if std_name in interface_mode else native_eth4 }}" interface5: "{{ std_eth5 if std_name in interface_mode else native_eth5 }}" interface6: "{{ std_eth6 if std_name in interface_mode else native_eth6 }}" +interface7: "{{ std_eth7 if std_name in interface_mode else native_eth7 }}" +interface8: "{{ std_eth8 if std_name in interface_mode else native_eth8 }}" +interface9: "{{ std_eth9 if std_name in interface_mode else native_eth9 }}" +interface10: "{{ std_eth10 if std_name in interface_mode else native_eth10 }}" +interface11: "{{ std_eth11 if std_name in interface_mode else native_eth11 }}" +interface12: "{{ std_eth12 if std_name in interface_mode else native_eth12 }}" diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/defaults/main.yml index 291f615ee..e51a026cd 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/defaults/main.yml @@ -28,9 +28,27 @@ tests: data: fail_through: true group: radius - local: true - name: test_case_04 + description: Replace aaa properties + state: replaced + input: + authentication: + data: + fail_through: false + group: ldap + + - name: test_case_05 + description: Override aaa properties + state: overridden + input: + authentication: + data: + fail_through: true + group: radius + local: true + + - name: test_case_06 description: Delete aaa properties state: deleted input: @@ -38,7 +56,7 @@ tests: data: group: radius - - name: test_case_05 + - name: test_case_07 description: aaa properties state: merged input: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/defaults/main.yml new file mode 100644 index 000000000..43c3a1491 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/defaults/main.yml @@ -0,0 +1,188 @@ +--- +ansible_connection: httpapi +module_name: acl_interfaces + +po1: "Portchannel 100" + +vlan1: "Vlan 100" + +macacl1: "mac-acl-1" +macacl2: "mac-acl-2" + +ipv4acl1: "ipv4-acl-1" +ipv4acl2: "ipv4-acl-2" + +ipv6acl1: "ipv6-acl-1" +ipv6acl2: "ipv6-acl-2" + +preparations_tests: + lag_interfaces: + - name: '{{ po1 }}' + vlans: + - vlan_id: 100 + l2_acls: + - name: '{{ macacl1 }}' + - name: '{{ macacl2 }}' + l3_acls: + - address_family: 'ipv4' + acls: + - name: '{{ ipv4acl1 }}' + - name: '{{ ipv4acl2 }}' + - address_family: 'ipv6' + acls: + - name: '{{ ipv6acl1 }}' + - name: '{{ ipv6acl2 }}' + +tests: + - name: test_case_01 + description: Add interface access-group configurations + state: merged + input: + - name: '{{ interface1 }}' + access_groups: + - type: 'mac' + acls: + - name: '{{ macacl1 }}' + direction: 'in' + - type: 'ipv4' + acls: + - name: '{{ ipv4acl1 }}' + direction: 'in' + - name: '{{ ipv4acl2 }}' + direction: 'out' + + - name: test_case_02 + description: Update interface access-group configurations + state: merged + input: + - name: '{{ interface1 }}' + access_groups: + - type: 'mac' + acls: + - name: '{{ macacl2 }}' + direction: 'out' + - name: '{{ interface2 }}' + access_groups: + - type: 'ipv6' + acls: + - name: '{{ ipv6acl2 }}' + direction: 'out' + + - name: test_case_03 + description: Update interface access-group configuration + state: merged + input: + - name: '{{ vlan1 }}' + access_groups: + - type: 'mac' + acls: + - name: '{{ macacl2 }}' + direction: 'out' + - name: '{{ po1 }}' + access_groups: + - type: 'mac' + acls: + - name: '{{ macacl2 }}' + direction: 'out' + - type: 'ipv4' + acls: + - name: '{{ ipv4acl2 }}' + direction: 'out' + - type: 'ipv6' + acls: + - name: '{{ ipv6acl2 }}' + direction: 'out' + + - name: test_case_04 + description: Replace interface access-group configurations + state: replaced + input: + - name: '{{ interface1 }}' + access_groups: + - type: 'ipv6' + acls: + - name: '{{ ipv6acl1 }}' + direction: 'in' + - name: '{{ interface2 }}' + + - name: test_case_05 + description: Override interface access-group configurations + state: overridden + input: + - name: '{{ interface1 }}' + access_groups: + - type: 'mac' + acls: + - name: '{{ macacl2 }}' + direction: 'out' + - type: 'ipv6' + acls: + - name: '{{ ipv6acl1 }}' + direction: 'in' + - name: '{{ ipv6acl2 }}' + direction: 'out' + - name: '{{ vlan1 }}' + access_groups: + - type: 'ipv4' + acls: + - name: '{{ ipv4acl1 }}' + direction: 'in' + - name: '{{ ipv4acl2 }}' + direction: 'out' + + - name: test_case_06 + description: Update interface access-group configurations + state: merged + input: + - name: '{{ interface2 }}' + access_groups: + - type: 'ipv4' + acls: + - name: '{{ ipv4acl1 }}' + direction: 'in' + - name: '{{ ipv4acl2 }}' + direction: 'out' + - type: 'ipv6' + acls: + - name: '{{ ipv6acl1 }}' + direction: 'in' + - name: '{{ ipv6acl2 }}' + direction: 'out' + - type: 'mac' + acls: + - name: '{{ macacl1 }}' + direction: 'in' + - name: '{{ macacl2 }}' + direction: 'out' + - name: '{{ po1 }}' + access_groups: + - type: 'mac' + acls: + - name: '{{ macacl1 }}' + direction: 'in' + - name: '{{ macacl2 }}' + direction: 'out' + + - name: test_case_07 + description: Delete interface ACL bindings based on interface name, access-group type and ACLs + state: deleted + input: + - name: '{{ interface1 }}' + access_groups: + - type: 'ipv6' + acls: + - name: '{{ ipv6acl1 }}' + direction: 'in' + - name: '{{ interface2 }}' + access_groups: + - type: 'ipv4' + - type: 'ipv6' + - name: '{{ vlan1 }}' + - name: '{{ po1 }}' + access_groups: + - type: 'mac' + + - name: test_case_08 + description: Delete all interface access-group configurations + state: deleted + input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/meta/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/meta/main.yml new file mode 100644 index 000000000..d0ceaf6f5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/meta/main.yml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common } diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..319f9f449 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/tasks/cleanup_tests.yaml @@ -0,0 +1,30 @@ +--- +- name: Delete interface access-group configurations + dellemc.enterprise_sonic.sonic_acl_interfaces: + config: [] + state: deleted + ignore_errors: yes + +- name: Delete test L2 ACLs + dellemc.enterprise_sonic.sonic_l2_acls: + config: "{{ preparations_tests.l2_acls }}" + state: deleted + ignore_errors: yes + +- name: Delete test L3 ACLs + dellemc.enterprise_sonic.sonic_l3_acls: + config: "{{ preparations_tests.l3_acls }}" + state: deleted + ignore_errors: yes + +- name: Delete test VLANs + dellemc.enterprise_sonic.sonic_vlans: + config: "{{ preparations_tests.vlans }}" + state: deleted + ignore_errors: yes + +- name: Delete test lag interfaces + dellemc.enterprise_sonic.sonic_lag_interfaces: + config: "{{ preparations_tests.lag_interfaces }}" + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/tasks/main.yml new file mode 100644 index 000000000..b037b17e8 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/tasks/main.yml @@ -0,0 +1,17 @@ +--- +- ansible.builtin.debug: + msg: "sonic_acl_interfaces Test started ..." + +- name: "Preparations for {{ module_name }}" + ansible.builtin.include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started" + ansible.builtin.include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: "Cleanup of {{ module_name }}" + ansible.builtin.include_tasks: cleanup_tests.yaml + +- name: Display all variables/facts known for a host + ansible.builtin.debug: + var: hostvars[inventory_hostname].ansible_facts.test_reports diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/tasks/preparation_tests.yaml new file mode 100644 index 000000000..7b6a39964 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/tasks/preparation_tests.yaml @@ -0,0 +1,50 @@ +--- +- name: Delete old interface access-group configurations + dellemc.enterprise_sonic.sonic_acl_interfaces: + config: [] + state: deleted + ignore_errors: yes + +- name: Delete old L2 ACLs + dellemc.enterprise_sonic.sonic_l2_acls: + config: [] + state: deleted + ignore_errors: yes + +- name: Delete old L3 ACLs + dellemc.enterprise_sonic.sonic_l3_acls: + config: [] + state: deleted + ignore_errors: yes + +- name: Initialize default interfaces + vars: + ansible_connection: network_cli + dellemc.enterprise_sonic.sonic_config: + commands: "{{ default_interface_cli }}" + register: output + ignore_errors: yes + +- name: Create lag interfaces + dellemc.enterprise_sonic.sonic_lag_interfaces: + config: "{{ preparations_tests.lag_interfaces }}" + state: merged + ignore_errors: yes + +- name: Create VLANs + dellemc.enterprise_sonic.sonic_vlans: + config: "{{ preparations_tests.vlans }}" + state: merged + ignore_errors: yes + +- name: Configure L2 ACLs + dellemc.enterprise_sonic.sonic_l2_acls: + config: "{{ preparations_tests.l2_acls }}" + state: merged + ignore_errors: yes + +- name: Configure L3 ACLs + dellemc.enterprise_sonic.sonic_l3_acls: + config: "{{ preparations_tests.l3_acls }}" + state: merged + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/tasks/tasks_template.yaml new file mode 100644 index 000000000..ebfb8d92d --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_acl_interfaces/tasks/tasks_template.yaml @@ -0,0 +1,22 @@ +--- +- name: "{{ item.name }} , {{ item.description }}" + dellemc.enterprise_sonic.sonic_acl_interfaces: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name }} , {{ item.description }} Idempotent" + dellemc.enterprise_sonic.sonic_acl_interfaces: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/defaults/main.yml new file mode 100644 index 000000000..0703a2443 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/defaults/main.yml @@ -0,0 +1,236 @@ +--- +ansible_connection: httpapi +module_name: bfd + +tests: + - name: test_case_01 + description: Merge BFD configuration + state: merged + input: + profiles: + - profile_name: 'p1' + enabled: True + transmit_interval: 120 + receive_interval: 200 + detect_multiplier: 2 + passive_mode: True + min_ttl: 140 + echo_interval: 150 + echo_mode: True + single_hops: + - remote_address: '196.88.6.1' + vrf: 'default' + interface: '{{interface1}}' + local_address: '1.1.1.1' + enabled: True + transmit_interval: 50 + receive_interval: 80 + detect_multiplier: 4 + passive_mode: True + echo_interval: 110 + echo_mode: True + profile_name: 'p1' + multi_hops: + - remote_address: '192.40.1.3' + vrf: 'default' + local_address: '3.3.3.3' + enabled: True + transmit_interval: 75 + receive_interval: 100 + detect_multiplier: 3 + passive_mode: True + min_ttl: 125 + profile_name: 'p1' + + - name: test_case_02 + description: Update BFD configuration + state: merged + input: + profiles: + - profile_name: 'p1' + enabled: False + transmit_interval: 130 + receive_interval: 220 + detect_multiplier: 5 + passive_mode: False + min_ttl: 245 + echo_interval: 255 + echo_mode: False + - profile_name: 'p2' + enabled: True + transmit_interval: 135 + receive_interval: 225 + detect_multiplier: 10 + passive_mode: True + min_ttl: 250 + echo_interval: 250 + echo_mode: True + single_hops: + - remote_address: '196.88.6.1' + vrf: 'default' + interface: '{{interface1}}' + local_address: '1.1.1.1' + enabled: False + transmit_interval: 60 + receive_interval: 90 + detect_multiplier: 2 + passive_mode: False + echo_interval: 140 + echo_mode: False + profile_name: 'p1' + - remote_address: '194.56.2.1' + vrf: 'default' + interface: '{{interface2}}' + local_address: '2.2.2.2' + enabled: False + transmit_interval: 65 + receive_interval: 95 + detect_multiplier: 7 + passive_mode: False + echo_interval: 145 + echo_mode: False + profile_name: 'p2' + multi_hops: + - remote_address: '192.40.1.3' + vrf: 'default' + local_address: '3.3.3.3' + enabled: False + transmit_interval: 65 + receive_interval: 280 + detect_multiplier: 3 + passive_mode: False + min_ttl: 150 + profile_name: 'p2' + - remote_address: '198.72.1.4' + vrf: 'default' + local_address: '4.4.4.4' + enabled: False + transmit_interval: 70 + receive_interval: 285 + detect_multiplier: 8 + passive_mode: False + min_ttl: 155 + profile_name: 'p2' + + - name: test_case_03 + description: Replace BFD configuration + state: replaced + input: + profiles: + - profile_name: 'p2' + enabled: False + single_hops: + - remote_address: '194.56.2.1' + vrf: 'default' + interface: '{{interface2}}' + local_address: '2.2.2.2' + echo_interval: 125 + echo_mode: True + profile_name: 'p1' + multi_hops: + - remote_address: '198.72.1.4' + vrf: 'default' + local_address: '4.4.4.4' + enabled: True + transmit_interval: 71 + receive_interval: 286 + detect_multiplier: 9 + + - name: test_case_04 + description: Override BFD configuration + state: overridden + input: + profiles: + - profile_name: 'p3' + enabled: True + transmit_interval: 110 + receive_interval: 230 + detect_multiplier: 10 + passive_mode: True + min_ttl: 170 + echo_interval: 140 + echo_mode: True + - profile_name: 'p4' + single_hops: + - remote_address: '182.98.4.1' + vrf: 'default' + interface: '{{interface3}}' + local_address: '3.3.3.3' + enabled: True + transmit_interval: 42 + receive_interval: 84 + detect_multiplier: 8 + passive_mode: True + echo_interval: 115 + echo_mode: True + profile_name: 'p3' + - remote_address: '183.98.3.2' + vrf: 'default' + interface: '{{interface2}}' + local_address: '1.2.3.4' + multi_hops: + - remote_address: '182.44.1.2' + vrf: 'default' + local_address: '2.2.2.2' + enabled: True + transmit_interval: 74 + receive_interval: 101 + detect_multiplier: 6 + passive_mode: True + min_ttl: 127 + profile_name: 'p3' + - remote_address: '162.45.5.1' + vrf: 'default' + local_address: '2.1.1.1' + + - name: test_case_05 + description: Delete BFD configuration + state: deleted + input: + profiles: + - profile_name: 'p3' + enabled: True + transmit_interval: 110 + receive_interval: 230 + detect_multiplier: 10 + passive_mode: True + min_ttl: 170 + echo_interval: 140 + echo_mode: True + - profile_name: 'p4' + single_hops: + - remote_address: '182.98.4.1' + vrf: 'default' + interface: '{{interface3}}' + local_address: '3.3.3.3' + enabled: True + transmit_interval: 42 + receive_interval: 84 + detect_multiplier: 8 + passive_mode: True + echo_interval: 115 + echo_mode: True + profile_name: 'p3' + - remote_address: '183.98.3.2' + vrf: 'default' + interface: '{{interface2}}' + local_address: '1.2.3.4' + multi_hops: + - remote_address: '182.44.1.2' + vrf: 'default' + local_address: '2.2.2.2' + enabled: True + transmit_interval: 74 + receive_interval: 101 + detect_multiplier: 6 + passive_mode: True + min_ttl: 127 + profile_name: 'p3' + - remote_address: '162.45.5.1' + vrf: 'default' + local_address: '2.1.1.1' + + - name: test_case_06 + description: Delete all BFD configuration + state: deleted + input: {} diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/meta/main.yaml new file mode 100644 index 000000000..0b356217e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/meta/main.yaml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common } diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/tasks/main.yml new file mode 100644 index 000000000..28d60a497 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/tasks/main.yml @@ -0,0 +1,11 @@ +- debug: msg="sonic_bfd Test started ..." + +- set_fact: + base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}" + +- name: Preparations test + include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started ..." + include_tasks: tasks_template.yaml + loop: "{{ tests }}" diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/tasks/preparation_tests.yaml new file mode 100644 index 000000000..db13675de --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/tasks/preparation_tests.yaml @@ -0,0 +1,5 @@ +- name: Delete old BFD configuration + sonic_bfd: + config: {} + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/tasks/tasks_template.yaml new file mode 100644 index 000000000..596617309 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/tasks/tasks_template.yaml @@ -0,0 +1,21 @@ +- name: "{{ item.name}} , {{ item.description}}" + sonic_bfd: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name}} , {{ item.description}} Idempotent" + sonic_bfd: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/tasks/tasks_template_del.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/tasks/tasks_template_del.yaml new file mode 100644 index 000000000..1044f56c6 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bfd/tasks/tasks_template_del.yaml @@ -0,0 +1,21 @@ +- name: "{{ item.name}} , {{ item.description}}" + sonic_bfd: + state: "{{ item.state }}" + config: + register: action_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name}} , {{ item.description}} Idempotent" + sonic_bfd: + state: "{{ item.state }}" + config: + register: idempotent_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/defaults/main.yml index 0eb7a6cb0..04d0a515b 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/defaults/main.yml @@ -4,15 +4,18 @@ module_name: bgp vrf_1: VrfReg1 vrf_2: VrfReg2 +vrf_3: VrfReg3 bgp_as_1: 51 bgp_as_2: 52 bgp_as_3: 53 +bgp_as_4: 54 preparations_tests: init_vrf: - "ip vrf {{vrf_1}}" - "ip vrf {{vrf_2}}" + - "ip vrf {{vrf_3}}" tests_cli: - name: cli_test_case_01 @@ -21,6 +24,7 @@ tests_cli: input: - bgp_as: "{{ bgp_as_1 }}" router_id: 110.2.2.4 + rt_delay: 10 bestpath: as_path: confed: True @@ -34,9 +38,10 @@ tests_cli: max_med: on_startup: timer: 667 - med_val: 7878 + med_val: 7878 - bgp_as: "{{ bgp_as_2 }}" router_id: 110.2.2.5 + rt_delay: 20 vrf_name: "{{vrf_1}}" bestpath: as_path: @@ -51,7 +56,7 @@ tests_cli: max_med: on_startup: timer: 889 - med_val: 8854 + med_val: 8854 tests: - name: test_case_01 @@ -60,8 +65,10 @@ tests: input: - bgp_as: "{{ bgp_as_1 }}" router_id: 110.2.2.4 + rt_delay: 10 - bgp_as: "{{ bgp_as_2 }}" router_id: 110.2.2.5 + rt_delay: 20 vrf_name: "{{vrf_1}}" - name: test_case_02 description: Updates BGP properties @@ -69,9 +76,11 @@ tests: input: - bgp_as: "{{ bgp_as_1 }}" router_id: 110.2.2.30 + rt_delay: 12 log_neighbor_changes: True - bgp_as: "{{ bgp_as_2 }}" router_id: 110.2.2.31 + rt_delay: 22 vrf_name: "{{vrf_1}}" log_neighbor_changes: True - name: test_case_03 @@ -80,9 +89,11 @@ tests: input: - bgp_as: "{{ bgp_as_1 }}" router_id: 110.2.2.30 + rt_delay: 12 log_neighbor_changes: True - bgp_as: "{{ bgp_as_2 }}" router_id: 110.2.2.31 + rt_delay: 22 vrf_name: "{{vrf_1}}" log_neighbor_changes: True - name: test_case_04 @@ -91,6 +102,7 @@ tests: input: - bgp_as: "{{ bgp_as_1 }}" router_id: 110.2.2.4 + rt_delay: 10 bestpath: as_path: confed: True @@ -104,9 +116,10 @@ tests: max_med: on_startup: timer: 889 - med_val: 8854 + med_val: 8854 - bgp_as: "{{ bgp_as_2 }}" router_id: 110.2.2.5 + rt_delay: 20 vrf_name: "{{vrf_1}}" bestpath: as_path: @@ -117,17 +130,18 @@ tests: compare_routerid: True med: confed: True - missing_as_worst: True + missing_as_worst: True max_med: on_startup: timer: 556 - med_val: 5567 + med_val: 5567 - name: test_case_05 description: Update bestpath BGP properties state: merged input: - bgp_as: "{{ bgp_as_1 }}" router_id: 110.2.2.51 + rt_delay: 50 bestpath: as_path: confed: False @@ -138,9 +152,10 @@ tests: max_med: on_startup: timer: 776 - med_val: 7768 + med_val: 7768 - bgp_as: "{{ bgp_as_2 }}" router_id: 110.2.2.52 + rt_delay: 100 vrf_name: "{{vrf_1}}" bestpath: as_path: @@ -152,7 +167,7 @@ tests: max_med: on_startup: timer: 445 - med_val: 4458 + med_val: 4458 - name: test_case_06 description: Update1 bestpath BGP properties state: merged @@ -191,7 +206,7 @@ tests: max_med: on_startup: timer: 889 - med_val: 8854 + med_val: 8854 - bgp_as: "{{ bgp_as_2 }}" vrf_name: "{{vrf_1}}" bestpath: @@ -204,7 +219,7 @@ tests: max_med: on_startup: timer: 889 - med_val: 8854 + med_val: 8854 - name: test_case_08 description: Update1 bestpath BGP properties state: merged @@ -237,7 +252,7 @@ tests: multipath_relax_as_set: True compare_routerid: True med: - missing_as_worst: True + missing_as_worst: True - name: test_case_09 description: Deletes BGP properties state: deleted @@ -245,6 +260,59 @@ tests: - bgp_as: "{{ bgp_as_2 }}" vrf_name: "{{vrf_1}}" - name: test_case_10 + description: Replaces BGP properties + state: replaced + input: + - bgp_as: "{{ bgp_as_1 }}" + router_id: 110.2.2.51 + rt_delay: 15 + bestpath: + as_path: + confed: True + compare_routerid: False + med: + always_compare_med: True + max_med: + on_startup: + timer: 890 + med_val: 8854 + - bgp_as: "{{ bgp_as_2 }}" + vrf_name: "{{vrf_1}}" + router_id: 110.2.2.52 + bestpath: + as_path: + confed: True + med: + confed: True + timers: + holdtime: 90 + keepalive_interval: 30 + - name: test_case_11 + description: Overrides BGP properties + state: overridden + input: + - bgp_as: "{{ bgp_as_1 }}" + router_id: 110.2.2.51 + bestpath: + as_path: + confed: True + compare_routerid: True + med: + confed: True + timers: + holdtime: 90 + keepalive_interval: 30 + - bgp_as: "{{ bgp_as_3 }}" + vrf_name: "{{vrf_3}}" + router_id: 110.2.2.52 + rt_delay: 15 + bestpath: + as_path: + multipath_relax: True + multipath_relax_as_set: True + med: + missing_as_worst: True + - name: test_case_12 description: Deletes all BGP properties state: deleted - input: [] + input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/main.yml index 29d5392d6..b488340e0 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/main.yml @@ -1,7 +1,7 @@ - debug: msg="sonic_interfaces Test started ..." - set_fact: - base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}" + base_cfg_path: "{{ role_path + '/' + 'templates/' }}" - name: Preparations test include_tasks: preparation_tests.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/templates/cli_test_case_01.cfg b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/templates/cli_test_case_01.cfg index 720006565..42857467e 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/templates/cli_test_case_01.cfg +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/templates/cli_test_case_01.cfg @@ -1,6 +1,8 @@ router bgp 52 vrf VrfReg1 router-id 110.2.2.5 log-neighbor-changes + max-med on-startup 889 8854 + route-map delay-timer 20 bestpath as-path multipath-relax as-set bestpath as-path ignore bestpath as-path confed @@ -10,6 +12,8 @@ router bgp 52 vrf VrfReg1 router bgp 51 router-id 110.2.2.4 log-neighbor-changes + max-med on-startup 667 7878 + route-map delay-timer 10 bestpath as-path multipath-relax as-set bestpath as-path ignore bestpath as-path confed diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/defaults/main.yml index ba23b3f57..d920b3880 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/defaults/main.yml @@ -5,6 +5,7 @@ module_name: bgp_af bgp_as_1: 51 bgp_as_2: 52 +bgp_as_3: 53 vrf_1: VrfReg1 vrf_2: VrfReg2 @@ -25,6 +26,11 @@ preparations_tests: router_id: 111.2.2.42 log_neighbor_changes: True vrf_name: VrfReg1 + - bgp_as: "{{bgp_as_3}}" + router_id: 111.2.2.43 + log_neighbor_changes: True + vrf_name: VrfReg2 + tests: - name: test_case_01 description: BGP AF properties @@ -39,6 +45,11 @@ tests: safi: unicast - afi: l2vpn safi: evpn + advertise_all_vni: True + route_advertise_list: + - advertise_afi: ipv4 + vnis: + - vni_number: 1 - bgp_as: "{{ bgp_as_2 }}" vrf_name: "{{vrf_1}}" address_family: @@ -92,10 +103,20 @@ tests: advertise_pip_ip: "1.1.1.1" advertise_pip_peer_ip: "2.2.2.2" advertise_svi_ip: True - advertise_all_vni: True - route_advertise_list: - - advertise_afi: ipv4 - route_map: rmap_reg1 + rd: "1.1.1.1:11" + rt_in: + - "22:22" + rt_out: + - "33:33" + vnis: + - vni_number: 1 + advertise_default_gw: True + advertise_svi_ip: True + rd: "2.2.2.2:22" + rt_in: + - "44:44" + rt_out: + - "44:44" - bgp_as: "{{bgp_as_2}}" vrf_name: "{{vrf_1}}" address_family: @@ -175,14 +196,35 @@ tests: route_map: rmap_reg2 - afi: l2vpn safi: evpn - advertise_pip: False advertise_pip_ip: "3.3.3.3" advertise_pip_peer_ip: "4.4.4.4" advertise_svi_ip: False - advertise_all_vni: False route_advertise_list: - advertise_afi: ipv6 route_map: rmap_reg2 + rd: "3.3.3.3:33" + rt_in: + - "12:12" + rt_out: + - "14:14" + vnis: + - vni_number: 1 + advertise_default_gw: False + advertise_svi_ip: False + rd: "5.5.5.5:55" + rt_in: + - "88:88" + rt_out: + - "77:77" + - vni_number: 2 + advertise_default_gw: True + advertise_svi_ip: True + - vni_number: 3 + rd: "9.9.9.9:99" + rt_in: + - "60:60" + rt_out: + - "80:80" - bgp_as: "{{bgp_as_2}}" vrf_name: "{{vrf_1}}" address_family: @@ -233,7 +275,7 @@ tests: - afi: ipv4 safi: unicast max_path: - ebgp: 4 + ebgp: 2 ibgp: 3 redistribute: - metric: "30" @@ -253,13 +295,31 @@ tests: route_map: rmap_reg2 - afi: l2vpn safi: evpn - advertise_pip: False + advertise_pip: True advertise_pip_ip: "3.3.3.3" advertise_pip_peer_ip: "4.4.4.4" advertise_svi_ip: False route_advertise_list: - advertise_afi: ipv4 route_map: rmap_reg1 + rd: "3.3.3.3:33" + rt_in: + - "22:22" + rt_out: + - "33:33" + - "14:14" + vnis: + - vni_number: 1 + advertise_default_gw: False + advertise_svi_ip: False + rd: "5.5.5.5:55" + rt_in: + - "44:44" + - "88:88" + rt_out: + - "77:77" + - vni_number: 2 + - vni_number: 3 - bgp_as: "{{bgp_as_2}}" vrf_name: "{{vrf_1}}" address_family: @@ -268,7 +328,7 @@ tests: safi: unicast max_path: ebgp: 4 - ibgp: 3 + ibgp: 5 redistribute: - metric: "41" protocol: ospf @@ -279,8 +339,8 @@ tests: - afi: ipv6 safi: unicast max_path: - ebgp: 4 - ibgp: 6 + ebgp: 9 + ibgp: 8 redistribute: - metric: "43" protocol: connected @@ -309,7 +369,6 @@ tests: afis: - afi: l2vpn safi: evpn - route_advertise_list: - name: test_case_06 description: Delete2 BGP AF properties state: deleted @@ -319,6 +378,162 @@ tests: address_family: afis: - name: test_case_07 + description: Create BGP AF configuration + state: merged + input: + - bgp_as: "{{ bgp_as_1 }}" + address_family: + afis: + - afi: l2vpn + safi: evpn + - name: test_case_08 + description: Add BGP AF configuration for replace + state: merged + input: + - bgp_as: "{{bgp_as_1}}" + address_family: + afis: + - afi: l2vpn + safi: evpn + advertise_all_vni: True + route_advertise_list: + - advertise_afi: ipv4 + route_map: rmap_reg1 + - advertise_afi: ipv6 + route_map: rmap_reg2 + rd: "5.5.5.5:55" + rt_in: + - "11:11" + - "22:22" + - "33:33" + rt_out: + - "77:77" + vnis: + - vni_number: 4 + advertise_default_gw: True + advertise_svi_ip: True + rd: "1.1.1.1:11" + rt_in: + - "88:88" + - "99:99" + rt_out: + - "88:88" + - "99:99" + - vni_number: 5 + advertise_default_gw: True + advertise_svi_ip: True + rd: "1.1.1.2:11" + - bgp_as: "{{bgp_as_2}}" + vrf_name: "{{vrf_1}}" + address_family: + afis: + - afi: ipv4 + safi: unicast + max_path: + ebgp: 5 + network: + - '10.1.1.0/24' + - '10.1.2.0/24' + - name: test_case_09 + description: Replace BGP AF configuration + state: replaced + input: + - bgp_as: "{{bgp_as_1}}" + address_family: + afis: + - afi: l2vpn + safi: evpn + advertise_all_vni: True + route_advertise_list: + - advertise_afi: ipv6 + route_map: rmap_reg2 + rd: "5.5.5.5:55" + rt_in: + - "11:11" + rt_out: + - "77:77" + vnis: + - vni_number: 4 + advertise_default_gw: True + advertise_svi_ip: True + rd: "1.1.1.1:11" + rt_in: + - "87:87" + rt_out: + - "88:88" + - bgp_as: "{{bgp_as_2}}" + vrf_name: "{{vrf_1}}" + address_family: + afis: + - afi: ipv4 + safi: unicast + dampening: True + redistribute: + - protocol: connected + network: + - '10.1.1.1/24' + - '11.1.1.1/24' + - afi: ipv6 + safi: unicast + redistribute: + - protocol: ospf + metric: 40 + - protocol: static + route_map: rmap_reg1 + network: + - '1::1/64' + - '2::1/64' + - bgp_as: "{{bgp_as_3}}" + vrf_name: "{{vrf_2}}" + address_family: + afis: + - afi: ipv4 + safi: unicast + dampening: True + - name: test_case_10 + description: Override BGP AF configuration + state: overridden + input: + - bgp_as: "{{bgp_as_1}}" + address_family: + afis: + - afi: l2vpn + safi: evpn + advertise_all_vni: True + route_advertise_list: + - advertise_afi: ipv4 + route_map: rmap_reg1 + - advertise_afi: ipv6 + rd: "5.5.5.5:55" + rt_in: + - "22:22" + - "33:33" + rt_out: + - "66:66" + - "77:77" + vnis: + - vni_number: 10 + advertise_default_gw: True + advertise_svi_ip: True + rd: "1.1.1.1:11" + rt_in: + - "87:87" + rt_out: + - "88:88" + - bgp_as: "{{bgp_as_2}}" + vrf_name: "{{vrf_1}}" + address_family: + afis: + - afi: ipv4 + safi: unicast + redistribute: + - protocol: ospf + - protocol: connected + metric: 40 + network: + - '11.1.1.1/24' + - '12.1.1.1/24' + - name: test_case_11 description: Delete3 BGP AF properties state: deleted input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/defaults/main.yml index f2e31e4a2..ebf984bb8 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/defaults/main.yml @@ -7,10 +7,10 @@ bgp_as_2: 52 vrf_1: VrfReg1 vrf_2: VrfReg2 - + tests: - name: test_case_01 - description: BGP properties + description: Add BGP as-path lists state: merged input: - name: test @@ -22,57 +22,124 @@ tests: - "101.101" permit: False - name: test_case_02 - description: Update created BGP properties + description: Update BGP as-path lists state: merged input: - - name: test - members: - - "11" - - "22" - - "33" - - 44 - permit: True - - name: test_1 - members: - - "101.101" - - "201.201" - - "301.301" - permit: False - - name: test_2 - members: - - '111\\:' - - '11\\d+' - - '113\\*' - - '114\\' - permit: True + - name: test + members: + - "11" + - "22" + - "33" + - 44 + permit: True + - name: test_1 + members: + - "101.101" + - "201.201" + - "301.301" + permit: False + - name: test_2 + members: + - "110" + - "111*" + - "112*" + - "^113" + - "45$" + permit: True - name: test_case_03 - description: Delete BGP properties + description: Delete BGP as-path lists' members state: deleted input: - - name: test - members: - - "33" - - name: test_1 - members: - - "101.101" - - "201.201" - - "301.301" - permit: False - - name: test_2 - members: - - '111\\:' - - '11\\d+' - - '113\\*' - - '114\\' - permit: True + - name: test + members: + - "33" + - name: test_1 + members: + - "101.101" + - "201.201" + - "301.301" + permit: False + - name: test_2 + members: + - "111*" + - "112*" + - "^113" + - "45$" + permit: True - name: test_case_04 - description: Delete BGP properties - state: deleted + description: Add BGP as-path lists + state: merged input: - - name: test - members: - permit: + - name: test_1 + members: + - "100.*" + - "200.*" + permit: False + - name: test_2 + members: + - "110" + - "120" + - "^800" + - "25$" + permit: True + - name: test_3 + members: + - "900.*" + - "910.*" + permit: False - name: test_case_05 - description: Delete BGP properties + description: Replace BGP as-path lists + state: replaced + input: + - name: test + - name: test_1 + members: + - "301.301" + permit: False + - name: test_2 + members: + - "111*" + - "112*" + - "^800" + - "25$" + permit: True + - name: test_3 + members: + - "900.*" + - "910.*" + permit: True + - name: test_4 + members: + - "800.*" + permit: True + - name: test_case_06 + description: Override BGP as-path lists + state: overridden + input: + - name: test + members: + - "33.*" + - "44.*" + permit: True + - name: test_1 + members: + - "201.201" + - "301.301" + permit: False + - name: test_2 + members: + - "111*" + - "^800" + - "25$" + permit: True + - name: test_case_07 + description: Delete BGP as-path list + state: deleted + input: + - name: test + members: + permit: + - name: test_case_08 + description: Delete all BGP as-path list state: deleted input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/defaults/main.yml index eb32d2759..cf2f82d43 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/defaults/main.yml @@ -9,7 +9,7 @@ tests: input: - name: test type: expanded - permit: false + permit: true match: ANY members: regex: @@ -17,19 +17,16 @@ tests: - "12" - name: test2 type: standard - permit: true + permit: false + no_export: true match: ALL - members: - regex: - - "21" - - "22" - name: test_case_02 description: Update created BGP properties state: merged input: - name: test type: expanded - permit: false + permit: true match: ANY members: regex: @@ -38,20 +35,16 @@ tests: - 14 - name: test2 type: standard - permit: true + permit: false + no_peer: true match: ALL - members: - regex: - - "23" - - "24" - - 25 - name: test_case_03 description: Update1 created BGP properties state: merged input: - name: test type: expanded - permit: true + permit: false match: ANY members: regex: @@ -59,12 +52,8 @@ tests: - "12" - name: test2 type: standard - permit: false + permit: true match: ALL - members: - regex: - - "21" - - "22" - name: test_case_04 description: Delete BGP properties state: deleted @@ -77,11 +66,10 @@ tests: - "13" - name: test2 type: standard + permit: false match: ALL - members: - regex: - - "23" - - "24" + no_export: true + no_peer: true - name: test_case_05 description: Delete1 BGP properties state: deleted @@ -91,11 +79,124 @@ tests: members: regex: - name: test_case_06 - description: Delete2 BGP properties - state: deleted + description: Update2 BGP properties + state: merged input: - name: test + type: expanded + match: ANY + permit: true + members: + regex: + - 201 + - name: test3 + type: expanded + match: ALL + permit: true + members: + regex: + - "110" + - 111 - name: test_case_07 + description: Replace BGP properties + state: replaced + input: + - name: test + type: standard + local_as: true + permit: true + - name: test2 + type: expanded + match: ALL + permit: false + members: + regex: + - "220" + - 222 + - "123" + - name: test_case_08 + description: Replace2 BGP properties + state: replaced + input: + - name: test4 + type: standard + permit: true + no_peer: true + - name: test5 + type: expanded + members: + regex: + - 113 + permit: true + - name: test_case_09 + description: Override BGP properties + state: overridden + input: + - name: test3 + type: standard + local_as: True + permit: false + - name: test2 + type: standard + permit: true + no_export: true + - name: test_case_10 + description: Override2 BGP properties + state: overridden + input: + - name: test3 + type: standard + permit: false + no_export: true + - name: test4 + type: expanded + permit: false + members: + regex: + - 113 + - name: test2 + type: standard + permit: true + no_export: true + - name: test_case_11 + description: Override3 BGP properties + state: overridden + input: + - name: test4 + type: expanded + permit: false + members: + regex: + - 113 + - name: test2 + type: standard + local_as: true + no_peer: true + no_advertise: true + permit: true + no_export: true + - name: test_case_12 + description: Override4 BGP properties + state: overridden + input: + - name: test4 + type: expanded + permit: false + members: + regex: + - 113 + - name: test2 + type: standard + local_as: true + no_advertise: true + permit: true + no_export: true + - name: test_case_13 + description: Delete2 BGP properties + state: deleted + input: + - name: test4 + - name: test_case_14 description: Delete2 BGP properties state: deleted input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml index be6e96a86..6e9daf9a1 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml @@ -2,7 +2,7 @@ ansible_connection: httpapi module_name: sonic_bgp_ext_communities -tests: "{{ merged_tests + deleted_tests }}" +tests: "{{ merged_tests + deleted_tests + replaced_tests + overridden_tests + deleted_all }}" merged_tests: - name: test_case_01 @@ -95,19 +95,25 @@ merged_tests: match: all members: route_origin: - - "4403.301" - - "5503.401" + - "4403:301" + - "5503:401" - name: test_case_05 - description: Create empty Communities properties + description: Create new Communities properties state: merged input: - name: test_ext1 type: expanded permit: true + members: + regex: + - "20" match: any - name: test_std1 type: standard permit: false + members: + route_target: + - "1.1.1.1:33" match: any - name: test_case_06 description: test BGP Communities properties @@ -127,8 +133,8 @@ merged_tests: match: any members: route_origin: - - "4403.301" - - "5503.401" + - "4403:301" + - "5503:401" deleted_tests: # Ethernet testcases started... @@ -154,101 +160,65 @@ deleted_tests: match: all members: route_target: - - "101.101" - - "201.201" - - "102.101" - - "202.201" - - "1.1.1.1.101" - - "1.1.1.2.201" + - "101:101" + - "201:201" + - "102:101" + - "202:201" + - "1.1.1.1:101" + - "1.1.1.2:201" route_origin: - - "301.301" - - "401.401" - - "302.301" - - "402.401" - - "303.301" - - "403.401" + - "301:301" + - "401:401" + - "302:301" + - "402:401" + - "303:301" + - "403:401" - name: test_std11 type: standard permit: true match: all members: route_target: - - "101.101" - - "201.201" - - "102.101" - - "202.201" - - "103.101" - - "203.201" - - "1.1.1.1.101" - - "1.1.1.2.201" - - "1.1.1.1.102" - - "1.1.1.2.203" - route_origin: - - "301.301" - - "401.401" - - "302.301" - - "402.401" - - "303.301" - - "403.401" - - name: test_std12 - type: standard - permit: true - match: all - members: - route_target: - - "101.101" - - "201.201" - - "102.101" - - "202.201" - - "103.101" - - "203.201" - - "1.1.1.1.101" - - "1.1.1.2.201" - - "1.1.1.1.102" - - "1.1.1.2.203" - route_origin: - - "301.301" - - "401.401" - - "302.301" - - "402.401" - - "303.301" - - "403.401" - - name: test_std12 - type: standard - permit: true - match: all - members: - route_target: - - "101.101" - - "201.201" - - "102.101" - - "202.201" - - "103.101" - - "203.201" - - "1.1.1.1.101" - - "1.1.1.2.201" - - "1.1.1.1.102" - - "1.1.1.2.203" + - "101:101" + - "201:201" + - "102:101" + - "202:201" + - "103:101" + - "203:201" + - "1.1.1.1:101" + - "1.1.1.2:201" + - "1.1.1.1:102" + - "1.1.1.2:203" route_origin: - - "301.301" - - "401.401" - - "302.301" - - "402.401" - - "303.301" - - "403.401" + - "301:301" + - "401:401" + - "302:301" + - "402:401" + - "303:301" + - "403:401" - name: test_std12 type: standard permit: true match: all members: route_target: - - "103.101" - - "203.201" + - "101:101" + - "201:201" + - "102:101" + - "202:201" + - "103:101" + - "203:201" + - "1.1.1.1:101" + - "1.1.1.2:201" - "1.1.1.1:102" - - "1.1.1.2.203" + - "1.1.1.2:203" route_origin: - - "301.301" - - "401.401" + - "301:301" + - "401:401" + - "302:301" + - "402:401" + - "303:301" + - "403:401" - name: del_test_case_01 description: BGP Communities properties state: deleted @@ -277,7 +247,7 @@ deleted_tests: route_origin: - "301:301" - "401:401" - - name: test_case_02 + - name: del_test_case_02 description: BGP Communities properties state: deleted input: @@ -289,14 +259,15 @@ deleted_tests: type: standard members: route_target: - - "1.1.1.1.101" + - "1.1.1.1:101" - name: test_std11 type: standard members: route_origin: - - "301.301" - - "401.401" - - "1.1.1.1.101" + - "301:301" + - "401:401" + route_target: + - "1.1.1.1:101" - name: del_test_case_03 description: Update created BGP properties state: deleted @@ -315,6 +286,150 @@ deleted_tests: members: route_origin: route_target: + +replaced_tests: + - name: replace_test_case_01 + description: Replace created BGP properties + state: replaced + input: + - name: test_std + type: standard + permit: false + match: any + members: + route_origin: + - "4403:301" + - "5503:401" + - name: test_comm112 + type: standard + permit: true + match: any + members: + route_target: + - "2.2.2.2:201" + route_origin: + - "500:500" + - "600:600" + - name: replace_test_case_02 + description: Replace2 created BGP properties + state: replaced + input: + - name: test_comm13 + type: expanded + permit: true + match: all + members: + regex: + - "15" + - "20" + - 25 + - name: test_comm112 + type: standard + permit: true + match: any + members: + route_target: + - "2.2.2.2:201" + route_origin: + - "500:500" + - "600:600" + - name: test_std + type: standard + permit: false + match: any + members: + route_origin: + - "5503:401" + +overridden_tests: + - name: overridden_test_case_01 + description: Override created BGP properties + state: overridden + input: + - name: test_new_std + type: standard + permit: true + match: all + members: + route_target: + - "12.12.12.12:335" + - "13.13.13.13:445" + route_origin: + - "4301:4301" + - "501:501" + - name: test_new_expanded + type: expanded + permit: false + match: any + members: + regex: + - 22 + - 23 + - 24 + - name: overridden_test_case_02 + description: Override2 created BGP properties + state: overridden + input: + - name: test_new_expanded2 + type: expanded + permit: true + members: + regex: + - 33 + - "44" + - name: test_new_std + type: standard + permit: true + match: all + members: + route_target: + - "13.13.13.13:445" + route_origin: + - "501:501" + - name: test_new_expanded + type: expanded + permit: false + match: any + members: + regex: + - 22 + - 23 + - 24 + - name: overridden_test_case_03 + description: Override3 created BGP properties + state: overridden + input: + - name: test_new_std + type: standard + permit: true + match: all + members: + route_target: + - "13.13.13.13:445" + route_origin: + - "501:501" + - name: test_new_expanded + type: expanded + permit: false + match: any + members: + regex: + - 22 + - 23 + - 24 + - name: overridden_test_case_04 + description: Override4 created BGP properties + state: overridden + input: + - name: test_new_std + type: standard + permit: true + match: all + members: + route_origin: + - "502:502" + +deleted_all: - name: del_test_case_04 description: Update created BGP properties state: deleted diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main copy.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main copy.yml deleted file mode 100644 index 353861255..000000000 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main copy.yml +++ /dev/null @@ -1,316 +0,0 @@ ---- -ansible_connection: httpapi -module_name: sonic_bgp_neighbors - -bgp_as_1: 51 -bgp_as_2: 52 - -vrf_1: VrfReg1 -vrf_2: VrfReg2 - -preparations_tests: - init_route_map: - - route-map rmap_reg1 permit 11 - - route-map rmap_reg2 permit 11 - - route-map rmap_reg3 permit 11 - - route-map rmap_reg4 permit 11 - - route-map rmap_reg5 permit 11 - init_vrf: - - "ip vrf {{vrf_1}}" - - "ip vrf {{vrf_2}}" - init_bgp: - - bgp_as: "{{bgp_as_1}}" - router_id: 111.2.2.41 - log_neighbor_changes: False - - bgp_as: "{{bgp_as_2}}" - router_id: 111.2.2.42 - log_neighbor_changes: True - vrf_name: VrfReg1 - -negative_tests: - - name: negative_test_case_01 - description: allowas_in beyond value - state: merged - input: - - bgp_as: "{{bgp_as_1}}" - neighbors: - - neighbor: Ethernet12 - address_family: - - afi: ipv4 - safi: unicast - allowas_in: - value: 11 - route_map: - - name: rmap_reg1 - direction: in - - name: rmap_reg1 - direction: out - route_reflector_client: true - route_server_client: true - - name: negative_test_case_02 - description: BGP properties - state: merged - input: - - bgp_as: "{{bgp_as_1}}" - neighbors: - - neighbor: Ethernet12 - address_family: - - afi: ipv4 - safi: unicast - allowas_in: - value: 11 - route_map: - - name: rmap_reg1 - direction: in - - name: rmap_reg2 - direction: in - - name: rmap_reg1 - direction: out - route_reflector_client: true - route_server_client: true - -tests: "{{ merged_tests }}" - -deleted_tests: - - name: test_case_del_01 - description: Delete peer group BGP properties - state: deleted - input: - - bgp_as: "{{bgp_as_1}}" - peergroup: - - name: SPINE - - name: SPINE1 - - bgp_as: "{{bgp_as_1}}" - vrf_name: "{{vrf_1}}" - peergroup: - - name: SPINE - - name: SPINE1 - - - -merged_tests: - - name: test_case_01 - description: BGP properties - state: merged - input: - - bgp_as: "{{bgp_as_1}}" - peergroup: - - name: SPINE - neighbors: - - neighbor: Ethernet12 - remote_as: 5 - peer_group: SPINE - advertisement_interval: 10 - timers: - keepalive: 40 - holdtime: 50 - bfd: true - capability: - dynamic: true - extended_nexthop: true - - neighbor: 192.168.1.4 - - neighbor: 2::2 - - neighbor: Ethernet8 - - neighbor: 192.168.1.5 - remote_as: 6 - peer_group: SPINE - advertisement_interval: 20 - timers: - keepalive: 30 - holdtime: 20 - capability: - dynamic: true - - neighbor: 3::3 - remote_as: 7 - peer_group: SPINE - advertisement_interval: 20 - timers: - keepalive: 30 - holdtime: 20 - capability: - dynamic: true - - bgp_as: "{{bgp_as_1}}" - vrf_name: "{{vrf_1}}" - peergroup: - - name: SPINE - neighbors: - - neighbor: Ethernet24 - remote_as: 11 - peer_group: SPINE - advertisement_interval: 10 - timers: - keepalive: 40 - holdtime: 50 - bfd: true - capability: - dynamic: true - extended_nexthop: true - - neighbor: 192.168.2.2 - - neighbor: Ethernet28 - remote_as: 12 - peer_group: SPINE - advertisement_interval: 20 - timers: - keepalive: 30 - holdtime: 20 - capability: - dynamic: true - - name: test_case_02 - description: Update BGP properties - state: merged - input: - - bgp_as: "{{bgp_as_1}}" - peergroup: - - name: SPINE - - name: SPINE1 - neighbors: - - neighbor: Ethernet12 - remote_as: 111 - peer_group: SPINE - advertisement_interval: 11 - timers: - keepalive: 41 - holdtime: 51 - bfd: false - capability: - dynamic: false - extended_nexthop: false - - neighbor: 192.168.1.4 - - neighbor: Ethernet8 - - neighbor: 192.168.1.5 - remote_as: 112 - peer_group: SPINE1 - advertisement_interval: 21 - timers: - keepalive: 22 - holdtime: 23 - capability: - dynamic: true - - bgp_as: "{{bgp_as_1}}" - vrf_name: "{{vrf_1}}" - peergroup: - - name: SPINE - - name: SPINE1 - neighbors: - - neighbor: Ethernet24 - remote_as: 213 - peer_group: SPINE1 - advertisement_interval: 44 - timers: - keepalive: 55 - holdtime: 44 - bfd: false - capability: - dynamic: false - extended_nexthop: false - - neighbor: 192.168.2.2 - - neighbor: Ethernet28 - remote_as: 214 - peer_group: SPINE - advertisement_interval: 45 - timers: - keepalive: 33 - holdtime: 34 - capability: - dynamic: false - - neighbor: 3::3 - remote_as: 215 - peer_group: SPINE - advertisement_interval: 20 - timers: - keepalive: 30 - holdtime: 20 - capability: - dynamic: true - - name: test_case_03 - description: BGP ipv6 properties - state: merged - input: - - bgp_as: "{{bgp_as_1}}" - peergroup: - - name: SPINE - neighbors: - - neighbor: 2::2 - - neighbor: 11::11 - remote_as: external - peer_group: SPINE - advertisement_interval: 20 - timers: - keepalive: 30 - holdtime: 20 - capability: - dynamic: true - - neighbor: 3::3 - remote_as: 7 - peer_group: SPINE - advertisement_interval: 20 - timers: - keepalive: 30 - holdtime: 20 - capability: - dynamic: true - - bgp_as: "{{bgp_as_1}}" - vrf_name: "{{vrf_1}}" - peergroup: - - name: SPINE - neighbors: - - neighbor: 192.168.2.2 - - neighbor: Ethernet28 - remote_as: 12 - peer_group: SPINE - advertisement_interval: 20 - timers: - keepalive: 30 - holdtime: 20 - capability: - dynamic: true - - name: test_case_04 - description: BGP remote-as properties - state: merged - input: - - bgp_as: "{{bgp_as_1}}" - peergroup: - - name: SPINE - neighbors: - - neighbor: Ethernet8 - remote_as: internal - - neighbor: 11::11 - remote_as: external - - neighbor: 67.1.1.1 - remote_as: 7 - - bgp_as: "{{bgp_as_1}}" - vrf_name: "{{vrf_1}}" - peergroup: - - name: SPINE - neighbors: - - neighbor: Ethernet8 - remote_as: 1345 - - neighbor: 11::11 - remote_as: 2345 - - neighbor: 67.1.1.1 - remote_as: external - - name: test_case_05 - description: BGP remote-as properties - state: merged - input: - - bgp_as: "{{bgp_as_1}}" - peergroup: - - name: SPINE - neighbors: - - neighbor: Ethernet8 - remote_as: external - - neighbor: 11::11 - remote_as: internal - - neighbor: 67.1.1.1 - remote_as: internal - - bgp_as: "{{bgp_as_1}}" - vrf_name: "{{vrf_1}}" - peergroup: - - name: SPINE - neighbors: - - neighbor: Ethernet8 - remote_as: internal - - neighbor: 11::11 - remote_as: external - - neighbor: 67.1.1.1 - remote_as: 1123
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main.yml index 140eeeae5..dcc2b657f 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main.yml @@ -116,10 +116,6 @@ deleted_tests: prefix_list_out: p2 - afi: l2vpn safi: evpn - prefix_limit: - max_prefixes: 4 - warning_threshold: 66 - restart_timer: 15 prefix_list_in: p2 prefix_list_out: p1 @@ -213,7 +209,7 @@ deleted_tests: - neighbor: 3::3 peer_group: SPINE - neighbor: 192.168.1.5 - peer_group: SPINE1 + peer_group: SPINE - bgp_as: "{{bgp_as_1}}" vrf_name: "{{vrf_1}}" peer_group: @@ -230,7 +226,7 @@ deleted_tests: - neighbor: "{{ interface4 }}" peer_group: SPINE - neighbor: "{{ interface3 }}" - peer_group: SPINE1 + peer_group: SPINE bfd: enabled: false check_failure: false @@ -468,7 +464,6 @@ merged_tests: - neighbor: 192.168.1.5 remote_as: peer_as: 112 - peer_group: SPINE1 advertisement_interval: 21 timers: keepalive: 22 @@ -509,7 +504,6 @@ merged_tests: - neighbor: "{{ interface3 }}" remote_as: peer_as: 212 - peer_group: SPINE1 advertisement_interval: 44 timers: keepalive: 55 @@ -832,10 +826,6 @@ merged_tests: prefix_list_out: p1 - afi: l2vpn safi: evpn - prefix_limit: - max_prefixes: 3 - warning_threshold: 60 - restart_timer: 8 prefix_list_in: p1 prefix_list_out: p2 - name: test_case_09 @@ -872,9 +862,5 @@ merged_tests: prefix_list_out: p2 - afi: l2vpn safi: evpn - prefix_limit: - max_prefixes: 4 - warning_threshold: 66 - restart_timer: 15 prefix_list_in: p2 prefix_list_out: p1 diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/defaults/main.yml index dcb7b46e5..30b16bc41 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/defaults/main.yml @@ -382,10 +382,6 @@ tests: prefix_list_out: p1 - afi: l2vpn safi: evpn - prefix_limit: - max_prefixes: 3 - warning_threshold: 60 - restart_timer: 8 prefix_list_in: p1 prefix_list_out: p2 - name: test_case_06 @@ -421,10 +417,6 @@ tests: prefix_list_out: p2 - afi: l2vpn safi: evpn - prefix_limit: - max_prefixes: 4 - warning_threshold: 66 - restart_timer: 15 prefix_list_in: p2 prefix_list_out: p1 - name: test_case_07 @@ -460,9 +452,5 @@ tests: prefix_list_out: p2 - afi: l2vpn safi: evpn - prefix_limit: - max_prefixes: 4 - warning_threshold: 66 - restart_timer: 15 prefix_list_in: p2 prefix_list_out: p1 diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/defaults/main.yml new file mode 100644 index 000000000..77ef90f0e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/defaults/main.yml @@ -0,0 +1,80 @@ +--- +ansible_connection: httpapi +module_name: copp + +tests: + - name: test_case_01 + description: Merge CoPP groups configuration + state: merged + input: + copp_groups: + - copp_name: 'copp-1' + trap_priority: 1 + trap_action: 'DROP' + queue: 1 + cir: '45' + cbs: '45' + - copp_name: 'copp-2' + trap_priority: 2 + trap_action: 'FORWARD' + queue: 2 + cir: '90' + cbs: '90' + - name: test_case_02 + description: Update CoPP groups configuration + state: merged + input: + copp_groups: + - copp_name: 'copp-1' + trap_priority: 8 + trap_action: 'FORWARD' + queue: 8 + cir: '20' + cbs: '20' + - name: test_case_03 + description: Replace CoPP groups configuration + state: replaced + input: + copp_groups: + - copp_name: 'copp-2' + trap_action: 'FORWARD' + cir: '60' + cbs: '60' + - copp_name: 'copp-3' + trap_priority: 3 + trap_action: 'DROP' + queue: 3 + cir: '70' + cbs: '70' + - copp_name: 'copp-4' + trap_priority: 5 + trap_action: 'DROP' + queue: 5 + cir: '75' + cbs: '75' + - name: test_case_04 + description: Delete CoPP groups configuration + state: deleted + input: + copp_groups: + - copp_name: 'copp-1' + trap_priority: 8 + queue: 8 + cir: '20' + cbs: '20' + - copp_name: 'copp-2' + - name: test_case_05 + description: Overridden CoPP groups configuration + state: overridden + input: + copp_groups: + - copp_name: 'copp-5' + trap_priority: 1 + trap_action: 'FORWARD' + queue: 1 + cir: '15' + cbs: '15' + - name: test_case_06 + description: Delete all CoPP groups + state: deleted + input: {} diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/meta/main.yaml new file mode 100644 index 000000000..0b356217e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/meta/main.yaml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common } diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/tasks/main.yml new file mode 100644 index 000000000..d7c2bfb11 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/tasks/main.yml @@ -0,0 +1,11 @@ +- debug: msg="sonic_copp Test started ..." + +- set_fact: + base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}" + +- name: Preparations test + include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started ..." + include_tasks: tasks_template.yaml + loop: "{{ tests }}" diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/tasks/preparation_tests.yaml new file mode 100644 index 000000000..2c01c9c08 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/tasks/preparation_tests.yaml @@ -0,0 +1,5 @@ +- name: Delete old CoPP groups configuration + sonic_copp: + config: {} + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/tasks/tasks_template.yaml new file mode 100644 index 000000000..cd5e02ef3 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_copp/tasks/tasks_template.yaml @@ -0,0 +1,21 @@ +- name: "{{ item.name}} , {{ item.description}}" + sonic_copp: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name}} , {{ item.description}} Idempotent" + sonic_copp: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/defaults/main.yml new file mode 100644 index 000000000..ab5c73af7 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/defaults/main.yml @@ -0,0 +1,350 @@ +--- +ansible_connection: httpapi +module_name: dhcp_relay + +vrf1: "VrfReg1" +vrf2: "VrfReg2" + +po1: "Portchannel 100" +po2: "Portchannel 101" + +vlan1: "Vlan 100" +vlan2: "Vlan 101" + +preparations_tests: + vrfs: + - name: '{{ vrf1 }}' + - name: '{{ vrf2 }}' + lag_interfaces: + - name: '{{ po1 }}' + - name: '{{ po2 }}' + vlans: + - vlan_id: 100 + - vlan_id: 101 + l3_interfaces: + - name: '{{ interface1 }}' + ipv4: + addresses: + - address: 100.1.1.1/24 + ipv6: + addresses: + - address: 100::1/122 + - name: '{{ interface2 }}' + ipv4: + addresses: + - address: 101.1.1.1/24 + ipv6: + addresses: + - address: 101::1/122 + - name: '{{ vlan1 }}' + ipv4: + addresses: + - address: 110.1.1.1/24 + ipv6: + addresses: + - address: 110::1/122 + - name: '{{ vlan2 }}' + ipv4: + addresses: + - address: 111.1.1.1/24 + ipv6: + addresses: + - address: 111::1/122 + - name: '{{ po1 }}' + ipv4: + addresses: + - address: 120.1.1.1/24 + ipv6: + addresses: + - address: 120::1/122 + - name: '{{ po2 }}' + ipv4: + addresses: + - address: 121.1.1.1/24 + ipv6: + addresses: + - address: 121::1/122 + +tests: + - name: test_case_01 + description: Add DHCP and DHCPv6 relay configuration + state: merged + input: + - name: '{{ interface1 }}' + ipv4: + server_addresses: + - address: 100.1.1.2 + - address: 100.1.1.3 + vrf_name: '{{ vrf1 }}' + vrf_select: true + source_interface: '{{ vlan2 }}' + link_select: true + max_hop_count: 8 + policy_action: 'replace' + ipv6: + server_addresses: + - address: 100::2 + - address: 100::3 + vrf_name: '{{ vrf2 }}' + vrf_select: true + source_interface: '{{ vlan2 }}' + max_hop_count: 8 + + - name: test_case_02 + description: Update DHCP and DHCPv6 relay configuration + state: merged + input: + - name: '{{ interface1 }}' + ipv4: + server_addresses: + - address: 100.1.1.4 + circuit_id: '%h:%p' + policy_action: 'discard' + vrf_select: false + ipv6: + vrf_select: false + - name: '{{ interface2 }}' + ipv4: + server_addresses: + - address: 101.1.1.2 + circuit_id: '%h:%p' + max_hop_count: 8 + ipv6: + server_addresses: + - address: 101::2 + - address: 101::3 + max_hop_count: 8 + + - name: test_case_03 + description: Update DHCP and DHCPv6 relay configuration + state: merged + input: + - name: '{{ po1 }}' + ipv4: + server_addresses: + - address: 120.1.1.2 + - address: 120.1.1.3 + source_interface: '{{ vlan2 }}' + link_select: false + circuit_id: '%p' + max_hop_count: 8 + - name: '{{ po2 }}' + ipv6: + server_addresses: + - address: 121::2 + - address: 121::3 + source_interface: '{{ vlan2 }}' + max_hop_count: 8 + + - name: test_case_04 + description: Update DHCP and DHCPv6 relay configuration + state: merged + input: + - name: '{{ po1 }}' + ipv4: + link_select: true + circuit_id: '%i' + max_hop_count: 6 + ipv6: + server_addresses: + - address: 120::2 + - name: '{{ po2 }}' + ipv4: + server_addresses: + - address: 121.1.1.2 + - name: '{{ vlan1 }}' + ipv4: + server_addresses: + - address: 110.1.1.2 + - address: 110.1.1.3 + - address: 110.1.1.4 + - address: 110.1.1.5 + circuit_id: '%i' + policy_action: 'append' + + - name: test_case_05 + description: Delete specific DHCP and DHCPv6 relay configurations + state: deleted + input: + - name: '{{ interface1 }}' + ipv4: + server_addresses: + - address: 100.1.1.2 + source_interface: '{{ vlan2 }}' + link_select: true + ipv6: + server_addresses: + - address: 100::2 + source_interface: '{{ vlan2 }}' + - name: '{{ interface2 }}' + ipv4: + circuit_id: '%h:%p' + max_hop_count: 8 + ipv6: + server_addresses: + - address: 101::3 + max_hop_count: 8 + - name: '{{ vlan1 }}' + ipv4: + policy_action: 'append' + + - name: test_case_06 + description: Delete all DHCP relay configuration for specified interface by deleting all DHCP server addresses + state: deleted + input: + - name: '{{ vlan1 }}' + ipv4: + server_addresses: + - address: 110.1.1.2 + - address: 110.1.1.3 + - address: 110.1.1.4 + - address: 110.1.1.5 + + - name: test_case_07 + description: Delete all DHCP relay configuration for specified interface by specifying only an empty DHCP server address + state: deleted + input: + - name: '{{ interface1 }}' + ipv4: + server_addresses: + - address: + + - name: test_case_08 + description: Delete all DHCPv6 relay configuration for specified interface by specifying only an empty DHCPv6 server address + state: deleted + input: + - name: '{{ interface2 }}' + ipv6: + server_addresses: + - address: + + - name: test_case_09 + description: Delete all DHCP and DHCPv6 relay configuration for specified interface + state: deleted + input: + - name: '{{ po1 }}' + - name: '{{ po2 }}' + + - name: test_case_10 + description: Add DHCP and DHCPv6 relay configuration for replace + state: merged + input: + - name: '{{ interface1 }}' + ipv4: + server_addresses: + - address: 100.1.1.2 + - address: 100.1.1.3 + vrf_name: '{{ vrf1 }}' + vrf_select: true + source_interface: '{{ vlan2 }}' + link_select: true + max_hop_count: 8 + policy_action: 'replace' + ipv6: + server_addresses: + - address: 100::2 + - address: 100::3 + vrf_name: '{{ vrf2 }}' + vrf_select: true + source_interface: '{{ vlan2 }}' + max_hop_count: 8 + - name: '{{ interface2 }}' + ipv4: + server_addresses: + - address: 101.1.1.2 + - address: 101.1.1.3 + vrf_name: '{{ vrf1 }}' + circuit_id: '%h:%p' + max_hop_count: 8 + ipv6: + server_addresses: + - address: 101::2 + - address: 101::3 + vrf_name: '{{ vrf2 }}' + max_hop_count: 8 + - name: '{{ po1 }}' + ipv4: + server_addresses: + - address: 120.1.1.2 + - address: 120.1.1.3 + source_interface: '{{ vlan2 }}' + link_select: false + circuit_id: '%p' + max_hop_count: 8 + + - name: test_case_11 + description: Replace DHCP and DHCPv6 relay configurations + state: replaced + input: + - name: '{{ interface1 }}' + ipv4: + server_addresses: + - address: 100.1.1.2 + - address: 100.1.1.3 + vrf_name: '{{ vrf2 }}' + vrf_select: true + source_interface: '{{ vlan2 }}' + max_hop_count: 8 + policy_action: 'append' + ipv6: + server_addresses: + - address: 100::2 + - address: 100::4 + - address: 100::6 + vrf_name: '{{ vrf2 }}' + vrf_select: true + - name: '{{ interface2 }}' + ipv4: + server_addresses: + - address: 101.1.1.2 + - address: 101.1.1.4 + - address: 101.1.1.6 + circuit_id: '%h:%p' + max_hop_count: 8 + ipv6: + server_addresses: + - address: 101::2 + - address: 101::3 + max_hop_count: 8 + - name: '{{ po2 }}' + ipv4: + server_addresses: + - address: 120.1.1.2 + - address: 120.1.1.3 + source_interface: '{{ vlan2 }}' + link_select: false + circuit_id: '%p' + max_hop_count: 8 + + - name: test_case_12 + description: Override DHCP and DHCPv6 relay configurations + state: overridden + input: + - name: '{{ interface1 }}' + ipv4: + server_addresses: + - address: 100.1.1.10 + - address: 100.1.1.11 + vrf_name: '{{ vrf2 }}' + vrf_select: true + source_interface: '{{ vlan2 }}' + max_hop_count: 12 + policy_action: 'replace' + - name: '{{ interface2 }}' + ipv6: + server_addresses: + - address: 101::20 + - address: 101::30 + max_hop_count: 8 + - name: '{{ po2 }}' + ipv6: + server_addresses: + - address: 121::2 + - address: 121::3 + source_interface: '{{ vlan2 }}' + max_hop_count: 8 + + - name: test_case_13 + description: Delete all DHCP and DHCPv6 relay configurations + state: deleted + input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/meta/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/meta/main.yml new file mode 100644 index 000000000..d0ceaf6f5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/meta/main.yml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common } diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..b21a7a4b9 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/tasks/cleanup_tests.yaml @@ -0,0 +1,30 @@ +--- +- name: Delete DHCP, DHCPv6 relay configurations + dellemc.enterprise_sonic.sonic_dhcp_relay: + config: [] + state: deleted + ignore_errors: yes + +- name: Delete l3 configurations on test interfaces + dellemc.enterprise_sonic.sonic_l3_interfaces: + config: "{{ preparations_tests.l3_interfaces }}" + state: deleted + ignore_errors: yes + +- name: Delete test VRFs + dellemc.enterprise_sonic.sonic_vrfs: + config: "{{ preparations_tests.vrfs }}" + state: deleted + ignore_errors: yes + +- name: Delete test VLANs + dellemc.enterprise_sonic.sonic_vlans: + config: "{{ preparations_tests.vlans }}" + state: deleted + ignore_errors: yes + +- name: Delete test lag interfaces + dellemc.enterprise_sonic.sonic_lag_interfaces: + config: "{{ preparations_tests.lag_interfaces }}" + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/tasks/main.yml new file mode 100644 index 000000000..063a33d65 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/tasks/main.yml @@ -0,0 +1,17 @@ +--- +- ansible.builtin.debug: + msg: "sonic_dhcp_relay Test started ..." + +- name: "Preparations for {{ module_name }}" + ansible.builtin.include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started" + ansible.builtin.include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: "Cleanup of {{ module_name }}" + ansible.builtin.include_tasks: cleanup_tests.yaml + +- name: Display all variables/facts known for a host + ansible.builtin.debug: + var: hostvars[inventory_hostname].ansible_facts.test_reports diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/tasks/preparation_tests.yaml new file mode 100644 index 000000000..5101b7f4c --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/tasks/preparation_tests.yaml @@ -0,0 +1,38 @@ +--- +- name: Delete old DHCP, DHCPv6 relay configurations + dellemc.enterprise_sonic.sonic_dhcp_relay: + config: [] + state: deleted + ignore_errors: yes + +- name: Initialize default interfaces + vars: + ansible_connection: network_cli + dellemc.enterprise_sonic.sonic_config: + commands: "{{ default_interface_cli }}" + register: output + ignore_errors: yes + +- name: Create VRFs + dellemc.enterprise_sonic.sonic_vrfs: + config: "{{ preparations_tests.vrfs }}" + state: merged + ignore_errors: yes + +- name: Create lag interfaces + dellemc.enterprise_sonic.sonic_lag_interfaces: + config: "{{ preparations_tests.lag_interfaces }}" + state: merged + ignore_errors: yes + +- name: Create VLANs + dellemc.enterprise_sonic.sonic_vlans: + config: "{{ preparations_tests.vlans }}" + state: merged + ignore_errors: yes + +- name: Configure l3 interfaces + dellemc.enterprise_sonic.sonic_l3_interfaces: + config: "{{ preparations_tests.l3_interfaces }}" + state: merged + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/tasks/tasks_template.yaml new file mode 100644 index 000000000..c4a91213e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_relay/tasks/tasks_template.yaml @@ -0,0 +1,22 @@ +--- +- name: "{{ item.name }} , {{ item.description }}" + dellemc.enterprise_sonic.sonic_dhcp_relay: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name }} , {{ item.description }} Idempotent" + dellemc.enterprise_sonic.sonic_dhcp_relay: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/defaults/main.yml new file mode 100644 index 000000000..bf3e70d0e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/defaults/main.yml @@ -0,0 +1,133 @@ +--- +ansible_connection: httpapi +module_name: sonic_dhcp_snooping + +preparations_tests: + init: + - 'interface vlan 1' + - 'interface vlan 2' + - 'interface vlan 3' + - 'interface vlan 4' + - 'interface portchannel 1' + - 'interface portchannel 2' + - 'interface portchannel 3' + cleanup: + - 'no interface vlan 1' + - 'no interface vlan 2' + - 'no interface vlan 3' + - 'no interface vlan 4' + - 'no interface portchannel 1' + - 'no interface portchannel 2' + - 'no interface portchannel 3' + +tests: + - name: test_case_01_merge_add + description: Add DHCPv4 and DHCPv6 snooping configuration + state: merged + input: + afis: + - afi: 'ipv4' + enabled: true + vlans: + - '1' + trusted: + - intf_name: 'Ethernet1' + source_bindings: + - mac_addr: "12:12:12:12:12:12" + ip_addr: "2.2.2.2" + vlan_id: 1 + intf_name: "Ethernet3" + - afi: 'ipv6' + enabled: true + verify_mac: false + vlans: + - '2' + - '3' + trusted: + - intf_name: 'Ethernet2' + - intf_name: 'PortChannel1' + - name: test_case_02_merge_update + description: Update DHCPv4 and DHCPv6 snooping configuration + state: merged + input: + afis: + - afi: 'ipv4' + trusted: + - intf_name: 'Ethernet2' + source_bindings: + - mac_addr: "12:12:12:12:12:12" + ip_addr: "2.2.2.2" + vlan_id: 2 + intf_name: 'Ethernet3' + - mac_addr: "14:14:14:14:14:14" + ip_addr: "4.4.4.4" + vlan_id: 4 + intf_name: 'Ethernet4' + - afi: 'ipv6' + vlans: + - '2' + - '3' + - '4' + trusted: + - intf_name: 'PortChannel3' + - name: test_case_03_override + description: Override DHCPv4 and DHCPv6 snooping configuration + state: overridden + input: + afis: + - afi: 'ipv4' + enabled: true + vlans: ['1', '2'] + source_bindings: + - mac_addr: "12:12:12:12:12:12" + ip_addr: "3.3.3.3" + vlan_id: 2 + intf_name: 'Ethernet3' + trusted: + - intf_name: 'Ethernet1' + - intf_name: 'Ethernet2' + - afi: 'ipv6' + verify_mac: true + source_bindings: + - mac_addr: "12:12:12:12:12:12" + ip_addr: "2002::2" + vlan_id: 3 + intf_name: 'Ethernet3' + trusted: + - intf_name: 'Ethernet1' + - name: test_case_04_replace + description: Replace DHCPv4 and DHCPv6 snooping configuration + state: replaced + input: + afis: + - afi: 'ipv4' + verify_mac: false + vlans: ['3'] + trusted: + - intf_name: 'Ethernet1' + - intf_name: 'Ethernet2' + - afi: 'ipv6' + vlans: ['1', '4'] + trusted: + - intf_name: 'PortChannel1' + - intf_name: 'PortChannel2' + enabled: true + - name: test_case_05_delete_afi + description: Delete DHCP snooping configuration for specific AFI + state: deleted + input: + afis: + - afi: 'ipv6' + - name: test_case_06_delete_subsettings + description: Delete DHCP snooping configuration for each section + state: deleted + input: + afis: + - afi: 'ipv4' + source_bindings: [] + trusted: [] + vlans: [] + - name: test_case_07_delete_all + description: Delete all DHCP snooping configuration + state: deleted + input: {}
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/meta/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/meta/main.yml new file mode 100644 index 000000000..d0ceaf6f5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/meta/main.yml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common } diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..756783cef --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/tasks/cleanup_tests.yaml @@ -0,0 +1,7 @@ +--- +- name: "cleanup dhcp test environment" + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparations_tests.cleanup }}" + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/tasks/main.yml new file mode 100644 index 000000000..bbd8846fc --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/tasks/main.yml @@ -0,0 +1,17 @@ +--- +- ansible.builtin.debug: + msg: "sonic_dhcp_snooping Test started ..." + +- name: "Preparations for {{ module_name }}" + ansible.builtin.include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started" + ansible.builtin.include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: "Cleanup of {{ module_name }}" + ansible.builtin.include_tasks: cleanup_tests.yaml + +- name: Display all variables/facts known for a host + ansible.builtin.debug: + var: hostvars[inventory_hostname].ansible_facts.test_reports diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/tasks/preparation_tests.yaml new file mode 100644 index 000000000..32038e703 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/tasks/preparation_tests.yaml @@ -0,0 +1,12 @@ +--- +- name: "initialize dhcp test environment" + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparations_tests.init }}" + +- name: Delete old DHCP, DHCPv6 snooping configurations + dellemc.enterprise_sonic.sonic_dhcp_snooping: + config: {} + state: deleted + # ignore_errors: yes
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/tasks/tasks_template.yaml new file mode 100644 index 000000000..9b1339dc9 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_dhcp_snooping/tasks/tasks_template.yaml @@ -0,0 +1,22 @@ +--- +- name: "{{ item.name}} , {{ item.description}}" + sonic_dhcp_snooping: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name}} , {{ item.description}} Idempotent" + sonic_dhcp_snooping: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/defaults/main.yml index d391e690d..7bb6dc33f 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/defaults/main.yml @@ -29,73 +29,94 @@ tests: description: Update interface parameters state: merged input: - - name: "{{ interface1 }}" - description: ansible Ethernet4 descr + - name: "{{ interface7 }}" + description: ansible Ethernet96 mtu: 6445 enabled: false - name: test_case_02 description: Update interface parameters state: merged input: - - name: "{{ interface1 }}" - description: ansible Ethernet4 descr + - name: "{{ interface7 }}" + description: ansible Ethernet96 mtu: 6444 enabled: true - - name: "{{ interface3 }}" - description: ansible Ethernet12 descr + - name: "{{ interface8 }}" + description: ansible Ethernet100 mtu: 6000 enabled: true - - name: "{{ interface2 }}" - description: ansible Ethernet8 descr + - name: "{{ interface10 }}" + description: ansible Ethernet108 mtu: 5666 enabled: false - - name: "{{ interface4 }}" - description: ansible Ethernet16 descr + - name: "{{ interface9 }}" + description: ansible Ethernet104 mtu: 5222 enabled: true - name: test_case_03 - description: Update interface parameters + description: Configure interface parameter speed + state: merged + input: + - name: "{{ interface7 }}" + speed: SPEED_40GB + - name: test_case_04 + description: Configure interface parameters auto_negotiate and advertised_speed + state: merged + input: + - name: "{{ interface10 }}" + auto_negotiate: true + advertised_speed: + - 100000 + - 40000 + - name: test_case_05 + description: Configure interface parameters FEC + state: merged + input: + - name: "{{ interface8 }}" + fec: FEC_AUTO + - name: test_case_06 + description: Delete interface parameters state: deleted input: - - name: "{{ interface1 }}" + - name: "{{ interface7 }}" description: - - name: "{{ interface3 }}" - mtu: - - name: "{{ interface2 }}" + - name: "{{ interface8 }}" + mtu: 6000 + - name: "{{ interface10 }}" enabled: - - name: "{{ interface4 }}" - - name: test_case_04 + - name: "{{ interface9 }}" + - name: test_case_07 description: Update interface parameters state: merged input: - - name: "{{ interface1 }}" - description: ansible Ethernet4 descr + - name: "{{ interface7 }}" + description: ansible Ethernet96 mtu: 6444 enabled: true - - name: "{{ interface3 }}" - description: ansible Ethernet12 descr - - name: "{{ interface4 }}" - description: ansible eth56 descr + - name: "{{ interface8 }}" + description: ansible Ethernet100 + - name: "{{ interface9 }}" + description: ansible eth60 # Loopback test cases started - - name: test_case_05 + - name: test_case_08 description: Loopback interface parameters state: merged input: - name: "{{ lo1 }}" # Loopback: mtu, desc, enabled not configurable in sonic-os - name: "{{ lo2 }}" # Loopback: mtu, desc, enabled not configurable in sonic-os - - name: test_case_06 + - name: test_case_09 description: Loopback delete interface parameters state: deleted input: - name: "{{ lo1 }}" # Loopback: mtu, desc, enabled not configurable in sonic-os - - name: test_case_07 + - name: test_case_10 description: Loopback delete interface parameters state: deleted input: - name: "{{ lo1 }}" # Loopback: mtu, desc, enabled not configurable in sonic-os - name: "{{ lo2 }}" # Loopback: mtu, desc, enabled not configurable in sonic-os # Vlan test cases started - - name: test_case_08 + - name: test_case_11 description: Update interface parameters state: merged input: @@ -103,14 +124,13 @@ tests: mtu: 5000 - name: "Vlan{{ vlan2 }}" mtu: 5001 - - name: test_case_09 - description: Update interface parameters + - name: test_case_12 + description: Delete interface parameters state: deleted input: - name: "Vlan{{ vlan1 }}" - mtu: - - name: "Vlan{{ vlan2 }}" - - name: test_case_10 + mtu: 5000 + - name: test_case_13 description: Update interface parameters state: merged input: @@ -119,7 +139,7 @@ tests: - name: "Vlan{{ vlan2 }}" mtu: 5113 # Portchannel testcase started - - name: test_case_12 + - name: test_case_14 description: Update interface parameters state: merged input: @@ -130,13 +150,16 @@ tests: description: ansible PortChannel51 descr mtu: 5454 enabled: true - - name: test_case_13 - description: Update interface parameters + - name: test_case_15 + description: Delete interface parameters state: deleted input: - name: "{{ po1 }}" + mtu: 3434 + enabled: true - name: "{{ po2 }}" - - name: test_case_14 + mtu: 5454 + - name: test_case_16 description: Update interface parameters state: merged input: @@ -147,7 +170,60 @@ tests: description: ansible PortChannel51 descr mtu: 5454 enabled: true - - name: test_case_15 - description: Update interface parameters - state: deleted - input: [] + - name: test_case_17 + description: Update interface parameters descr and mtu + state: merged + input: + - name: "{{ interface7 }}" + description: Ansible Interface1 descr + mtu: 6500 + enabled: true + - name: "{{ interface9 }}" + description: ansible Interface2 descr + mtu: 7500 + enabled: true + - name: test_case_18 + description: Replace interface mtu parameters + state: replaced + input: + - name: "{{ interface7 }}" + mtu: 3300 + auto_negotiate: true + advertised_speed: + - 100000 + - 40000 + - name: "{{ interface9 }}" + mtu: 3300 + auto_negotiate: true + advertised_speed: + - 100000 + - name: test_case_19 + description: Replace interface parameters + state: replaced + input: + - name: "{{ interface7 }}" + mtu: 3500 + auto_negotiate: true + advertised_speed: + - 40000 + - name: "{{ interface9 }}" + description: Ansible Interface2 + mtu: 3500 + auto_negotiate: true + advertised_speed: + - 100000 + - 40000 + - name: test_case_20 + description: Replace interface parameters + state: replaced + input: + - name: "{{ interface9 }}" + description: Ansible Interface2 + - name: test_case_21 + description: Override interface parameters + state: overridden + input: + - name: "{{ interface7 }}" + description: Interface1 + mtu: 3300 + enabled: true diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/tasks_template.yaml index debf1a6ce..4b928adc7 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/tasks_template.yaml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/tasks_template.yaml @@ -18,4 +18,4 @@ - import_role: name: common - tasks_from: idempotent.facts.report.yaml
\ No newline at end of file + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ip_neighbor/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ip_neighbor/defaults/main.yml new file mode 100644 index 000000000..b4d67019a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ip_neighbor/defaults/main.yml @@ -0,0 +1,41 @@ +--- +ansible_connection: httpapi +module_name: ip_neighbor + +tests: + - name: test_case_01 + description: Configure some IP neighbor global parameters + state: merged + input: + ipv4_arp_timeout: 1200 + ipv4_drop_neighbor_aging_time: 600 + - name: test_case_02 + description: Configure some IP neighbor global parameters + state: merged + input: + ipv6_drop_neighbor_aging_time: 600 + ipv6_nd_cache_expiry: 1200 + num_local_neigh: 1000 + - name: test_case_03 + description: Delete some IP neighbor global parameters + state: deleted + input: + ipv4_arp_timeout: 0 + ipv4_drop_neighbor_aging_time: 0 + - name: test_case_04 + description: Change some IP neighbor global parameters + state: replaced + input: + ipv4_drop_neighbor_aging_time: 800 + ipv6_drop_neighbor_aging_time: 800 + - name: test_case_05 + description: Reset IP neighbor global configuration, and configure some + state: overridden + input: + ipv4_arp_timeout: 1800 + ipv4_drop_neighbor_aging_time: 800 + num_local_neigh: 1100 + - name: test_case_06 + description: Clean up - reset all IP neighbor global configuration + state: deleted + input: {} diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ip_neighbor/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ip_neighbor/meta/main.yaml new file mode 100644 index 000000000..611fd54d2 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ip_neighbor/meta/main.yaml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common }
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ip_neighbor/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ip_neighbor/tasks/main.yml new file mode 100644 index 000000000..9ed8dc1bf --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ip_neighbor/tasks/main.yml @@ -0,0 +1,10 @@ +- debug: msg="sonic_ip_neighbor Test started ..." + +- name: "Test {{ module_name }} started ..." + include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: Display all variables/facts known for a host + debug: + var: hostvars[inventory_hostname].ansible_facts.test_reports + diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ip_neighbor/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ip_neighbor/tasks/tasks_template.yaml new file mode 100644 index 000000000..35548591e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ip_neighbor/tasks/tasks_template.yaml @@ -0,0 +1,21 @@ +- name: "{{ item.name}} , {{ item.description}}" + sonic_ip_neighbor: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name}} , {{ item.description}} Idempotent" + sonic_ip_neighbor: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/defaults/main.yml new file mode 100644 index 000000000..604aae22f --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/defaults/main.yml @@ -0,0 +1,214 @@ +--- +ansible_connection: httpapi +module_name: l2_acls + +tests: + - name: test_case_01 + description: Add L2 ACL + state: merged + input: + - name: 'acl1' + remark: 'L2 ACL 1' + rules: + - sequence_num: 1 + action: 'permit' + source: + host: '11:11:11:11:11:11' + destination: + host: '11:11:11:22:22:22' + remark: 'Rule1' + - sequence_num: 2 + action: 'transit' + source: + address: '00:00:10:00:00:00' + address_mask: '00:00:ff:ff:00:00' + destination: + any: true + vlan_id: 100 + - sequence_num: 3 + action: 'transit' + source: + any: true + destination: + address: '00:00:00:00:10:00' + address_mask: '00:00:00:00:ff:ff' + ethertype: + value: '0x0842' + + - name: test_case_02 + description: Add new rules to existing L2 ACL and add new L2 ACLs + state: merged + input: + - name: 'acl1' + remark: 'Remark_ACL1' + rules: + - sequence_num: 4 + action: 'discard' + source: + any: true + destination: + any: true + ethertype: + arp: true + - sequence_num: 5 + action: 'discard' + source: + any: true + destination: + any: true + vlan_tag_format: + multi_tagged: true + remark: 'VLAN_multi_tagged' + - name: 'acl2' + remark: 'Remark_ACL2' + rules: + - sequence_num: 1 + action: 'permit' + source: + any: true + destination: + any: true + ethertype: + ipv6: true + vlan_id: 200 + - sequence_num: 2 + action: 'permit' + source: + any: true + destination: + any: true + dei: 1 + - sequence_num: 3 + action: 'permit' + source: + any: true + destination: + any: true + pcp: + value: 4 + mask: 6 + - sequence_num: 4 + action: 'do-not-nat' + source: + any: true + destination: + any: true + ethertype: + ipv4: true + + - name: test_case_03 + description: Replace specified L2 ACL with provided configuration and add new L2 ACL + state: replaced + input: + - name: 'acl2' + rules: + - sequence_num: 1 + action: 'permit' + source: + any: true + destination: + host: '11:11:11:22:22:22' + - sequence_num: 2 + action: 'deny' + source: + any: true + destination: + any: true + ethertype: + ipv4: true + vlan_id: 100 + - name: 'acl3' + rules: + - sequence_num: 1 + action: 'permit' + source: + host: '22:22:22:22:22:22' + destination: + any: true + pcp: + traffic_type: 'ca' + - sequence_num: 2 + action: 'deny' + source: + any: true + destination: + any: true + remark: 'Deny_All' + + - name: test_case_04 + description: Override existing L2 ACL configuration with provided L2 ACL configuration + state: overridden + input: + - name: 'acl1' + rules: + - sequence_num: 1 + action: 'permit' + source: + host: '33:33:33:33:33:33' + destination: + host: '44:44:44:44:44:44' + - name: 'test-acl' + remark: 'test_mac_acl' + rules: + - sequence_num: 1 + action: 'permit' + source: + host: '22:22:22:22:22:22' + destination: + any: true + vlan_id: 20 + - sequence_num: 2 + action: 'permit' + source: + any: true + destination: + any: true + ethertype: + value: '0x88cc' + remark: 'LLDP' + - sequence_num: 3 + action: 'permit' + source: + any: true + destination: + address: '00:00:10:00:00:00' + address_mask: '00:00:ff:ff:00:00' + pcp: + value: 4 + mask: 6 + - name: 'test-acl-1' + remark: 'test_mac_acl_1' + rules: + - sequence_num: 1 + action: 'permit' + source: + any: true + destination: + any: true + ethertype: + arp: true + vlan_id: 200 + - sequence_num: 2 + action: 'discard' + source: + any: true + destination: + any: true + + - name: test_case_05 + description: Delete specified L2 ACLs, ACL remark and ACL rule entries + state: deleted + input: + - name: 'acl1' + - name: 'test-acl' + rules: + - sequence_num: 3 + - name: 'test-acl-1' + remark: 'test_mac_acl_1' + rules: + - sequence_num: 2 + + - name: test_case_06 + description: Delete all L2 ACLs + state: deleted + input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/meta/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/meta/main.yml new file mode 100644 index 000000000..d0ceaf6f5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/meta/main.yml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common } diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..646e0bfa9 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/tasks/cleanup_tests.yaml @@ -0,0 +1,6 @@ +--- +- name: Delete L2 ACLs + dellemc.enterprise_sonic.sonic_l2_acls: + config: [] + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/tasks/main.yml new file mode 100644 index 000000000..bf07d028a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/tasks/main.yml @@ -0,0 +1,17 @@ +--- +- ansible.builtin.debug: + msg: "sonic_l2_acls Test started ..." + +- name: "Preparations for {{ module_name }}" + ansible.builtin.include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started" + ansible.builtin.include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: "Cleanup of {{ module_name }}" + ansible.builtin.include_tasks: cleanup_tests.yaml + +- name: Display all variables/facts known for a host + ansible.builtin.debug: + var: hostvars[inventory_hostname].ansible_facts.test_reports diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/tasks/preparation_tests.yaml new file mode 100644 index 000000000..ceab447fc --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/tasks/preparation_tests.yaml @@ -0,0 +1,18 @@ +--- +- name: Delete old interface access-group configurations + dellemc.enterprise_sonic.sonic_acl_interfaces: + config: [] + state: deleted + ignore_errors: yes + +- name: Delete old L2 ACLs + dellemc.enterprise_sonic.sonic_l2_acls: + config: [] + state: deleted + ignore_errors: yes + +- name: Delete old L3 ACLs + dellemc.enterprise_sonic.sonic_l3_acls: + config: [] + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/tasks/tasks_template.yaml new file mode 100644 index 000000000..19fb375fd --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_acls/tasks/tasks_template.yaml @@ -0,0 +1,22 @@ +--- +- name: "{{ item.name }} , {{ item.description }}" + dellemc.enterprise_sonic.sonic_l2_acls: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name }} , {{ item.description }} Idempotent" + dellemc.enterprise_sonic.sonic_l2_acls: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/defaults/main.yml index 8117a89cc..b24cec7bd 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/defaults/main.yml @@ -13,6 +13,48 @@ preparations_tests: - vlan_id: 400 - vlan_id: 401 - vlan_id: 402 + - vlan_id: 605 + - vlan_id: 606 + - vlan_id: 607 + - vlan_id: 609 + - vlan_id: 610 + - vlan_id: 611 + - vlan_id: 612 + - vlan_id: 613 + - vlan_id: 615 + - vlan_id: 616 + - vlan_id: 617 + - vlan_id: 619 + - vlan_id: 620 + - vlan_id: 621 + - vlan_id: 622 + - vlan_id: 624 + - vlan_id: 625 + - vlan_id: 626 + - vlan_id: 627 + - vlan_id: 628 + - vlan_id: 629 + - vlan_id: 634 + - vlan_id: 635 + - vlan_id: 636 + - vlan_id: 637 + - vlan_id: 639 + - vlan_id: 640 + - vlan_id: 642 + - vlan_id: 643 + - vlan_id: 644 + - vlan_id: 646 + - vlan_id: 647 + - vlan_id: 649 + - vlan_id: 650 + - vlan_id: 651 + - vlan_id: 653 + - vlan_id: 654 + - vlan_id: 655 + - vlan_id: 656 + - vlan_id: 658 + - vlan_id: 659 + - vlan_id: 660 delete_port_configurations: - name: "{{ interface1 }}" - name: "{{ interface2 }}" @@ -61,14 +103,73 @@ tests: - vlan: 503 access: vlan: 402 - # delete test cases started - name: test_case_03 + description: Add trunk vlan range base config + state: merged + input: + - name: "{{ interface5 }}" + trunk: + allowed_vlans: + - vlan: 605 + - vlan: 611 + - vlan: 617 + - vlan: 619-620 + - vlan: 626-627 + - vlan: 636-637 + - vlan: 639 + - vlan: 643 + - vlan: 647 + - vlan: 649-650 + - vlan: 654-655 + - vlan: 659-660 + access: + vlan: 611 + - name: test_case_04 + description: Add trunk vlan range overlay lower half + state: merged + input: + - name: "{{ interface5 }}" + trunk: + allowed_vlans: + - vlan: 605-607 + - vlan: 609-613 + - vlan: 615-617 + - vlan: 619-622 + - vlan: 624-629 + - vlan: 634-637 + - vlan: 639-640 + access: + vlan: 611 + - name: test_case_05 + description: Add trunk vlan range overlay all + state: merged + input: + - name: "{{ interface5 }}" + trunk: + allowed_vlans: + - vlan: 605-607 + - vlan: 609-613 + - vlan: 615-617 + - vlan: 619-622 + - vlan: 624-629 + - vlan: 634-637 + - vlan: 639-640 + - vlan: 642-644 + - vlan: 646-647 + - vlan: 649-651 + - vlan: 653-656 + - vlan: 658-660 + access: + vlan: 611 + # delete test cases started + - name: test_case_06 description: Delete Access VLAN state: deleted input: - name: "{{ interface1 }}" access: - - name: test_case_04 + vlan: + - name: test_case_07 description: Delete specific trunk VLANs state: deleted input: @@ -76,26 +177,147 @@ tests: trunk: allowed_vlans: - vlan: 502 - - name: test_case_05 + - name: test_case_08 description: Delete access VLANs from both associations state: deleted input: - name: "{{ interface3 }}" access: vlan: - - name: test_case_06 + - name: test_case_09 description: Delete all trunk VLANs state: deleted input: - name: "{{ interface3 }}" trunk: allowed_vlans: - - name: test_case_07 + - name: test_case_10 description: Delete all associations in specific interface state: deleted input: - name: "{{ interface2 }}" - - name: test_case_08 + - name: test_case_11 + description: Delete trunk vlan range overlay base + state: deleted + input: + - name: "{{ interface5 }}" + trunk: + allowed_vlans: + - vlan: 605 + - vlan: 611 + - vlan: 617 + - vlan: 619-620 + - vlan: 626-627 + - vlan: 636-637 + - vlan: 639 + - vlan: 643 + - vlan: 647 + - vlan: 649-650 + - vlan: 654-655 + - vlan: 659-660 + access: + vlan: 611 + - name: test_case_12 + description: Delete trunk vlan range overlay lower half + state: deleted + input: + - name: "{{ interface5 }}" + trunk: + allowed_vlans: + - vlan: 605-607 + - vlan: 609-613 + - vlan: 615-617 + - vlan: 619-622 + - vlan: 624-629 + - vlan: 634-637 + - vlan: 639-640 + access: + vlan: 611 + - name: test_case_13 + description: Delete trunk vlan range overlay all + state: deleted + input: + - name: "{{ interface5 }}" + trunk: + allowed_vlans: + - vlan: 605-607 + - vlan: 609-613 + - vlan: 615-617 + - vlan: 619-622 + - vlan: 624-629 + - vlan: 634-637 + - vlan: 639-640 + - vlan: 642-644 + - vlan: 646-647 + - vlan: 649-651 + - vlan: 653-656 + - vlan: 658-660 + access: + vlan: 611 + # Base config for replace test case + - name: test_case_14 + description: Add access and trunk VLANs + state: merged + input: + - name: "{{ interface1 }}" + access: + vlan: 400 + trunk: + allowed_vlans: + - vlan: 600-650 + - name: "{{ interface2 }}" + access: + vlan: 400 + trunk: + allowed_vlans: + - vlan: 600-650 + - name: "{{ interface3 }}" + access: + vlan: 400 + trunk: + allowed_vlans: + - vlan: 600-650 + - name: test_case_15 + description: Replace access and trunk VLANs + state: replaced + input: + - name: "{{ interface2 }}" + trunk: + allowed_vlans: + - vlan: 500 + - vlan: 502-505 + - vlan: 620-630 + - name: "{{ interface3 }}" + access: + vlan: 405 + - name: PortChannel100 + - name: PortChannel101 + access: + vlan: 400 + trunk: + allowed_vlans: + - vlan: 620-630 + - name: test_case_16 + description: Override access and trunk VLANs + state: overridden + input: + - name: "{{ interface1 }}" + access: + vlan: 400 + trunk: + allowed_vlans: + - vlan: 500-505 + - vlan: 600-605 + - name: "{{ interface2 }}" + access: + vlan: 400 + - name: PortChannel100 + trunk: + allowed_vlans: + - vlan: 500 + - vlan: 600 + - vlan: 605 + - name: test_case_17 description: Delete All associations in all interfaces state: deleted input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/defaults/main.yml new file mode 100644 index 000000000..2c3c23b3d --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/defaults/main.yml @@ -0,0 +1,476 @@ +--- +ansible_connection: httpapi +module_name: l3_acls + +tests: + - name: test_case_01 + description: Add IPv4 ACL + state: merged + input: + - address_family: 'ipv4' + acls: + - name: 'ip-acl1' + remark: 'IPv4 ACL 1' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'ip' + source: + host: '192.168.1.2' + destination: + host: '192.168.2.2' + remark: 'Host-Rule' + - sequence_num: 2 + action: 'transit' + protocol: + name: 'icmp' + source: + prefix: '192.168.0.0/16' + destination: + any: true + protocol_options: + icmp: + type: 8 + vlan_id: 100 + + - name: test_case_02 + description: Add IPv6 ACL + state: merged + input: + - address_family: 'ipv6' + acls: + - name: 'ipv6-acl1' + remark: 'IPv6 ACL 1' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'ipv6' + source: + prefix: '192::/64' + destination: + any: true + - sequence_num: 2 + action: 'permit' + protocol: + name: 'tcp' + source: + any: true + port_number: + gt: 1024 + destination: + host: '192::2' + port_number: + eq: 80 + + - name: test_case_03 + description: Add new rules to existing L3 ACLs and add new L3 ACLs + state: merged + input: + - address_family: 'ipv4' + acls: + - name: 'ip-acl1' + rules: + - sequence_num: 3 + action: 'deny' + protocol: + number: 17 + source: + host: '192.168.1.2' + destination: + prefix: '192.168.1.0/24' + port_number: + lt: 1024 + remark: "Drop UDP" + - name: 'ip-acl2' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'tcp' + source: + any: true + destination: + any: true + protocol_options: + tcp: + ack: true + syn: true + fin: true + - sequence_num: 2 + action: 'permit' + protocol: + number: 2 + source: + any: true + destination: + any: true + dscp: + voice_admit: true + - sequence_num: 3 + action: 'discard' + protocol: + name: 'icmp' + source: + any: true + destination: + any: true + - address_family: 'ipv6' + acls: + - name: 'ipv6-acl1' + rules: + - sequence_num: 3 + action: 'deny' + protocol: + name: 'udp' + source: + any: true + destination: + host: '100::1' + port_number: + range: + begin: 1024 + end: 2048 + - name: 'ipv6-acl2' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'icmpv6' + source: + any: true + destination: + any: true + protocol_options: + icmpv6: + type: 128 + code: 0 + vlan_id: 200 + - sequence_num: 2 + action: 'deny' + protocol: + name: 'ipv6' + source: + host: '100::1' + destination: + any: true + vlan_id: 200 + + - name: test_case_04 + description: Replace specified L3 ACLs with provided configuration and add new L3 ACLs + state: replaced + input: + - address_family: 'ipv4' + acls: + - name: 'ip-acl2' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'tcp' + source: + any: true + destination: + any: true + protocol_options: + tcp: + established: true + remark: 'TCP established' + - sequence_num: 2 + action: 'deny' + protocol: + name: 'ip' + source: + any: true + destination: + any: true + - name: 'ip-acl3' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'ip' + source: + any: true + destination: + host: '192.168.2.2' + dscp: + value: 4 + - sequence_num: 2 + action: 'discard' + protocol: + name: 'tcp' + source: + any: true + destination: + any: true + protocol_options: + tcp: + psh: true + urg: true + - address_family: 'ipv6' + acls: + - name: 'ipv6-acl1' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'tcp' + source: + host: '100::2' + port_number: + eq: 80 + destination: + any: true + - sequence_num: 2 + action: 'deny' + protocol: + name: 'udp' + source: + any: true + port_number: + gt: 1024 + destination: + any: true + port_number: + gt: 1024 + + - name: test_case_05 + description: Override existing L3 ACL configuration with provided L3 ACL configuration + state: overridden + input: + - address_family: 'ipv4' + acls: + - name: 'ip-acl' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'icmp' + source: + host: '100.1.1.2' + destination: + host: '100.1.2.2' + - sequence_num: 2 + action: 'deny' + protocol: + name: 'ip' + source: + any: true + destination: + any: true + - name: 'test-acl-ipv4-udp' + remark: 'UDP' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'udp' + source: + any: true + port_number: + lt: 1000 + destination: + any: true + - sequence_num: 2 + action: 'discard' + protocol: + name: 'udp' + source: + any: true + destination: + any: true + + - name: test_case_06 + description: Add new L3 ACLs + state: merged + input: + - address_family: 'ipv6' + acls: + - name: 'test-acl-ipv6' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'ipv6' + source: + any: true + destination: + any: true + dscp: + value: 8 + - sequence_num: 2 + action: 'discard' + protocol: + name: 'ipv6' + source: + any: true + destination: + any: true + vlan_id: 100 + - name: 'test-acl-ipv6-tcp' + remark: 'TCP' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'tcp' + source: + prefix: '200::/64' + port_number: + range: + begin: 3000 + end: 10000 + destination: + any: true + - sequence_num: 2 + action: 'permit' + protocol: + name: 'tcp' + source: + any: true + destination: + any: true + protocol_options: + tcp: + not_psh: true + not_urg: true + + - name: test_case_07 + description: Delete specified L3 ACLs, ACL remark and ACL rule entries + state: deleted + input: + - address_family: 'ipv4' + acls: + - name: 'ip-acl' + - name: 'test-acl-ipv4-udp' + rules: + - sequence_num: 2 + - address_family: 'ipv6' + acls: + - name: 'test-acl-ipv6-tcp' + remark: 'TCP' + rules: + - sequence_num: 1 + + - name: test_case_08 + description: Add new L3 ACLs + state: merged + input: + - address_family: 'ipv4' + acls: + - name: 'test-acl-ipv4' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'ip' + source: + prefix: '100.1.1.0/24' + destination: + prefix: '100.1.2.0/24' + - sequence_num: 2 + action: 'deny' + protocol: + name: 'ip' + source: + any: true + destination: + any: true + + - name: test_case_09 + description: Delete all IPv6 ACLs + state: deleted + input: + - address_family: 'ipv6' + + - name: test_case_10 + description: Add new IPv6 ACLs + state: merged + input: + - address_family: 'ipv6' + acls: + - name: 'acl-IPv6' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'ipv6' + source: + prefix: '100:1:1::/64' + destination: + prefix: '100:1:2::/64' + vlan_id: 10 + - sequence_num: 2 + action: 'deny' + protocol: + name: 'udp' + source: + prefix: '100:1:1::/64' + port_number: + gt: 200 + destination: + prefix: '100:1:2::/64' + port_number: + gt: 200 + - name: 'acl-IPv6-1' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'icmpv6' + source: + any: true + destination: + any: true + + - name: test_case_11 + description: Delete all IPv4 ACLs + state: deleted + input: + - address_family: 'ipv4' + + - name: test_case_12 + description: Add new IPv4 ACLs + state: merged + input: + - address_family: 'ipv4' + acls: + - name: 'acl-IPv4' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'tcp' + source: + prefix: '100.0.0.0/8' + destination: + host: '101.1.1.2' + port_number: + eq: 80 + - sequence_num: 2 + action: 'deny' + protocol: + name: 'ip' + source: + any: true + destination: + any: true + - name: 'acl-IPv4-1' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'ip' + source: + any: true + destination: + any: true + dscp: + af11: true + + - name: test_case_13 + description: Delete all L3 ACLs + state: deleted + input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/meta/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/meta/main.yml new file mode 100644 index 000000000..d0ceaf6f5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/meta/main.yml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common } diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..297ad05a2 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/tasks/cleanup_tests.yaml @@ -0,0 +1,6 @@ +--- +- name: Delete L3 ACLs + dellemc.enterprise_sonic.sonic_l3_acls: + config: [] + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/tasks/main.yml new file mode 100644 index 000000000..dffa26c5d --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/tasks/main.yml @@ -0,0 +1,17 @@ +--- +- ansible.builtin.debug: + msg: "sonic_l3_acls Test started ..." + +- name: "Preparations for {{ module_name }}" + ansible.builtin.include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started" + ansible.builtin.include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: "Cleanup of {{ module_name }}" + ansible.builtin.include_tasks: cleanup_tests.yaml + +- name: Display all variables/facts known for a host + ansible.builtin.debug: + var: hostvars[inventory_hostname].ansible_facts.test_reports diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/tasks/preparation_tests.yaml new file mode 100644 index 000000000..ceab447fc --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/tasks/preparation_tests.yaml @@ -0,0 +1,18 @@ +--- +- name: Delete old interface access-group configurations + dellemc.enterprise_sonic.sonic_acl_interfaces: + config: [] + state: deleted + ignore_errors: yes + +- name: Delete old L2 ACLs + dellemc.enterprise_sonic.sonic_l2_acls: + config: [] + state: deleted + ignore_errors: yes + +- name: Delete old L3 ACLs + dellemc.enterprise_sonic.sonic_l3_acls: + config: [] + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/tasks/tasks_template.yaml new file mode 100644 index 000000000..a57059008 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_acls/tasks/tasks_template.yaml @@ -0,0 +1,22 @@ +--- +- name: "{{ item.name }} , {{ item.description }}" + dellemc.enterprise_sonic.sonic_l3_acls: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name }} , {{ item.description }} Idempotent" + dellemc.enterprise_sonic.sonic_l3_acls: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/defaults/main.yml index de6322049..5c5d5a74d 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/defaults/main.yml @@ -6,8 +6,6 @@ preparations_tests: delete_port_configurations: - name: "{{ interface1 }}" - name: "{{ interface2 }}" - - name: "{{ interface3 }}" - - name: "{{ interface4 }}" init_loopback: - "interface Loopback 100" - "interface Loopback 101" @@ -149,24 +147,28 @@ tests: - address: 92::1/16 - address: 93::1/16 - name: test_case_07 - description: Update interface parameters + description: Delete interface addresses state: deleted input: - name: "{{ interface1 }}" ipv4: addresses: - - address: 82.1.1.1/16 + - address: 83.1.1.1/16 - name: "{{ interface2 }}" ipv6: enabled: false addresses: - address: 91::1/16 + - name: Loopback100 + ipv4: + addresses: + - address: 103.1.1.1/32 - name: vlan 102 ipv4: anycast_addresses: - 1.1.1.1/16 - name: test_case_08 - description: Update interface parameters + description: Delete interface parameters state: deleted input: - name: "{{ interface1 }}" @@ -201,7 +203,7 @@ tests: - address: 3041::1/16 - address: 3042::1/16 - name: test_case_10 - description: Naming tests + description: Update interface parameters state: merged input: - name: vlan 501 @@ -232,13 +234,109 @@ tests: - address: 3051::1/16 - address: 3052::1/16 - name: test_case_11 - description: Naming tests + description: Update l3 interface parameters + state: merged + input: + - name: '{{ interface1 }}' + ipv6: + enabled: true + addresses: + - address: 213::1/64 + ipv4: + addresses: + - address: 213.1.1.1/24 + - name: '{{ interface3 }}' + ipv4: + addresses: + - address: 215.1.1.1/24 + - address: 216.1.1.1/24 + secondary: true + - name: test_case_12 + description: Replace interface parameters + state: replaced + input: + - name: '{{ interface2 }}' + ipv6: + enabled: true + addresses: + - address: 251::1/64 + - address: 252::1/64 + - name: '{{ interface3 }}' + ipv4: + addresses: + - address: 222.1.1.1/24 + - name: vlan 101 + ipv4: + anycast_addresses: + - 15.16.17.18/16 + - name: test_case_13 + description: Replace interface parameters + state: replaced + input: + - name: vlan 501 + ipv4: + addresses: + - address: 105.2.2.2/16 + - name: test_case_14 + description: Override interface parameters + state: overridden + input: + - name: vlan 501 + ipv6: + enabled: true + addresses: + - address: 1053::1/64 + - address: 1054::1/64 + - address: 1055::1/64 + - address: 1056::2/64 + - name: Loopback101 + ipv4: + addresses: + - address: 152.1.1.1/32 + - address: 153.1.1.1/32 + secondary: true + - name: vlan 100 + ipv4: + anycast_addresses: + - 20.21.22.23/16 + - 85.1.1.12/16 + ipv6: + enabled: false + - name: test_case_15 + description: Override2 interface parameters + state: overridden + input: + - name: vlan 501 + ipv6: + enabled: true + addresses: + - address: 1053::1/64 + - address: 1054::1/64 + - address: 1055::1/64 + - address: 1056::2/64 + - address: 1057::2/64 + - name: Loopback101 + ipv4: + addresses: + - address: 152.1.1.1/32 + - address: 153.1.1.1/32 + secondary: true + - name: vlan 100 + ipv4: + anycast_addresses: + - 20.21.22.23/16 + - 85.1.1.12/16 + ipv6: + enabled: false + - name: test_case_16 + description: Delete all interfaces config state: deleted input: + - name: vlan 100 + - name: vlan 101 + - name: vlan 102 - name: vlan 501 - name: lo101 + - name: lo102 - name: portchannel 100 # po100 or portchannel100 - - name: test_case_12 - description: Update interface parameters - state: deleted - input: [] + - name: portchannel 101 diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/preparation_tests.yaml index 66700d53e..48fc64d5c 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/preparation_tests.yaml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/preparation_tests.yaml @@ -8,11 +8,6 @@ config: [] state: deleted ignore_errors: yes -- name: Deletes old l3 interfaces - sonic_l3_interfaces: - config: [] - state: deleted - ignore_errors: yes - name: "initialize default interfaces" vars: ansible_connection: network_cli diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/defaults/main.yml index 3f77cabab..c320445e6 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/defaults/main.yml @@ -8,6 +8,8 @@ preparations_tests: - name: "{{ interface2 }}" - name: "{{ interface3 }}" - name: "{{ interface4 }}" + - name: "{{ interface5 }}" + - name: "{{ interface6 }}" tests: # Ethernet testcases started... @@ -77,13 +79,49 @@ tests: interfaces: - member: "{{ interface3 }}" - name: test_case_06 + description: Replace portchannel configuration + state: replaced + input: + - name: portchannel 40 + members: + interfaces: + - member: "{{ interface5 }}" + - name: po41 + members: + interfaces: + - member: "{{ interface6 }}" + - name: test_case_07 + description: Override portchannel configuration + state: overridden + input: + - name: portchannel 40 + members: + interfaces: + - member: "{{ interface1 }}" + - name: po41 + members: + interfaces: + - member: "{{ interface2 }}" + - name: test_case_08 + description: Override all portchannel configuration + state: overridden + input: + - name: portchannel 42 + members: + interfaces: + - member: "{{ interface5 }}" + - name: po43 + members: + interfaces: + - member: "{{ interface6 }}" + - name: test_case_09 description: Create standalone portchannels state: merged input: - name: portchannel42 - name: portchannel 12 - name: po10 - - name: test_case_07 + - name: test_case_10 description: Update interface parameters state: deleted input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/defaults/main.yml new file mode 100644 index 000000000..ab33abe28 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/defaults/main.yml @@ -0,0 +1,47 @@ +--- +ansible_connection: httpapi +module_name: lldp_global + + +tests: + - name: test_case_01 + description: Add Global LLDP configuration + state: merged + input: + enable: true + multiplier: 9 + system_name: CR_sonic + hello_time: 18 + mode: receive + system_description: Sonic_CR1_device + tlv_select: + system_capabilities: false + + - name: test_case_02 + description: Update Global LLDP configuration + state: merged + input: + multiplier: 10 + system_name: CR1_sonic + hello_time: 200 + tlv_select: + system_capabilities: true + + - name: test_case_03 + description: Delete specific global lldp configurations + state: deleted + input: + multiplier: 10 + system_name: CR1_sonic + + - name: test_case_04 + description: Delete default global lldp configurations + state: deleted + input: + tlv_select: + system_capabilities: true + +test_delete_all: + - name: del_all_test_case_05 + description: Delete all global lldp configurations + state: deleted diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/meta/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/meta/main.yml new file mode 100644 index 000000000..d0ceaf6f5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/meta/main.yml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common } diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..e2ca40667 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/cleanup_tests.yaml @@ -0,0 +1,5 @@ +- name: Delete global LLDP configurations + dellemc.enterprise_sonic.sonic_lldp_global: + config: + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/main.yml new file mode 100644 index 000000000..a5c0b020c --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/main.yml @@ -0,0 +1,22 @@ +--- +- ansible.builtin.debug: + msg: "sonic_lldp_global Test started ..." + +- name: "Preparations for {{ module_name }}" + ansible.builtin.include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started" + ansible.builtin.include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: "test_delete_all {{ module_name }} stated ..." + ansible.builtin.include_tasks: tasks_template_del.yaml + loop: "{{ test_delete_all }}" + when: test_delete_all is defined + +- name: "Cleanup of {{ module_name }}" + ansible.builtin.include_tasks: cleanup_tests.yaml + +- name: Display all variables/facts known for a host + ansible.builtin.debug: + var: hostvars[inventory_hostname].ansible_facts.test_reports diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/preparation_tests.yaml new file mode 100644 index 000000000..aaadc7dc2 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/preparation_tests.yaml @@ -0,0 +1,6 @@ +--- +- name: Delete old global LLDP configurations + dellemc.enterprise_sonic.sonic_lldp_global: + config: {} + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/tasks_template.yaml new file mode 100644 index 000000000..9e90e5468 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/tasks_template.yaml @@ -0,0 +1,22 @@ +--- +- name: "{{ item.name }} , {{ item.description }}" + dellemc.enterprise_sonic.sonic_lldp_global: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name }} , {{ item.description }} Idempotent" + dellemc.enterprise_sonic.sonic_lldp_global: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/tasks_template_del.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/tasks_template_del.yaml new file mode 100644 index 000000000..383e72b84 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lldp_global/tasks/tasks_template_del.yaml @@ -0,0 +1,21 @@ +- name: "{{ item.name }} , {{ item.description }}" + dellemc.enterprise_sonic.sonic_lldp_global: + config: + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name }} , {{ item.description }} Idempotent" + dellemc.enterprise_sonic.sonic_lldp_global: + config: + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/defaults/main.yml new file mode 100644 index 000000000..456635fcb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/defaults/main.yml @@ -0,0 +1,127 @@ +--- +ansible_connection: httpapi +module_name: logging + +po1: Portchannel 100 +vlan1: Vlan 100 +lo1: Loopback 100 +mgmt: Management 0 + +logging_ip_server_1: 10.11.0.1 +logging_ip_server_2: 10.11.0.2 +logging_ip_server_3: 10.11.0.3 +logging_ip_server_4: 10.11.0.4 +logging_ip_server_5: 10.11.0.5 +logging_ip_server_6: 10.11.0.6 +logging_host_server: logging.dell.com + +tests: + - name: test_case_01 + description: Create a logging remote server + state: merged + input: + remote_servers: + - host: "{{ logging_ip_server_1 }}" + source_interface: "{{ interface1 }}" + remote_port: 616 + message_type: event + vrf: Vrf_logging_1 + + - name: test_case_02 + description: Create several logging remote servers + state: merged + input: + remote_servers: + - host: "{{ logging_ip_server_2 }}" + source_interface: "{{ po1 }}" + - host: "{{ logging_ip_server_3 }}" + source_interface: "{{ vlan1 }}" + remote_port: 818 + message_type: event + vrf: Vrf_logging_2 + - host: "{{ logging_ip_server_4 }}" + source_interface: "{{ mgmt }}" + message_type: event + vrf: Vrf_logging_1 + - host: "{{ logging_host_server }}" + source_interface: "{{ lo1 }}" + message_type: log + vrf: Vrf_logging_1 + + - name: test_case_03 + description: Replace logging remote server + state: replaced + input: + remote_servers: + - host: "{{ logging_ip_server_3 }}" + source_interface: "{{ interface2 }}" + remote_port: 838 + + - name: test_case_04 + description: Replace logging remote servers + state: replaced + input: + remote_servers: + - host: "{{ logging_ip_server_3 }}" + source_interface: "{{ interface3 }}" + remote_port: 838 + message_type: event + - host: "{{ logging_ip_server_4 }}" + source_interface: "{{ interface4 }}" + - host: "{{ logging_ip_server_6 }}" + source_interface: "{{ interface4 }}" + remote_port: 868 + message_type: event + + - name: test_case_05 + description: Overridden logging remote servers + state: overridden + input: + remote_servers: + - host: "{{ logging_ip_server_2 }}" + source_interface: "{{ interface3 }}" + - host: "{{ logging_ip_server_5 }}" + source_interface: "{{ interface4 }}" + remote_port: 858 + message_type: event + + - name: test_case_06 + description: Create several logging remote servers + state: merged + input: + remote_servers: + - host: "{{ logging_ip_server_1 }}" + source_interface: "{{ interface1 }}" + remote_port: 616 + message_type: event + vrf: Vrf_logging_1 + - host: "{{ logging_ip_server_3 }}" + source_interface: "{{ vlan1 }}" + remote_port: 818 + message_type: event + vrf: Vrf_logging_2 + - host: "{{ logging_host_server }}" + source_interface: "{{ lo1 }}" + message_type: log + vrf: Vrf_logging_1 + + - name: test_case_07 + description: Delete a logging remote server + state: deleted + input: + remote_servers: + - host: "{{ logging_ip_server_1 }}" + + - name: test_case_08 + description: Delete several logging remote servers + state: deleted + input: + remote_servers: + - host: "{{ logging_ip_server_2 }}" + - host: "{{ logging_ip_server_3 }}" + - host: "{{ logging_host_server }}" + + - name: test_case_09 + description: Delete all logging configurations + state: deleted + input: {} diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/meta/main.yaml new file mode 100644 index 000000000..611fd54d2 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/meta/main.yaml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common }
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..c9800850d --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/tasks/cleanup_tests.yaml @@ -0,0 +1,28 @@ +- name: Delete loopback + sonic_interfaces: + config: + - name: Loopback100 + state: deleted + ignore_errors: yes + +- name: Delete portchannel + sonic_lag_interfaces: + config: + - name: PortChannel100 + state: deleted + ignore_errors: yes + +- name: Delete vlan + sonic_vlans: + config: + - vlan_id: 100 + state: deleted + ignore_errors: yes + +- name: Delete VRFs + sonic_vrfs: + config: + - name: Vrf_logging_1 + - name: Vrf_logging_2 + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/tasks/main.yml new file mode 100644 index 000000000..63ee885ce --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/tasks/main.yml @@ -0,0 +1,16 @@ +- debug: msg="sonic_logging Test started ..." + +- name: Preparations tests + include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started ..." + include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: "Cleanup test {{ module_name }} started" + include_tasks: cleanup_tests.yaml + +- name: Display all variables/facts known for a host + debug: + var: hostvars[inventory_hostname].ansible_facts.test_reports + diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/tasks/preparation_tests.yaml new file mode 100644 index 000000000..f360925d6 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/tasks/preparation_tests.yaml @@ -0,0 +1,34 @@ +- name: Delete existing logging configurations + sonic_logging: + config: {} + state: deleted + ignore_errors: yes + +- name: Initialize loopback + sonic_interfaces: + config: + - name: Loopback100 + state: merged + ignore_errors: yes + +- name: Initialize portchannel + sonic_lag_interfaces: + config: + - name: PortChannel100 + state: merged + ignore_errors: yes + +- name: Initialize vlan + sonic_vlans: + config: + - vlan_id: 100 + state: merged + ignore_errors: yes + +- name: Create VRFs + sonic_vrfs: + config: + - name: Vrf_logging_1 + - name: Vrf_logging_2 + state: merged + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/tasks/tasks_template.yaml new file mode 100644 index 000000000..7ca17b24b --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_logging/tasks/tasks_template.yaml @@ -0,0 +1,21 @@ +- name: "{{ item.name}} , {{ item.description}}" + sonic_logging: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name}} , {{ item.description}} Idempotent" + sonic_logging: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/defaults/main.yml new file mode 100644 index 000000000..fd092beab --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/defaults/main.yml @@ -0,0 +1,101 @@ +--- +ansible_connection: httpapi +module_name: sonic_mac + +vrf_1: VrfReg1 + +preparations_tests: + init_vlan: + - 'interface Vlan 1' + - 'interface Vlan 2' + - 'interface Vlan 3' +tests: + - name: test_case_01 + description: Configure MACs + state: merged + input: + - vrf_name: 'default' + mac: + aging_time: 50 + dampening_interval: 20 + dampening_threshold: 30 + mac_table_entries: + - mac_address: '00:00:5e:00:53:af' + vlan_id: 1 + interface: '{{interface1}}' + - mac_address: '00:33:33:33:33:33' + vlan_id: 2 + interface: '{{interface2}}' + - mac_address: '00:00:4e:00:24:af' + vlan_id: 3 + interface: '{{interface3}}' + - name: test_case_02 + description: Modify MAC configurations + state: merged + input: + - vrf_name: 'default' + mac: + aging_time: 25 + dampening_interval: 10 + dampening_threshold: 40 + mac_table_entries: + - mac_address: '00:00:5e:00:53:af' + vlan_id: 1 + interface: '{{interface2}}' + - mac_address: '00:33:33:33:33:33' + vlan_id: 2 + interface: '{{interface1}}' + - name: test_case_03 + description: Replace MAC configurations + state: replaced + input: + - vrf_name: 'default' + mac: + aging_time: 45 + dampening_interval: 30 + dampening_threshold: 60 + mac_table_entries: + - mac_address: '00:00:5e:00:53:af' + vlan_id: 1 + interface: '{{interface1}}' + - mac_address: '00:44:44:44:44:44' + vlan_id: 2 + interface: '{{interface3}}' + - name: test_case_04 + description: Override MAC cofigurations + state: overridden + input: + - vrf_name: 'default' + mac: + aging_time: 10 + dampening_interval: 20 + dampening_threshold: 30 + mac_table_entries: + - mac_address: '00:11:11:11:11:11' + vlan_id: 1 + interface: '{{interface1}}' + - mac_address: '00:22:22:22:22:22' + vlan_id: 2 + interface: '{{interface2}}' + - mac_address: '00:00:33:33:33:33' + vlan_id: 3 + interface: '{{interface3}}' + - name: test_case_05 + description: Delete MAC cofigurations + state: deleted + input: + - vrf_name: 'default' + mac: + aging_time: 10 + dampening_interval: 20 + dampening_threshold: 30 + mac_table_entries: + - mac_address: '00:11:11:11:11:11' + vlan_id: 1 + interface: '{{interface1}}' + - mac_address: '00:00:33:33:33:33' + vlan_id: 3 + - name: test_case_06 + description: Delete all MAC configurations + state: deleted + input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/meta/main.yaml new file mode 100644 index 000000000..0b356217e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/meta/main.yaml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common } diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/tasks/cleanup_tests.yaml index c6a26a684..bcb4847a8 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/cleanup_tests.yaml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/tasks/cleanup_tests.yaml @@ -1,6 +1,5 @@ -- name: Deletes old vrf - sonic_vrfs: +- name: Delete trunk Vlans + sonic_l2_interfaces: config: [] state: deleted ignore_errors: yes - diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/tasks/main.yml new file mode 100644 index 000000000..b4e9498f5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/tasks/main.yml @@ -0,0 +1,14 @@ +- debug: msg="sonic_mac test started ..." + +- set_fact: + base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}" + +- name: Preparation tests + include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started ..." + include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: Cleanup tests + include_tasks: cleanup_tests.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/tasks/preparation_tests.yaml new file mode 100644 index 000000000..7dcd8a9a9 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/tasks/preparation_tests.yaml @@ -0,0 +1,37 @@ +- name: Initialize Vlans + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparations_tests.init_vlan }}" + +- name: Add trunk Vlans + sonic_l2_interfaces: + config: + - name: "{{interface1}}" + trunk: + allowed_vlans: + - vlan: 1 + - vlan: 2 + - name: "{{interface2}}" + trunk: + allowed_vlans: + - vlan: 1 + - vlan: 2 + - vlan: 3 + - name: "{{interface3}}" + trunk: + allowed_vlans: + - vlan: 2 + - vlan: 3 + +- name: Delete VRF configurations + sonic_vrfs: + config: [] + state: deleted + ignore_errors: yes + +- name: Delete MAC configurations + sonic_mac: + config: [] + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/tasks/tasks_template.yaml new file mode 100644 index 000000000..8bf357322 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mac/tasks/tasks_template.yaml @@ -0,0 +1,21 @@ +- name: "{{ item.name }} , {{ item.description }}" + sonic_mac: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name }} , {{ item.description }} Idempotent" + sonic_mac: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/defaults/main.yml index a2df2d365..743ae9bec 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/defaults/main.yml @@ -7,6 +7,21 @@ preparations_tests: - vlan_id: 5 - vlan_id: 6 - vlan_id: 2 + - vlan_id: 11 + - vlan_id: 12 + - vlan_id: 13 + - vlan_id: 14 + - vlan_id: 15 + - vlan_id: 21 + - vlan_id: 22 + - vlan_id: 23 + - vlan_id: 24 + - vlan_id: 25 + - vlan_id: 31 + - vlan_id: 32 + - vlan_id: 33 + - vlan_id: 34 + - vlan_id: 35 add_lag_interfaces: - name: Po10 - name: Po11 @@ -24,7 +39,9 @@ merged_tests: peer_link: "{{ interface1 }}" keepalive: 3 session_timeout: 300 + delay_restore: 480 system_mac: 00:00:00:01:01:01 + gateway_mac: 00:00:00:02:02:02 - name: test_case_02 description: Update created MCLAG properties @@ -36,6 +53,7 @@ merged_tests: peer_link: "{{ interface2 }}" keepalive: 2 session_timeout: 350 + delay_restore: 600 system_mac: 00:00:00:11:11:11 - name: test_case_03 @@ -47,11 +65,37 @@ merged_tests: vlans: - vlan: vlan4 - vlan: vlan5 + - vlan: vlan12-13 + - vlan: vlan21-22 + - vlan: vlan35 + peer_gateway: + vlans: + - vlan: vlan4 + - vlan: vlan5 + - vlan: vlan12-13 + - vlan: vlan21-22 + - vlan: vlan35 members: portchannels: - lag: Po10 - lag: Po11 + - name: test_case_04 + description: Update MCLAG properties - associate further vlans + state: merged + input: + domain_id: 1 + unique_ip: + vlans: + - vlan: vlan11-15 + - vlan: vlan21-25 + - vlan: vlan31-35 + peer_gateway: + vlans: + - vlan: vlan11-15 + - vlan: vlan21-25 + - vlan: vlan31-35 + delete_all: - name: del_all_test_case_01 description: Delete MCLAG properties @@ -68,11 +112,23 @@ updated_tests: peer_link: "{{ interface3 }}" keepalive: 3 session_timeout: 300 + delay_restore: 450 system_mac: 00:00:00:01:01:01 + gateway_mac: 00:00:00:03:03:03 unique_ip: vlans: - vlan: vlan2 - vlan: vlan6 + - vlan: vlan11-15 + - vlan: vlan21-25 + - vlan: vlan31-35 + peer_gateway: + vlans: + - vlan: vlan2 + - vlan: vlan6 + - vlan: vlan11-15 + - vlan: vlan21-25 + - vlan: vlan31-35 members: portchannels: - lag: Po13 @@ -88,10 +144,21 @@ updated_tests: peer_link: "{{ interface3 }}" keepalive: 3 session_timeout: 300 + delay_restore: 450 system_mac: 00:00:00:01:01:01 + gateway_mac: 00:00:00:03:03:03 unique_ip: vlans: - vlan: vlan2 + - vlan: vlan12-14 + - vlan: vlan23-25 + - vlan: vlan31-34 + peer_gateway: + vlans: + - vlan: vlan2 + - vlan: vlan12-14 + - vlan: vlan23-25 + - vlan: vlan31-34 members: portchannels: - lag: Po13 @@ -103,5 +170,120 @@ updated_tests: domain_id: 2 unique_ip: vlans: + peer_gateway: + vlans: + members: + portchannels: + +replaced_overridden_tests: + - name: test_case_06 + description: Create new MCLAG with all properties including VLANs and Portchannels + state: merged + input: + domain_id: 2 + source_address: 3.3.3.5 + peer_address: 1.1.1.3 + peer_link: "{{ interface3 }}" + keepalive: 3 + session_timeout: 300 + delay_restore: 450 + system_mac: 00:00:00:01:01:01 + gateway_mac: 00:00:00:03:03:03 + unique_ip: + vlans: + - vlan: vlan2 + - vlan: vlan11-15 + peer_gateway: + vlans: + - vlan: vlan2 + - vlan: vlan11-15 + members: + portchannels: + - lag: Po10 + - lag: Po11 + + - name: test_case_07 + description: Replace MCLAG Portchannels and VLANs + state: replaced + input: + domain_id: 2 + unique_ip: + vlans: + - vlan: vlan2 + - vlan: vlan11-13 + - vlan: vlan21-25 + peer_gateway: + vlans: + - vlan: vlan12-13 + - vlan: vlan22-24 + members: + portchannels: + - lag: Po11 + - lag: Po12 + + - name: test_case_08 + description: Replace MCLAG domain ID and properties + state: replaced + input: + domain_id: 20 + source_address: 3.3.3.5 + peer_address: 1.1.1.3 + peer_link: "{{ interface3 }}" + keepalive: 3 + session_timeout: 300 + delay_restore: 480 + gateway_mac: 00:00:00:12:12:12 + unique_ip: + vlans: + - vlan: vlan11-13 + - vlan: vlan21-25 + peer_gateway: + vlans: + - vlan: vlan12-13 members: portchannels: + - lag: Po10 + - lag: Po12 + + - name: test_case_09 + description: Replace MCLAG properties + state: replaced + input: + domain_id: 20 + source_address: 3.3.3.5 + peer_address: 1.1.1.3 + peer_link: "{{ interface3 }}" + system_mac: 00:00:00:01:01:01 + gateway_mac: 00:00:00:03:03:03 + unique_ip: + vlans: + - vlan: vlan11-15 + peer_gateway: + vlans: + - vlan: vlan11-15 + members: + portchannels: + - lag: Po10 + - lag: Po11 + + - name: test_case_10 + description: Override MCLAG properties including VLANs and Portchannels + state: overridden + input: + domain_id: 10 + source_address: 3.3.3.5 + peer_address: 1.1.1.3 + peer_link: "{{ interface3 }}" + keepalive: 3 + unique_ip: + vlans: + - vlan: vlan11-12 + - vlan: vlan31-32 + peer_gateway: + vlans: + - vlan: vlan11-12 + - vlan: vlan31-32 + members: + portchannels: + - lag: Po10 + - lag: Po12 diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..502489684 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/cleanup_tests.yaml @@ -0,0 +1,18 @@ +--- +- name: Delete MCLAG configuration + dellemc.enterprise_sonic.sonic_mclag: + config: + state: deleted + ignore_errors: yes + +- name: Delete test VLANs + dellemc.enterprise_sonic.sonic_vlans: + config: "{{ preparations_tests.add_vlans_input }}" + state: deleted + ignore_errors: yes + +- name: Delete test lag interfaces + dellemc.enterprise_sonic.sonic_lag_interfaces: + config: "{{ preparations_tests.add_lag_interfaces }}" + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/main.yml index 071ef9495..b8d2efa83 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/main.yml @@ -16,7 +16,13 @@ include_tasks: tasks_template.yaml loop: "{{ updated_tests }}" +- name: "Test {{ module_name }} started ..." + include_tasks: tasks_template.yaml + loop: "{{ replaced_overridden_tests }}" + +- name: Cleanup test + include_tasks: cleanup_tests.yaml + - name: Display all variables/facts known for a host debug: var: hostvars[inventory_hostname].ansible_facts.test_reports - diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/defaults/main.yml index 860297d6c..d5538e51c 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/defaults/main.yml @@ -3,6 +3,7 @@ ansible_connection: httpapi module_name: ntp po1: Portchannel 100 +po2: Portchannel 200 vlan1: Vlan 100 lo1: Loopback 100 @@ -11,15 +12,18 @@ mgmt_vrf: mgmt ntp_ip_server_1: 10.11.0.1 ntp_ip_server_2: 10.11.0.2 ntp_ip_server_3: 10.11.0.3 +ntp_ip_server_4: 10.11.0.4 ntp_host_server: pool.ntp.org preparations_tests: delete_interfaces: - "no interface {{ po1 }}" + - "no interface {{ po2 }}" - "no interface {{ vlan1 }}" - "no interface {{ lo1 }}" init_interfaces: - "interface {{ po1 }}" + - "interface {{ po2 }}" - "interface {{ vlan1 }}" - "interface {{ lo1 }}" delete_mgmt_vrf: @@ -81,22 +85,54 @@ tests: - address: "{{ ntp_ip_server_3 }}" minpoll: 7 maxpoll: 10 + prefer: true - name: test_case_06 + description: Replace NTP source interfaces and some servers + state: replaced + input: + source_interfaces: + - "{{ interface2 }}" + - "{{ po2 }}" + servers: + - address: "{{ ntp_ip_server_4 }}" + minpoll: 5 + maxpoll: 8 + - address: "{{ ntp_ip_server_3 }}" + minpoll: 5 + maxpoll: 8 + prefer: true + + - name: test_case_07 + description: Configure NTP source interfaces and server prefer to false + state: merged + input: + source_interfaces: + - "{{ interface1 }}" + - "{{ po1 }}" + - "{{ vlan1 }}" + - "{{ lo1 }}" + servers: + - address: "{{ ntp_ip_server_3 }}" + minpoll: 6 + maxpoll: 10 + prefer: false + + - name: test_case_08 description: Delete a NTP source interface state: deleted input: source_interfaces: - "{{ interface1 }}" - - name: test_case_07 + - name: test_case_09 description: Delete a NTP server state: deleted input: servers: - address: "{{ ntp_ip_server_1 }}" - - name: test_case_08 + - name: test_case_10 description: Delete several NTP source interfaces state: deleted input: @@ -104,7 +140,7 @@ tests: - "{{ interface2 }}" - "{{ po1 }}" - - name: test_case_09 + - name: test_case_11 description: Delete several NTP servers state: deleted input: @@ -112,7 +148,7 @@ tests: - address: "{{ ntp_ip_server_1 }}" - address: "{{ ntp_ip_server_3 }}" - - name: test_case_10 + - name: test_case_12 description: Delete NTP source interfaces and servers state: deleted input: @@ -124,25 +160,25 @@ tests: - address: "{{ ntp_ip_server_1 }}" - address: "{{ ntp_host_server }}" - - name: test_case_11 + - name: test_case_13 description: Configure NTP VRF state: merged input: vrf: "{{ mgmt_vrf }}" - - name: test_case_12 + - name: test_case_14 description: Delete NTP VRF state: deleted input: vrf: "{{ mgmt_vrf }}" - - name: test_case_13 + - name: test_case_15 description: Enable NTP authentication state: merged input: enable_ntp_auth: true - - name: test_case_14 + - name: test_case_16 description: Create NTP authentication keys state: merged input: @@ -156,7 +192,7 @@ tests: key_value: U2FsdGVkX1/wWVxmcp59mJQO6uzhFEHIxScdCbIqJh4= encrypted: true - - name: test_case_15 + - name: test_case_17 description: Configure NTP trusted keys state: merged input: @@ -164,7 +200,7 @@ tests: - 2 - 6 - - name: test_case_16 + - name: test_case_18 description: Create NTP servers with key state: merged input: @@ -174,7 +210,7 @@ tests: minpoll: 6 maxpoll: 9 - - name: test_case_17 + - name: test_case_19 description: Delete NTP trusted keys state: deleted input: @@ -182,14 +218,14 @@ tests: - 2 - 6 - - name: test_case_18 + - name: test_case_20 description: Delete NTP server state: deleted input: servers: - address: "{{ ntp_ip_server_1 }}" - - name: test_case_19 + - name: test_case_21 description: Delete NTP authentication keys state: deleted input: @@ -197,13 +233,52 @@ tests: - key_id: 2 - key_id: 6 - - name: test_case_20 + - name: test_case_22 description: Delete NTP authentication state: deleted input: enable_ntp_auth: true - - name: test_case_21 + - name: test_case_23 + description: Overridden NTP configuration + state: overridden + input: + enable_ntp_auth: false + source_interfaces: + - "{{ interface1 }}" + - "{{ interface2 }}" + - "{{ po1 }}" + - "{{ po2 }}" + servers: + - address: "{{ ntp_ip_server_1 }}" + minpoll: 6 + maxpoll: 9 + - address: "{{ ntp_ip_server_4 }}" + minpoll: 5 + maxpoll: 8 + - address: "{{ ntp_ip_server_3 }}" + minpoll: 5 + maxpoll: 8 + prefer: true + + - name: test_case_24 + description: Replace more NTP configuration + state: replaced + input: + enable_ntp_auth: true + source_interfaces: + - "{{ interface2 }}" + - "{{ po2 }}" + servers: + - address: "{{ ntp_ip_server_4 }}" + minpoll: 6 + maxpoll: 8 + - address: "{{ ntp_ip_server_3 }}" + minpoll: 5 + maxpoll: 8 + prefer: true + + - name: test_case_25 description: Delete all NTP configurations state: deleted input: {} diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/defaults/main.yml new file mode 100644 index 000000000..104b5af04 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/defaults/main.yml @@ -0,0 +1,122 @@ +--- +ansible_connection: httpapi +module_name: pki + +preparations_tests: + delete_pki: + - name: delete_all + description: Delete PKI + state: deleted + input: +tests: + - name: test_case_01 + description: Add new PKI configuration + state: merged + input: + security_profiles: + - profile_name: mysp + - profile_name: myspb + ocsp_responder_list: + - http://example.com/ocsp + trust_stores: + - name: myts + ca_name: CA2 + + - name: test_case_02 + description: Update specific params of security-profile + state: merged + input: + security_profiles: + - profile_name: mysp + cdp_list: + - http://example.com/cdp + ocsp_responder_list: + - http://example.com/ocspb + - http://example.com/ocspc + trust_store: myts + revocation_check: false + - profile_name: mysp2 + trust_store: myts2 + trust_stores: + - name: myts + ca_name: + - CA2 + - CA + - name: myts2 + ca_name: CA + - name: test_case_03 + description: Delete specific params of security-profile + state: deleted + input: + security_profiles: + - profile_name: mysp + cdp_list: + - http://example.com/cdp + - profile_name: mysp2 + trust_store: myts2 + - name: test_case_04 + description: Delete all security_profiles configurations + state: deleted + input: + security_profiles: + - profile_name: mysp + - profile_name: myspb + + - name: test_case_05 + description: Merge parameter of security_profiles configurations + state: merged + input: + security_profiles: + - profile_name: mysp + revocation_check: false + peer_name_check: true + - profile_name: myspb + ocsp_responder_list: + - http://example.com/ocsp + trust_store: myts + key_usage_check: true + + - name: test_case_06 + description: Replace some parameter of security_profiles + state: replaced + input: + security_profiles: + - profile_name: mysp + revocation_check: true + peer_name_check: false + cdp_list: + - http://example.com/cdp + ocsp_responder_list: + - http://example.com/ocspb + - http://example.com/ocspc + trust_store: myts + + - name: test_case_07 + description: Replace CA of trust_store + state: replaced + input: + trust_stores: + - name: myts + ca_name: CA + + - name: test_case_08 + description: Override parameter of security_profiles and trust_stores + state: overridden + input: + security_profiles: + - profile_name: newsp + revocation_check: false + peer_name_check: true + - profile_name: newspb + ocsp_responder_list: + - http://example.com/ocsp + key_usage_check: true + trust_store: newts + trust_stores: + - name: newts + ca_name: CA + +test_delete_all: + - name: test_case_09 + description: Delete all PKI configurations + state: deleted diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/meta/main.yaml new file mode 100644 index 000000000..0b356217e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/meta/main.yaml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common } diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..b1ffad57e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/cleanup_tests.yaml @@ -0,0 +1,6 @@ +- name: Deletes old PKI + sonic_pki: + config: {} + state: deleted + ignore_errors: yes + diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/main.yml new file mode 100644 index 000000000..bd20b1292 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/main.yml @@ -0,0 +1,16 @@ +- debug: msg="sonic_pki Test started ..." + +- set_fact: + base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}" + +- name: Preparations test + include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started ..." + include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: "test_delete_all {{ module_name }} stated ..." + include_tasks: tasks_template_del.yaml + loop: "{{ test_delete_all }}" + when: test_delete_all is defined diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/preparation_tests.yaml new file mode 100644 index 000000000..241e75a81 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/preparation_tests.yaml @@ -0,0 +1,5 @@ +- name: Deletes old PKI configurations + sonic_pki: + config: {} + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/tasks_template.yaml new file mode 100644 index 000000000..ff6bce99e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/tasks_template.yaml @@ -0,0 +1,21 @@ +- name: "{{ item.name}} , {{ item.description}}" + sonic_pki: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name}} , {{ item.description}} Idempotent" + sonic_pki: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/tasks_template_del.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/tasks_template_del.yaml new file mode 100644 index 000000000..4b914364e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_pki/tasks/tasks_template_del.yaml @@ -0,0 +1,21 @@ +- name: "{{ item.name}} , {{ item.description}}" + sonic_pki: + state: "{{ item.state }}" + config: + register: action_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name}} , {{ item.description}} Idempotent" + sonic_pki: + state: "{{ item.state }}" + config: + register: idempotent_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/defaults/main.yml index 402088be5..dd31e4afc 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/defaults/main.yml @@ -2,40 +2,21 @@ ansible_connection: httpapi module_name: port_breakout -preparations_tests: - delete_port_breakouts: - - "no interface breakout port 1/97" - - "no interface breakout port 1/98" - - "no interface breakout port 1/99" - - "no interface breakout port 1/100" - - "no interface breakout port 1/101" - - "no interface breakout port 1/102" - -tests_cli: - - name: cli_test_case_01 - description: Configure breakout mode for ports - state: merged - input: - - name: 1/97 - mode: 4x25G - - name: 1/98 - mode: 1x40G - tests: - name: test_case_01 description: Configure breakout mode for ports state: merged input: - name: 1/97 - mode: 4x25G - - name: 1/98 mode: 1x40G + - name: 1/98 + mode: 1x50G - name: 1/99 - mode: 4x25G + mode: 1x100G - name: 1/100 - mode: 4x10G + mode: 2x50G - name: 1/101 - mode: 1x40G + mode: 4x10G - name: 1/102 mode: 4x25G - name: test_case_02 @@ -43,7 +24,7 @@ tests: state: merged input: - name: 1/97 - mode: 1x40G + mode: 2x50G - name: 1/98 mode: 4x10G - name: test_case_03 @@ -52,6 +33,22 @@ tests: input: - name: 1/98 - name: test_case_04 + description: Replace breakout mode for ports + state: replaced + input: + - name: 1/97 + mode: 4x10G + - name: 1/98 + mode: 4x10G + - name: test_case_05 + description: Override breakout mode for ports + state: overridden + input: + - name: 1/100 + mode: 4x10G + - name: 1/101 + mode: 2x50G + - name: test_case_06 description: deleting all the port breakout modes state: deleted input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/cleanup_tests.yaml index fc7e76dda..6dbbc8263 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/cleanup_tests.yaml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/cleanup_tests.yaml @@ -1,4 +1,4 @@ -- name: Deletes old bgp +- name: Deletes old port breakout configuration sonic_port_breakout: config: [] state: deleted diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/main.yml index 2dea65315..5e2e87c05 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/main.yml @@ -1,31 +1,15 @@ - debug: msg="sonic_port_breakout Test started ..." -- set_fact: - base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}" - -- name: Preparations test +- name: Preparation test include_tasks: preparation_tests.yaml -- name: "Test {{ module_name }} CLI validation started ..." +- name: "Test {{ module_name }} started ..." include_tasks: tasks_template.yaml - loop: "{{ tests_cli }}" - -- name: "Test CLI validation started ..." - include_role: - name: common - tasks_from: cli_tasks_template.yaml - loop: "{{ tests_cli }}" - -# - name: Preparations test -# include_tasks: preparation_tests.yaml - -# - name: "Test {{ module_name }} started ..." -# include_tasks: tasks_template.yaml -# loop: "{{ tests }}" + loop: "{{ tests }}" -# - name: Cleanup tests -# include_tasks: cleanup_tests.yaml +- name: Cleanup tests + include_tasks: cleanup_tests.yaml -# - name: Display all variables/facts known for a host -# debug: -# var: hostvars[inventory_hostname].ansible_facts.test_reports +- name: Display the full test report + debug: + var: hostvars[inventory_hostname].ansible_facts.test_reports diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/tasks_template.yaml index 04ab1b456..958a69f27 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/tasks_template.yaml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/tasks_template.yaml @@ -8,14 +8,18 @@ - import_role: name: common tasks_from: action.facts.report.yaml - + - name: "{{ item.name}} , {{ item.description}} Idempotent" sonic_port_breakout: config: "{{ item.input }}" state: "{{ item.state }}" register: idempotent_task_output ignore_errors: yes - + - import_role: name: common tasks_from: idempotent.facts.report.yaml + +- name: "Pause before the next test" + ansible.builtin.pause: + seconds: 10 diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/templates/cli_test_case_01.cfg b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/templates/cli_test_case_01.cfg deleted file mode 100644 index c08c5950c..000000000 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/templates/cli_test_case_01.cfg +++ /dev/null @@ -1,2 +0,0 @@ -interface breakout port 1/97 mode 4x25G -interface breakout port 1/98 mode 1x40G diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/defaults/main.yml new file mode 100644 index 000000000..89babf8d9 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/defaults/main.yml @@ -0,0 +1,82 @@ +--- +ansible_connection: httpapi +module_name: port_group + +pg1: 1 +pg2: 2 +pg16: 16 +pg20: 20 + +preparations_tests: + delete_port_group_configurations: + - "no port-group {{ pg1 }} speed" + - "no port-group {{ pg2 }} speed" + - "no port-group {{ pg16 }} speed" + - "no port-group {{ pg20 }} speed" + +tests: + - name: test_case_01 + description: Configure port group speeds + state: merged + input: + - id: "{{ pg1 }}" + speed: SPEED_10GB + - id: "{{ pg2 }}" + speed: SPEED_25GB + - id: "{{ pg16 }}" + speed: SPEED_10GB + - id: "{{ pg20 }}" + speed: SPEED_25GB + - name: test_case_02 + description: Delete some port group speeds + state: deleted + input: + - id: "{{ pg1 }}" + - id: "{{ pg2 }}" + - name: test_case_03 + description: Set all port group to default speeds + state: deleted + input: + - id: + - name: test_case_04 + description: Set some port group speeds + state: merged + input: + - id: "{{ pg16 }}" + speed: SPEED_10GB + - id: "{{ pg20 }}" + speed: SPEED_25GB + - name: test_case_05 + description: Replace some port group speeds + state: replaced + input: + - id: "{{ pg1 }}" + speed: SPEED_10GB + - id: "{{ pg20 }}" + speed: SPEED_10GB + - name: test_case_06 + description: Replace more port group speeds + state: replaced + input: + - id: "{{ pg1 }}" + speed: SPEED_25GB + - id: "{{ pg2 }}" + speed: SPEED_10GB + - id: "{{ pg16 }}" + speed: SPEED_10GB + - id: "{{ pg20 }}" + speed: SPEED_10GB + - name: test_case_07 + description: Override port group speeds + state: overridden + input: + - id: "{{ pg1 }}" + speed: SPEED_10GB + - id: "{{ pg2 }}" + speed: SPEED_10GB + - id: "{{ pg20 }}" + speed: SPEED_25GB + - name: test_case_08 + description: Clean up - reset all port group speeds + state: deleted + input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/meta/main.yaml new file mode 100644 index 000000000..611fd54d2 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/meta/main.yaml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common }
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..af476de06 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/tasks/cleanup_tests.yaml @@ -0,0 +1,7 @@ +- name: Reset port groups + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparations_tests.delete_port_group_configurations }}" + register: output + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/tasks/main.yml new file mode 100644 index 000000000..8c430cce9 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/tasks/main.yml @@ -0,0 +1,16 @@ +- debug: msg="sonic_port_group Test started ..." + +- name: Preparations tests + include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started ..." + include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: "Cleanup test {{ module_name }} started" + include_tasks: cleanup_tests.yaml + +- name: Display all variables/facts known for a host + debug: + var: hostvars[inventory_hostname].ansible_facts.test_reports + diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/tasks/preparation_tests.yaml new file mode 100644 index 000000000..b9104df7b --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/tasks/preparation_tests.yaml @@ -0,0 +1,7 @@ +- name: Initialize port groups + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparations_tests.delete_port_group_configurations }}" + register: output + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/tasks/tasks_template.yaml new file mode 100644 index 000000000..936cadb54 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_group/tasks/tasks_template.yaml @@ -0,0 +1,21 @@ +- name: "{{ item.name}} , {{ item.description}}" + sonic_port_group: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name}} , {{ item.description}} Idempotent" + sonic_port_group: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/defaults/main.yml index be5199910..6df415c3c 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/defaults/main.yml @@ -83,40 +83,98 @@ tests: le: 29 - name: test_case_05 - description: Delete prefixes from existing prefix sets - state: deleted + description: Override prefix list configuration + state: overridden input: - afi: ipv4 - name: pf2 + name: pf6 + prefixes: + - action: deny + sequence: 10 + prefix: 1.2.3.0/24 + ge: 25 + le: 27 + - afi: ipv4 + name: pf7 prefixes: - action: deny prefix: 10.20.30.0/24 sequence: 20 ge: 26 + - action: permit + prefix: 10.20.30.128/25 + sequence: 50 + ge: 27 + le: 29 - afi: ipv4 - name: pf3 + name: pf8 + prefixes: + - action: deny + prefix: 1.2.3.128/25 + sequence: 30 + le: 27 + + - name: test_case_06 + description: Replace prefix list configuration + state: replaced + input: + - afi: ipv4 + name: pf6 prefixes: - action: permit - prefix: 1.2.3.192/26 - sequence: 40 - ge: 28 - le: 30 + sequence: 10 + prefix: 1.2.3.0/24 + ge: 25 + le: 27 + - action: deny + sequence: 11 + prefix: 1.2.4.0/24 + ge: 26 + le: 28 - afi: ipv4 - name: pf5 + name: pf7 + prefixes: + - action: deny + prefix: 10.20.30.128/25 + sequence: 50 + ge: 27 + le: 29 + - afi: ipv6 + name: pf9 prefixes: - action: permit - prefix: 15.25.35.0/24 - sequence: 15 + sequence: 32 + prefix: 60:70::/64 - - name: test_case_06 + - name: test_case_07 + description: Delete prefixes from existing prefix sets + state: deleted + input: + - afi: ipv4 + name: pf6 + prefixes: + - action: permit + sequence: 10 + prefix: 1.2.3.0/24 + ge: 25 + le: 27 + - afi: ipv4 + name: pf7 + prefixes: + - action: deny + prefix: 10.20.30.0/24 + sequence: 20 + ge: 26 + + - name: test_case_08 description: Delete prefix sets from the existing configuration state: deleted input: - - name: pf1 - - name: pf4 + - name: pf6 + - name: pf9 afi: ipv6 - - name: test_case_07 + - name: test_case_09 description: Delete all prefix set configuration state: deleted input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/defaults/main.yml index 6a79dc88d..9ef0e3094 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/defaults/main.yml @@ -108,7 +108,79 @@ tests: source_interface: "{{ interface3 }}" retransmit: 9 -test_delete_all: - name: test_case_06 - description: delete all the configurations of radius server + description: Replace some configuration of radius servers + state: replaced + input: + auth_type: mschapv2 + timeout: 36 + nas_ip: 11.11.11.22 + retransmit: 5 + statistics: true + servers: + host: + - name: my_host + auth_type: chap + port: 55 + timeout: 12 + priority: 3 + source_interface: "{{ interface3 }}" + + - name: test_case_07 + description: Replace hosts of radius servers + state: replaced + input: + auth_type: mschapv2 + timeout: 36 + nas_ip: 11.11.11.22 + retransmit: 5 + statistics: true + servers: + host: + - name: my_host + auth_type: chap + port: 55 + timeout: 21 + priority: 3 + source_interface: "{{ interface3 }}" + - name: 20.21.22.23 + auth_type: pap + port: 50 + timeout: 38 + priority: 4 + source_interface: "{{ interface2 }}" + - name: 18.21.22.23 + auth_type: chap + port: 20 + timeout: 19 + priority: 8 + source_interface: "{{ interface1 }}" + + - name: test_case_08 + description: Override configuration of radius server + state: overridden + input: + auth_type: mschapv2 + timeout: 20 + nas_ip: 10.10.10.20 + retransmit: 3 + servers: + host: + - name: 10.11.11.11 + auth_type: pap + port: 55 + timeout: 12 + priority: 3 + retransmit: 8 + source_interface: "{{ interface2 }}" + - name: your_host + auth_type: chap + port: 50 + timeout: 30 + priority: 6 + source_interface: "{{ interface3 }}" + +test_delete_all: + - name: test_case_09 + description: Delete all the configurations of radius server state: deleted diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/defaults/main.yml new file mode 100644 index 000000000..b0fc48be8 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/defaults/main.yml @@ -0,0 +1,1060 @@ +--- +ansible_connection: httpapi +module_name: sonic_route_maps + +preparation_tests: + init_vrfs: + - 'ip vrf Vrf1' + init_prefix_lists: + - 'ip prefix-list ip_pfx_list1 seq 40 permit 1.2.3.0/24' + - 'ip prefix-list ip_pfx_list2 seq 50 permit 5.6.7.0/24' + - 'ip prefix-list ip_pfx_list3 seq 89 permit 12.13.14.0/24' + - 'ipv6 prefix-list ipv6_pfx_list1 seq 40 permit 30:40::/64' + - 'ipv6 prefix-list ipv6_pfx_list2 seq 75 deny 20:80::/70' + init_bgp_communities: + - 'bgp community-list standard bgp_comm_list1 permit 67:35' + - 'bgp community-list standard bgp_comm_list2 permit local-as' + - 'bgp community-list standard bgp_comm_list3 permit 25:34' + init_bgp_extcommunities: + - 'bgp extcommunity-list standard bgp_ext_comm1 permit rt 35:45' + - 'bgp extcommunity-list standard bgp_ext_comm2 permit soo 21:43' + init_bgp_as_paths: + - 'bgp as-path-list bgp_as1 permit "34"' + - 'bgp as-path-list bgp_as2 permit "97"' + - 'bgp as-path-list bgp_as3 permit "270"' + init_interfaces: + - 'interface Vlan7' + - 'interface PortChannel 14' + +cleanup_tests: + delete_vrfs: + - 'no ip vrf Vrf1' + delete_prefix_lists: + - 'no ip prefix-list ip_pfx_list1 seq 40 permit 1.2.3.0/24' + - 'no ip prefix-list ip_pfx_list2 seq 50 permit 5.6.7.0/24' + - 'no ip prefix-list ip_pfx_list3 seq 89 permit 12.13.14.0/24' + - 'no ipv6 prefix-list ipv6_pfx_list1 seq 40 permit 30:40::/64' + - 'no ipv6 prefix-list ipv6_pfx_list2 seq 75 deny 20:80::/70' + delete_bgp_communities: + - 'no bgp community-list standard bgp_comm_list1 permit 67:35' + - 'no bgp community-list standard bgp_comm_list2 permit local-as' + - 'no bgp community-list standard bgp_comm_list3 permit 25:34' + delete_bgp_extcommunities: + - 'no bgp extcommunity-list standard bgp_ext_comm1 permit rt 35:45' + - 'no bgp extcommunity-list standard bgp_ext_comm2 permit soo 21:43' + delete_bgp_as_paths: + - 'no bgp as-path-list bgp_as1 permit "34"' + - 'no bgp as-path-list bgp_as2 permit "97"' + - 'no bgp as-path-list bgp_as3 permit "270"' + delete_interfaces: + - 'no interface Vlan7' + - 'no interface PortChannel 14' + +tests: + - name: test_case_01_merged_base_rm1_80_match + description: Add initial route map match configuration for map rm1 80 + state: merged + input: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + as_path: bgp_as1 + community: bgp_comm_list1 + evpn: + default_route: true + vni: 735 + ext_comm: bgp_ext_comm1 + interface: Ethernet8 + ip: + address: ip_pfx_list1 + ipv6: + address: ipv6_pfx_list1 + local_preference: 8000 + metric: 400 + origin: egp + peer: + ip: 10.20.30.40 + source_protocol: bgp + source_vrf: Vrf1 + tag: 7284 + + - name: test_case_02_merged_base_rm1_80_set + description: Add initial route map set configuration for map rm1 80 + state: merged + input: + - map_name: rm1 + action: permit + sequence_num: 80 + set: + as_path_prepend: 200,315,7135 + comm_list_delete: bgp_comm_list2 + community: + community_number: + - "35:58" + - "79:150" + - "308:650" + community_attributes: + - local_as + - no_advertise + - no_export + - no_peer + - additive + extcommunity: + rt: + - "30:40" + soo: + - "10.73.14.9:78" + ip_next_hop: 10.48.16.18 + ipv6_next_hop: + global_addr: 30::30 + prefer_global: true + local_preference: 635 + metric: + value: 870 + origin: egp + weight: 93471 + - name: test_case_03_merged_base_other_route_maps + description: Add initial route map match configuration for other maps + state: merged + input: + - map_name: rm1 + action: deny + sequence_num: 3047 + match: + evpn: + route_type: multicast + origin: incomplete + peer: + interface: Ethernet16 + source_protocol: ospf + set: + community: + community_attributes: + - no_advertise + metric: + rtt_action: subtract + origin: incomplete + - map_name: rm3 + action: deny + sequence_num: 285 + match: + evpn: + route_type: macip + origin: igp + peer: + ipv6: 87:95:15::53 + source_protocol: connected + set: + community: + community_attributes: + - none + metric: + rtt_action: set + origin: igp + call: rm1 + - map_name: rm4 + action: permit + sequence_num: 480 + match: + evpn: + route_type: prefix + source_protocol: static + set: + metric: + rtt_action: subtract + - name: test_case_04_merged_modify_rm1_80_match + description: Modify route map match configuration for map rm1 80 + state: merged + input: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + as_path: bgp_as2 + community: bgp_comm_list3 + evpn: + route_type: prefix + vni: 850 + interface: Vlan7 + ip: + address: ip_pfx_list2 + next_hop: ip_pfx_list3 + peer: + interface: PortChannel14 + - name: test_case_05_merged_modify_rm1_80_set + description: Modify route map set configuration for map rm1 80 + state: merged + input: + - map_name: rm1 + action: permit + sequence_num: 80 + set: + as_path_prepend: 188,257 + community: + community_number: + - 45:736 + ipv6_next_hop: + prefer_global: false + metric: + rtt_action: add + - name: test_case_06_merged_modify_other_route_maps + description: Modify route map configuration for other maps + state: merged + input: + - map_name: rm1 + action: deny + sequence_num: 3047 + match: + as_path: bgp_as3 + ext_comm: bgp_ext_comm2 + origin: igp + source_protocol: connected + set: + community: + community_attributes: + - local_as + - no_advertise + - map_name: rm2 + action: permit + sequence_num: 100 + match: + interface: Ethernet16 + source_protocol: ospf + origin: incomplete + peer: + ip: 5.6.7.8 + set: + as_path_prepend: 200,300,400 + ipv6_next_hop: + global_addr: 37::58 + prefer_global: true + metric: + value: 8000 + - map_name: rm3 + action: deny + sequence_num: 285 + match: + local_preference: 14783 + source_protocol: bgp + - name: test_case_07_deleted_individual_attributes + description: Delete at least one attribute of each type + state: deleted + input: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + as_path: bgp_as2 + community: bgp_comm_list3 + evpn: + default_route: true + route_type: prefix + vni: 850 + ext_comm: bgp_ext_comm1 + interface: Vlan7 + ip: + address: ip_pfx_list2 + next_hop: ip_pfx_list3 + ipv6: + address: ipv6_pfx_list1 + local_preference: 8000 + metric: 400 + origin: egp + peer: + interface: PortChannel14 + source_vrf: Vrf1 + tag: 7284 + set: + as_path_prepend: 188,257 + comm_list_delete: bgp_comm_list2 + community: + community_number: + - "35:58" + community_attributes: + - local_as + extcommunity: + rt: + - "30:40" + soo: + - 10.73.14.9:78 + ip_next_hop: 10.48.16.18 + ipv6_next_hop: + global_addr: 30::30 + prefer_global: false + local_preference: 635 + metric: + rtt_action: add + origin: egp + weight: 93471 + - map_name: rm1 + action: deny + sequence_num: 3047 + match: + evpn: + route_type: multicast + origin: igp + source_protocol: connected + set: + metric: + rtt_action: subtract + origin: incomplete + - map_name: rm2 + action: permit + sequence_num: 100 + match: + peer: + ip: 5.6.7.8 + source_protocol: ospf + set: + metric: + value: 8000 + ipv6_next_hop: + prefer_global: true + - map_name: rm3 + action: deny + sequence_num: 285 + match: + evpn: + route_type: macip + peer: + ipv6: 87:95:15::53 + source_protocol: bgp + set: + metric: + rtt_action: set + origin: igp + call: rm1 + - map_name: rm4 + action: permit + sequence_num: 480 + match: + source_protocol: static + - name: test_case_08_merged_restore_rm1_80_match + description: Restore route map match configuration for map rm1 80 + state: merged + input: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + as_path: bgp_as2 + community: bgp_comm_list3 + evpn: + default_route: true + route_type: prefix + vni: 850 + ext_comm: bgp_ext_comm1 + interface: Vlan7 + ip: + address: ip_pfx_list2 + next_hop: ip_pfx_list3 + ipv6: + address: ipv6_pfx_list1 + local_preference: 8000 + metric: 400 + origin: egp + peer: + interface: PortChannel14 + source_protocol: bgp + source_vrf: Vrf1 + tag: 7284 + - name: test_case_09_merged_restore_rm1_80_set + description: Restore route map set configuration for map rm1 80 + state: merged + input: + - map_name: rm1 + action: permit + sequence_num: 80 + set: + as_path_prepend: 188,257 + comm_list_delete: bgp_comm_list2 + community: + community_attributes: + - additive + - local_as + - no_advertise + - no_export + - no_peer + community_number: + - "35:58" + - "79:150" + - "308:650" + - "45:736" + extcommunity: + rt: + - "30:40" + soo: + - "10.73.14.9:78" + ip_next_hop: 10.48.16.18 + ipv6_next_hop: + global_addr: 30::30 + prefer_global: false + local_preference: 635 + metric: + rtt_action: add + origin: egp + weight: 93471 + - name: test_case_10_merged_restore_other_route_maps + description: Restore route map configuration for other maps + state: merged + input: + - map_name: rm1 + action: deny + sequence_num: 3047 + match: + as_path: bgp_as3 + evpn: + route_type: multicast + ext_comm: bgp_ext_comm2 + origin: igp + peer: + interface: Ethernet16 + source_protocol: connected + set: + community: + community_attributes: + - local_as + - no_advertise + metric: + rtt_action: subtract + origin: incomplete + - map_name: rm2 + action: permit + sequence_num: 100 + match: + interface: Ethernet16 + origin: incomplete + peer: + ip: 5.6.7.8 + source_protocol: ospf + set: + as_path_prepend: 200,300,400 + ipv6_next_hop: + global_addr: 37::58 + prefer_global: true + metric: + value: 8000 + - map_name: rm3 + action: deny + sequence_num: 285 + match: + evpn: + route_type: macip + local_preference: 14783 + origin: igp + peer: + ipv6: 87:95:15::53 + source_protocol: bgp + set: + community: + community_attributes: + - none + metric: + rtt_action: set + origin: igp + call: rm1 + - map_name: rm4 + action: permit + sequence_num: 480 + match: + evpn: + route_type: prefix + source_protocol: static + set: + metric: + rtt_action: subtract + - name: test_case_11_replaced_dict_and_list + description: Replaced state handling for dict and list attributes + state: replaced + input: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + evpn: + route_type: multicast + ip: + address: ip_pfx_list1 + set: + community: + community_attributes: + - no_advertise + community_number: + - "25:25" + extcommunity: + rt: + - "20:20" + soo: + - "45:55" + ipv6_next_hop: + global_addr: 30::30 + - map_name: rm2 + action: permit + sequence_num: 100 + set: + ipv6_next_hop: + global_addr: 45::90 + - name: test_case_12_merged_restore_rm1_80_match + description: Restore route map match configuration for map rm1 80 + state: merged + input: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + as_path: bgp_as2 + community: bgp_comm_list3 + evpn: + default_route: true + route_type: prefix + vni: 850 + ext_comm: bgp_ext_comm1 + interface: Vlan7 + ip: + address: ip_pfx_list2 + next_hop: ip_pfx_list3 + ipv6: + address: ipv6_pfx_list1 + local_preference: 8000 + metric: 400 + origin: egp + peer: + interface: PortChannel14 + source_protocol: bgp + source_vrf: Vrf1 + tag: 7284 + - name: test_case_13_merged_restore_rm1_80_set + description: Restore route map set configuration for map rm1 80 + state: merged + input: + - map_name: rm1 + action: permit + sequence_num: 80 + set: + as_path_prepend: 188,257 + comm_list_delete: bgp_comm_list2 + community: + community_attributes: + - additive + - local_as + - no_advertise + - no_export + - no_peer + community_number: + - "35:58" + - "79:150" + - "308:650" + - "45:736" + extcommunity: + rt: + - "30:40" + soo: + - "10.73.14.9:78" + ip_next_hop: 10.48.16.18 + ipv6_next_hop: + global_addr: 30::30 + prefer_global: false + local_preference: 635 + metric: + rtt_action: add + origin: egp + weight: 93471 + - name: test_case_14_merged_restore_other_route_maps + description: Restore route map configuration for other replaced maps (rm2 "set") + state: merged + input: + - map_name: rm2 + action: permit + sequence_num: 100 + set: + as_path_prepend: 200,300,400 + ipv6_next_hop: + global_addr: 37::58 + prefer_global: true + metric: + value: 8000 + - name: test_case_15_replaced_top_level_match_attr_rm1_80 + description: Replaced state handling for top level match attributes in rm1 80 + state: replaced + input: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + as_path: bgp_as3 + ext_comm: bgp_ext_comm2 + - name: test_case_16_replaced_top_level_attr_other_route_maps + description: Replaced state handling for top level attributes in other route maps + state: replaced + input: + - map_name: rm1 + action: permit + sequence_num: 80 + set: + origin: egp + - map_name: rm1 + action: deny + sequence_num: 3047 + match: + interface: Ethernet8 + ipv6: + address: ipv6_pfx_list1 + origin: egp + - map_name: rm2 + action: permit + sequence_num: 100 + set: + as_path_prepend: 375,94 + - map_name: rm3 + action: deny + sequence_num: 285 + match: + local_preference: 14783 + set: + comm_list_delete: bgp_comm_list1 + - name: test_case_17_deleted_route_map_statement + description: Delete entire route map "statements" (can be sub route maps sharing a map name) + state: deleted + input: + - map_name: rm1 + sequence_num: 3047 + - map_name: rm2 + sequence_num: 100 + - name: test_case_18_merged_restore_deleted_route_maps + description: Restore route map configuration for deleted maps + state: merged + input: + - map_name: rm1 + action: deny + sequence_num: 3047 + match: + as_path: bgp_as3 + evpn: + route_type: multicast + ext_comm: bgp_ext_comm2 + origin: igp + peer: + interface: Ethernet16 + source_protocol: connected + set: + community: + community_attributes: + - local_as + - no_advertise + metric: + rtt_action: subtract + origin: incomplete + - map_name: rm2 + action: permit + sequence_num: 100 + match: + interface: Ethernet16 + origin: incomplete + peer: + ip: 5.6.7.8 + source_protocol: ospf + set: + as_path_prepend: 200,300,400 + ipv6_next_hop: + global_addr: 37::58 + prefer_global: true + metric: + value: 8000 + - name: test_case_19_deleted_entire_route_maps + description: Delete entire route maps (single statement and multiple statement) + state: deleted + input: + - map_name: rm3 + - map_name: rm1 + - name: test_case_20_merged_restore_rm1_80_match + description: Restore route map match configuration for map rm1 80 + state: merged + input: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + as_path: bgp_as2 + community: bgp_comm_list3 + evpn: + default_route: true + route_type: prefix + vni: 850 + ext_comm: bgp_ext_comm1 + interface: Vlan7 + ip: + address: ip_pfx_list2 + next_hop: ip_pfx_list3 + ipv6: + address: ipv6_pfx_list1 + local_preference: 8000 + metric: 400 + origin: egp + peer: + interface: PortChannel14 + source_protocol: bgp + source_vrf: Vrf1 + tag: 7284 + - name: test_case_21_merged_restore_rm1_80_set + description: Restore route map set configuration for map rm1 80 + state: merged + input: + - map_name: rm1 + action: permit + sequence_num: 80 + set: + as_path_prepend: 188,257 + comm_list_delete: bgp_comm_list2 + community: + community_attributes: + - additive + - local_as + - no_advertise + - no_export + - no_peer + community_number: + - "35:58" + - "79:150" + - "308:650" + - "45:736" + extcommunity: + rt: + - "30:40" + soo: + - "10.73.14.9:78" + ip_next_hop: 10.48.16.18 + ipv6_next_hop: + global_addr: 30::30 + prefer_global: false + local_preference: 635 + metric: + rtt_action: add + origin: egp + weight: 93471 + - name: test_case_22_merged_restore_deleted_rm1_3047_and_rm3_route_map + description: Restore deleted route maps rm1 3047 and rm3 + state: merged + input: + - map_name: rm1 + action: deny + sequence_num: 3047 + match: + as_path: bgp_as3 + evpn: + route_type: multicast + ext_comm: bgp_ext_comm2 + origin: igp + peer: + interface: Ethernet16 + source_protocol: connected + set: + community: + community_attributes: + - local_as + - no_advertise + metric: + rtt_action: subtract + origin: incomplete + - map_name: rm3 + action: deny + sequence_num: 285 + match: + evpn: + route_type: macip + local_preference: 14783 + origin: igp + peer: + ipv6: 87:95:15::53 + source_protocol: bgp + set: + community: + community_attributes: + - none + metric: + rtt_action: set + origin: igp + call: rm1 + - name: test_case_23_overridden_existing_route_map_subset + description: Override with an existing configured route map subset + state: overridden + input: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + evpn: + vni: 735 + - name: test_case_24_merged_restore_rm1_80_match + description: Restore route map match configuration for map rm1 80 + state: merged + input: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + as_path: bgp_as2 + community: bgp_comm_list3 + evpn: + default_route: true + route_type: prefix + vni: 850 + ext_comm: bgp_ext_comm1 + interface: Vlan7 + ip: + address: ip_pfx_list2 + next_hop: ip_pfx_list3 + ipv6: + address: ipv6_pfx_list1 + local_preference: 8000 + metric: 400 + origin: egp + peer: + interface: PortChannel14 + source_protocol: bgp + source_vrf: Vrf1 + tag: 7284 + - name: test_case_25_merged_restore_rm1_80_set + description: Restore route map set configuration for map rm1 80 + state: merged + input: + - map_name: rm1 + action: permit + sequence_num: 80 + set: + as_path_prepend: 188,257 + comm_list_delete: bgp_comm_list2 + community: + community_attributes: + - additive + - local_as + - no_advertise + - no_export + - no_peer + community_number: + - "35:58" + - "79:150" + - "308:650" + - "45:736" + extcommunity: + rt: + - "30:40" + soo: + - "10.73.14.9:78" + ip_next_hop: 10.48.16.18 + ipv6_next_hop: + global_addr: 30::30 + prefer_global: false + local_preference: 635 + metric: + rtt_action: add + origin: egp + weight: 93471 + - name: test_case_26_merged_restore_deleted_rm1_3047_and_rm3_route_map + description: Restore deleted route maps rm1 3047 and rm3 + state: merged + input: + - map_name: rm1 + action: deny + sequence_num: 3047 + match: + as_path: bgp_as3 + evpn: + route_type: multicast + ext_comm: bgp_ext_comm2 + origin: igp + peer: + interface: Ethernet16 + source_protocol: connected + set: + community: + community_attributes: + - local_as + - no_advertise + metric: + rtt_action: subtract + origin: incomplete + - map_name: rm3 + action: deny + sequence_num: 285 + match: + evpn: + route_type: macip + local_preference: 14783 + origin: igp + peer: + ipv6: 87:95:15::53 + source_protocol: bgp + set: + community: + community_attributes: + - none + metric: + rtt_action: set + origin: igp + call: rm1 + - name: test_case_27_overridden_modified_route_map_subset + description: Override with a change to existing configured route map configuration + state: overridden + input: + - map_name: rm1 + action: permit + sequence_num: 80 + match: + ip: + address: ip_pfx_list2 + - name: test_case_28_merged_restore_some_deleted_route_maps + description: Restore some deleted route map configuration + state: merged + input: + - map_name: rm1 + action: deny + sequence_num: 3047 + match: + as_path: bgp_as3 + evpn: + route_type: multicast + ext_comm: bgp_ext_comm2 + origin: igp + peer: + interface: Ethernet16 + source_protocol: connected + set: + community: + community_attributes: + - local_as + - no_advertise + metric: + rtt_action: subtract + origin: incomplete + - map_name: rm2 + action: permit + sequence_num: 100 + match: + interface: Ethernet16 + origin: incomplete + peer: + ip: 5.6.7.8 + source_protocol: ospf + set: + as_path_prepend: 200,300,400 + ipv6_next_hop: + global_addr: 37::58 + prefer_global: true + metric: + value: 8000 + - map_name: rm3 + action: deny + sequence_num: 285 + match: + evpn: + route_type: macip + local_preference: 14783 + origin: igp + peer: + ipv6: 87:95:15::53 + source_protocol: bgp + set: + community: + community_attributes: + - none + metric: + rtt_action: set + origin: igp + call: rm1 + - map_name: rm4 + action: permit + sequence_num: 480 + match: + evpn: + route_type: prefix + source_protocol: static + set: + metric: + rtt_action: subtract + - name: test_case_29_overridden_new_route_map_configuration + description: Override by specifying previously unconfigured attributes + state: overridden + input: + - map_name: rm5 + action: permit + sequence_num: 250 + match: + interface: Ethernet24 + set: + as_path_prepend: 150,275 + metric: + value: 7249 + - name: test_case_30_merged_restore_some_deleted_route_maps + description: Restore some deleted route map configuration + state: merged + input: + - map_name: rm1 + action: deny + sequence_num: 3047 + match: + as_path: bgp_as3 + evpn: + route_type: multicast + ext_comm: bgp_ext_comm2 + origin: igp + peer: + interface: Ethernet16 + source_protocol: connected + set: + community: + community_attributes: + - local_as + - no_advertise + metric: + rtt_action: subtract + origin: incomplete + - map_name: rm2 + action: permit + sequence_num: 100 + match: + interface: Ethernet16 + origin: incomplete + peer: + ip: 5.6.7.8 + source_protocol: ospf + set: + as_path_prepend: 200,300,400 + ipv6_next_hop: + global_addr: 37::58 + prefer_global: true + metric: + value: 8000 + - map_name: rm3 + action: deny + sequence_num: 285 + match: + evpn: + route_type: macip + local_preference: 14783 + origin: igp + peer: + ipv6: 87:95:15::53 + source_protocol: bgp + set: + community: + community_attributes: + - none + metric: + rtt_action: set + origin: igp + call: rm1 + - map_name: rm4 + action: permit + sequence_num: 480 + match: + evpn: + route_type: prefix + source_protocol: static + set: + metric: + rtt_action: subtract + - name: test_case_31_deleted_all + description: Delete all route map configuration + state: deleted + input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/meta/main.yaml new file mode 100644 index 000000000..0b356217e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/meta/main.yaml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common } diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..dce856395 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/tasks/cleanup_tests.yaml @@ -0,0 +1,42 @@ +- name: "Delete all route maps" + vars: + ansible_connection: httpapi + sonic_route_maps: + config: [] + state: deleted + +- name: Remove test vrfs + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ cleanup_tests.delete_vrfs }}" + +- name: Remove test prefix lists + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ cleanup_tests.delete_prefix_lists }}" + +- name: Remove test BGP communities + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ cleanup_tests.delete_bgp_communities }}" + +- name: Remove test BGP extended communities + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ cleanup_tests.delete_bgp_extcommunities }}" + +- name: Remove test BGP AS paths + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ cleanup_tests.delete_bgp_as_paths }}" + +- name: Remove test interfaces + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ cleanup_tests.delete_interfaces }}" diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/tasks/main.yml new file mode 100644 index 000000000..b3e9671f8 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/tasks/main.yml @@ -0,0 +1,15 @@ +- debug: msg="sonic_route_maps Test started ..." + +- name: Preparation test + include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started ..." + include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: "Cleanup {{ module_name }} started ..." + include_tasks: cleanup_tests.yaml + +- name: Display Test Report Output + debug: + var: hostvars[inventory_hostname].ansible_facts.test_reports diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/tasks/preparation_tests.yaml new file mode 100644 index 000000000..cf27f70f1 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/tasks/preparation_tests.yaml @@ -0,0 +1,42 @@ +- name: "Delete old route maps" + vars: + ansible_connection: httpapi + sonic_route_maps: + config: [] + state: deleted + +- name: Initialize vrfs + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparation_tests.init_vrfs }}" + +- name: Initialize prefix lists + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparation_tests.init_prefix_lists }}" + +- name: Initialize BGP communities + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparation_tests.init_bgp_communities }}" + +- name: Initialize BGP extended communities + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparation_tests.init_bgp_extcommunities }}" + +- name: Initialize BGP AS paths + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparation_tests.init_bgp_as_paths }}" + +- name: Initialize interfaces + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparation_tests.init_interfaces }}" diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/tasks/tasks_template.yaml new file mode 100644 index 000000000..f36c29694 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_route_maps/tasks/tasks_template.yaml @@ -0,0 +1,21 @@ +- name: "{{ item.name}} , {{ item.description}}" + sonic_route_maps: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name}} , {{ item.description}} Idempotent" + sonic_route_maps: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/defaults/main.yml index 64d2485fe..67235ac5d 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/defaults/main.yml @@ -4,11 +4,24 @@ module_name: static_routes vrf_1: VrfReg1 vrf_2: VrfReg2 +vrf_3: VrfReg3 +vrf_4: VrfReg4 +vrf_5: VrfReg5 preparations_tests: init_vrf: - 'ip vrf {{vrf_1}}' - 'ip vrf {{vrf_2}}' + - 'ip vrf {{vrf_3}}' + - 'ip vrf {{vrf_4}}' + - 'ip vrf {{vrf_5}}' + delete_vrf: + - 'no ip vrf {{vrf_1}}' + - 'no ip vrf {{vrf_2}}' + - 'no ip vrf {{vrf_3}}' + - 'no ip vrf {{vrf_4}}' + - 'no ip vrf {{vrf_5}}' + tests: - name: test_case_01 description: Add new static routes configuration @@ -106,10 +119,10 @@ tests: tag: 10 track: 11 - name: test_case_03 - description: Delete static routes metric, tag, and track configuration - state: deleted + description: Override static routes configuration + state: overridden input: - - vrf_name: 'default' + - vrf_name: '{{vrf_3}}' static_list: - prefix: '2.0.0.0/8' next_hops: @@ -123,50 +136,139 @@ tests: metric: 8 tag: 10 track: 12 - - vrf_name: '{{vrf_1}}' + - vrf_name: '{{vrf_4}}' static_list: - - prefix: '3.0.0.0/8' + - prefix: '7.0.0.0/8' next_hops: - index: blackhole: True interface: '{{ interface1 }}' nexthop_vrf: '{{vrf_2}}' - next_hop: '5.0.0.0' + next_hop: '9.0.0.0' metric: 11 tag: 22 track: 33 + - vrf_name: '{{vrf_5}}' + static_list: + - prefix: '9.0.0.0/8' + next_hops: - index: - interface: '{{ interface1 }}' - nexthop_vrf: '{{vrf_2}}' + interface: '{{ interface2 }}' + nexthop_vrf: '{{vrf_1}}' + metric: 7 + tag: 9 + track: 11 + - prefix: '6.0.0.0/8' + next_hops: + - index: + nexthop_vrf: '{{vrf_1}}' next_hop: '4.0.0.0' + metric: 4 + tag: 16 + track: 28 - name: test_case_04 + description: Replace static routes configuration + state: replaced + input: + - vrf_name: '{{vrf_3}}' + static_list: + - prefix: '5.0.0.0/8' + next_hops: + - index: + interface: '{{ interface1 }}' + metric: 12 + tag: 18 + track: 32 + - index: + next_hop: '6.0.0.0' + metric: 4 + tag: 5 + track: 6 + - vrf_name: '{{vrf_4}}' + static_list: + - prefix: '7.0.0.0/8' + next_hops: + - index: + blackhole: True + interface: '{{ interface4 }}' + nexthop_vrf: '{{vrf_1}}' + next_hop: '12.0.0.0' + metric: 15 + tag: 25 + track: 35 + - vrf_name: '{{vrf_5}}' + static_list: + - prefix: '9.0.0.0/8' + next_hops: + - index: + interface: '{{ interface2 }}' + nexthop_vrf: '{{vrf_1}}' + metric: 8 + tag: 10 + track: 12 + - name: test_case_05 + description: Delete static routes metric, tag, and track configuration + state: deleted + input: + - vrf_name: '{{vrf_3}}' + static_list: + - prefix: '5.0.0.0/8' + next_hops: + - index: + interface: '{{ interface1 }}' + metric: 12 + tag: 18 + track: 32 + - index: + next_hop: '6.0.0.0' + metric: 4 + tag: 5 + track: 6 + - vrf_name: '{{vrf_4}}' + static_list: + - prefix: '7.0.0.0/8' + next_hops: + - index: + blackhole: True + interface: '{{ interface4 }}' + nexthop_vrf: '{{vrf_1}}' + next_hop: '12.0.0.0' + metric: 15 + tag: 25 + track: 35 + - index: + blackhole: True + interface: '{{ interface1 }}' + nexthop_vrf: '{{vrf_2}}' + next_hop: '9.0.0.0' + - name: test_case_06 description: Delete static route index configuration state: deleted input: - - vrf_name: 'default' + - vrf_name: '{{vrf_3}}' static_list: - - prefix: '2.0.0.0/8' + - prefix: '5.0.0.0/8' next_hops: - index: interface: '{{ interface1 }}' - index: - next_hop: '3.0.0.0' - - vrf_name: '{{vrf_2}}' + next_hop: '6.0.0.0' + - vrf_name: '{{vrf_5}}' static_list: - - prefix: '1.0.0.0/8' + - prefix: '6.0.0.0/8' next_hops: - index: - interface: '{{ interface3 }}' - next_hop: '2.0.0.0' - - name: test_case_05 + nexthop_vrf: '{{vrf_1}}' + next_hop: '4.0.0.0' + - name: test_case_07 description: Delete static route prefix configuration state: deleted input: - - vrf_name: '{{vrf_1}}' - - vrf_name: '{{vrf_2}}' + - vrf_name: '{{vrf_3}}' + - vrf_name: '{{vrf_4}}' static_list: - prefix: '7.0.0.0/8' - - name: test_case_06 + - name: test_case_08 description: Delete all static routes configuration state: deleted input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..8601ad94e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/cleanup_tests.yaml @@ -0,0 +1,7 @@ +- name: Delete VRFs + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparations_tests.delete_vrf }}" + register: output + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/main.yml index c87965de1..aa518f627 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/main.yml @@ -9,3 +9,10 @@ - name: "Test {{ module_name }} started ..." include_tasks: tasks_template.yaml loop: "{{ tests }}" + +- name: "Cleanup test {{ module_name }} started" + include_tasks: cleanup_tests.yaml + +- name: Display all variables/facts known for a host + debug: + var: hostvars[inventory_hostname].ansible_facts.test_reports diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/defaults/main.yml new file mode 100644 index 000000000..2b503453c --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/defaults/main.yml @@ -0,0 +1,534 @@ +--- +ansible_connection: httpapi +module_name: stp + +tests: + - name: test_case_01 + description: Add STP configuration for mst protocol + state: merged + input: + global: + enabled_protocol: mst + loop_guard: true + bpdu_filter: true + disabled_vlans: + - 4-6 + hello_time: 5 + max_age: 10 + fwd_delay: 15 + bridge_priority: 4096 + interfaces: + - intf_name: '{{ interface1 }}' + edge_port: true + link_type: shared + guard: loop + bpdu_guard: true + bpdu_filter: true + uplink_fast: true + shutdown: true + cost: 20 + port_priority: 30 + stp_enable: true + - intf_name: '{{ interface2 }}' + edge_port: true + link_type: point-to-point + guard: root + bpdu_guard: true + bpdu_filter: true + uplink_fast: true + shutdown: true + cost: 40 + port_priority: 50 + stp_enable: true + - intf_name: '{{ interface3 }}' + edge_port: true + link_type: shared + guard: none + bpdu_guard: true + bpdu_filter: true + uplink_fast: true + shutdown: true + cost: 60 + port_priority: 70 + stp_enable: true + mstp: + mst_name: mst1 + revision: 1 + max_hop: 3 + hello_time: 6 + max_age: 9 + fwd_delay: 12 + mst_instances: + - mst_id: 1 + bridge_priority: 2048 + vlans: + - 1 + interfaces: + - intf_name: '{{ interface1 }}' + cost: 60 + port_priority: 65 + - mst_id: 2 + bridge_priority: 1024 + vlans: + - 2 + interfaces: + - intf_name: '{{ interface2 }}' + cost: 50 + port_priority: 55 + - name: test_case_02 + description: Replace at mst instance and interface level + state: replaced + input: + mstp: + mst_instances: + - mst_id: 1 + bridge_priority: 1024 + vlans: + - 2-3 + - mst_id: 2 + interfaces: + - intf_name: '{{ interface2 }}' + cost: 52 + port_priority: 57 + - intf_name: '{{ interface3 }}' + cost: 40 + port_priority: 45 + - mst_id: 3 + bridge_priority: 3072 + - name: test_case_03 + description: Replace at mstp level + state: replaced + input: + mstp: + mst_name: mst2 + revision: 2 + max_hop: 2 + hello_time: 8 + max_age: 10 + fwd_delay: 14 + - name: test_case_04 + description: Modify mst configuration + state: merged + input: + mstp: + mst_name: mst1 + revision: 3 + max_hop: 3 + hello_time: 9 + max_age: 11 + fwd_delay: 15 + mst_instances: + - mst_id: 1 + vlans: + - 1 + interfaces: + - intf_name: '{{ interface1 }}' + cost: 70 + port_priority: 75 + - mst_id: 2 + bridge_priority: 2048 + vlans: + - 1-3 + interfaces: + - intf_name: '{{ interface2 }}' + cost: 52 + port_priority: 57 + - intf_name: '{{ interface3 }}' + cost: 45 + port_priority: 47 + - name: test_case_05 + description: Delete mstp attributes + state: deleted + input: + mstp: + mst_name: mst1 + revision: 3 + max_hop: 3 + hello_time: 9 + max_age: 11 + fwd_delay: 15 + mst_instances: + - mst_id: 1 + - mst_id: 2 + bridge_priority: 2048 + vlans: + - 2 + interfaces: + - intf_name: '{{ interface2 }}' + cost: 52 + port_priority: 57 + - intf_name: '{{ interface3 }}' + - name: test_case_06 + description: Delete global mstp configuration + state: deleted + input: + global: + enabled_protocol: mst + - name: test_case_07 + description: Add STP configuration for pvst protocol + state: merged + input: + global: + enabled_protocol: pvst + bpdu_filter: true + root_guard_timeout: 25 + portfast: true + hello_time: 5 + max_age: 10 + fwd_delay: 20 + bridge_priority: 4096 + pvst: + - vlan_id: 1 + hello_time: 4 + max_age: 6 + fwd_delay: 8 + bridge_priority: 4096 + interfaces: + - intf_name: '{{ interface1 }}' + cost: 10 + port_priority: 50 + - vlan_id: 2 + hello_time: 5 + max_age: 7 + fwd_delay: 9 + bridge_priority: 4096 + interfaces: + - intf_name: '{{ interface2 }}' + cost: 20 + port_priority: 60 + - intf_name: '{{ interface3 }}' + cost: 30 + port_priority: 70 + - name: test_case_08 + description: Replace pvst configuration + state: replaced + input: + pvst: + - vlan_id: 1 + hello_time: 7 + max_age: 8 + fwd_delay: 9 + bridge_priority: 8192 + - vlan_id: 2 + interfaces: + - intf_name: '{{ interface2 }}' + cost: 2 + port_priority: 60 + - intf_name: '{{ interface3 }}' + cost: 31 + port_priority: 71 + - name: test_case_09 + description: Modify pvst configuration + state: merged + input: + pvst: + - vlan_id: 1 + interfaces: + - intf_name: '{{ interface1 }}' + cost: 11 + port_priority: 51 + - vlan_id: 2 + hello_time: 3 + max_age: 9 + fwd_delay: 11 + bridge_priority: 4096 + - name: test_case_10 + description: Delete pvst attributes + state: deleted + input: + pvst: + - vlan_id: 1 + hello_time: 7 + max_age: 8 + fwd_delay: 9 + bridge_priority: 8192 + interfaces: + - intf_name: '{{ interface1 }}' + cost: 11 + - vlan_id: 2 + interfaces: + - intf_name: '{{ interface2 }}' + - intf_name: '{{ interface3 }}' + cost: 31 + port_priority: 71 + - name: test_case_11 + description: Delete pvst and global pvst configuration + state: deleted + input: + global: + enabled_protocol: pvst + - name: test_case_12 + description: Add STP configuration for rapid-pvst protocol + state: merged + input: + global: + enabled_protocol: rapid_pvst + bpdu_filter: true + root_guard_timeout: 25 + hello_time: 5 + max_age: 10 + fwd_delay: 20 + bridge_priority: 4096 + rapid_pvst: + - vlan_id: 1 + hello_time: 4 + max_age: 6 + fwd_delay: 8 + bridge_priority: 4096 + interfaces: + - intf_name: '{{ interface1 }}' + cost: 10 + port_priority: 50 + - vlan_id: 2 + hello_time: 5 + max_age: 7 + fwd_delay: 9 + bridge_priority: 4096 + interfaces: + - intf_name: '{{ interface2 }}' + cost: 20 + port_priority: 60 + - intf_name: '{{ interface3 }}' + cost: 30 + port_priority: 70 + - name: test_case_13 + description: Replace rapid-pvst configuration + state: replaced + input: + rapid_pvst: + - vlan_id: 1 + hello_time: 7 + max_age: 8 + fwd_delay: 9 + bridge_priority: 8192 + - vlan_id: 2 + interfaces: + - intf_name: '{{ interface2 }}' + cost: 2 + port_priority: 60 + - intf_name: '{{ interface3 }}' + cost: 31 + port_priority: 71 + - name: test_case_14 + description: Modify rapid-pvst configuration + state: merged + input: + rapid_pvst: + - vlan_id: 1 + interfaces: + - intf_name: '{{ interface1 }}' + cost: 11 + port_priority: 51 + - vlan_id: 2 + hello_time: 3 + max_age: 9 + fwd_delay: 11 + bridge_priority: 4096 + - name: test_case_15 + description: Delete rapid-pvst attributes + state: deleted + input: + rapid_pvst: + - vlan_id: 1 + hello_time: 7 + max_age: 8 + fwd_delay: 9 + bridge_priority: 8192 + interfaces: + - intf_name: '{{ interface1 }}' + cost: 11 + port_priority: 51 + - vlan_id: 2 + interfaces: + - intf_name: '{{ interface2 }}' + - intf_name: '{{ interface3 }}' + cost: 31 + port_priority: 71 + - name: test_case_16 + description: Delete rapid-pvst and global pvst configuration + state: deleted + input: + global: + enabled_protocol: rapid_pvst + - name: test_case_17 + description: Configure STP global and interfaces + state: merged + input: + global: + enabled_protocol: mst + disabled_vlans: + - 4 + bpdu_filter: true + hello_time: 5 + max_age: 10 + fwd_delay: 20 + bridge_priority: 4096 + interfaces: + - intf_name: '{{ interface1 }}' + edge_port: true + link_type: shared + guard: loop + bpdu_guard: true + bpdu_filter: true + uplink_fast: true + shutdown: true + cost: 20 + port_priority: 30 + stp_enable: true + - intf_name: '{{ interface2 }}' + edge_port: true + link_type: point-to-point + guard: root + bpdu_guard: true + bpdu_filter: true + uplink_fast: true + shutdown: true + cost: 40 + port_priority: 50 + stp_enable: true + - intf_name: '{{ interface3 }}' + edge_port: true + link_type: shared + guard: none + bpdu_guard: true + bpdu_filter: true + uplink_fast: true + shutdown: true + cost: 60 + port_priority: 70 + stp_enable: true + - name: test_case_18 + description: Modify STP interface + state: merged + input: + global: + disabled_vlans: + - 4-6 + bpdu_filter: false + hello_time: 7 + max_age: 20 + fwd_delay: 30 + bridge_priority: 8192 + interfaces: + - intf_name: '{{ interface1 }}' + edge_port: false + link_type: point-to-point + guard: root + bpdu_guard: false + bpdu_filter: false + uplink_fast: false + shutdown: false + cost: 28 + port_priority: 32 + stp_enable: false + - name: test_case_19 + description: Replace STP interface + state: replaced + input: + interfaces: + - intf_name: '{{ interface3 }}' + cost: 80 + port_priority: 90 + - name: test_case_20 + description: Delete STP interfaces + state: deleted + input: + interfaces: + - intf_name: '{{ interface2 }}' + edge_port: true + link_type: point-to-point + guard: root + bpdu_guard: true + bpdu_filter: true + uplink_fast: true + shutdown: true + cost: 40 + port_priority: 50 + stp_enable: true + - intf_name: '{{ interface3 }}' + - name: test_case_21 + description: Replace at global level + state: replaced + input: + global: + enabled_protocol: mst + interfaces: + - intf_name: '{{ interface1 }}' + - intf_name: '{{ interface2 }}' + - intf_name: '{{ interface3 }}' + - name: test_case_22 + description: Override STP configuration + state: overridden + input: + global: + enabled_protocol: mst + loop_guard: true + bpdu_filter: true + disabled_vlans: + - 4-6 + hello_time: 5 + max_age: 10 + fwd_delay: 15 + bridge_priority: 4096 + interfaces: + - intf_name: '{{ interface1 }}' + edge_port: true + link_type: shared + guard: loop + bpdu_guard: true + bpdu_filter: true + uplink_fast: true + shutdown: true + cost: 20 + port_priority: 30 + stp_enable: true + - intf_name: '{{ interface2 }}' + edge_port: true + link_type: point-to-point + guard: root + bpdu_guard: true + bpdu_filter: true + uplink_fast: true + shutdown: true + cost: 40 + port_priority: 50 + stp_enable: true + - intf_name: '{{ interface3 }}' + edge_port: true + link_type: shared + guard: none + bpdu_guard: true + bpdu_filter: true + uplink_fast: true + shutdown: true + cost: 60 + port_priority: 70 + stp_enable: true + mstp: + mst_name: mst1 + revision: 1 + max_hop: 3 + hello_time: 6 + max_age: 9 + fwd_delay: 12 + mst_instances: + - mst_id: 1 + bridge_priority: 2048 + vlans: + - 1 + interfaces: + - intf_name: '{{ interface1 }}' + cost: 60 + port_priority: 65 + - mst_id: 2 + bridge_priority: 1024 + vlans: + - 2 + interfaces: + - intf_name: '{{ interface2 }}' + cost: 50 + port_priority: 55 + - name: test_case_23 + description: Delete all STP configuration + state: deleted + input: {} diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/meta/main.yaml new file mode 100644 index 000000000..0b356217e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/meta/main.yaml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common } diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..5ccc027bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/cleanup_tests.yaml @@ -0,0 +1,11 @@ +- name: Delete L2 interfaces configuration + sonic_l2_interfaces: + config: [] + state: deleted + ignore_errors: yes + +- name: Delete VLANs configuration + sonic_vlans: + config: [] + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/main.yml new file mode 100644 index 000000000..470df5a0b --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/main.yml @@ -0,0 +1,14 @@ +- debug: msg="sonic_stp Test started ..." + +- set_fact: + base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}" + +- name: Preparations test + include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started ..." + include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: Cleanup tests + include_tasks: cleanup_tests.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/preparation_tests.yaml new file mode 100644 index 000000000..1c402e232 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/preparation_tests.yaml @@ -0,0 +1,47 @@ +- name: Delete STP configuration + sonic_stp: + config: {} + state: deleted + ignore_errors: yes + +- name: Delete L2 interfaces configuration + sonic_l2_interfaces: + config: [] + state: deleted + ignore_errors: yes + +- name: Delete VLANs configuration + sonic_vlans: + config: [] + state: deleted + ignore_errors: yes + +- name: Add VLANs + sonic_vlans: + config: + - vlan_id: 1 + - vlan_id: 2 + - vlan_id: 3 + - vlan_id: 4 + - vlan_id: 5 + - vlan_id: 6 + state: merged + ignore_errors: yes + +- name: Add L2 interfaces configuration + sonic_l2_interfaces: + config: + - name: '{{ interface1 }}' + trunk: + allowed_vlans: + - vlan: 1-3 + - name: '{{ interface2 }}' + trunk: + allowed_vlans: + - vlan: 1-3 + - name: '{{ interface3 }}' + trunk: + allowed_vlans: + - vlan: 1-3 + state: merged + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/tasks_template.yaml new file mode 100644 index 000000000..11f787418 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/tasks_template.yaml @@ -0,0 +1,21 @@ +- name: "{{ item.name}} , {{ item.description}}" + sonic_stp: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name}} , {{ item.description}} Idempotent" + sonic_stp: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/tasks_template_del.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/tasks_template_del.yaml index bbf2331df..bbf2331df 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/tasks_template_del.yaml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_stp/tasks/tasks_template_del.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/defaults/main.yml index c5146db97..4f0bda521 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/defaults/main.yml @@ -25,7 +25,7 @@ tests: anycast_address: mac_address: 00:09:5B:EC:EE:F2 - - name: del_test_case_01 + - name: test_case_04 description: Delete System properties state: deleted input: @@ -34,13 +34,41 @@ tests: anycast_address: ipv4: false - - name: del_test_case_02 + - name: test_case_05 description: Delete System associated anycast mac address state: deleted input: anycast_address: mac_address: 00:09:5B:EC:EE:F2 + - name: test_case_06 + description: Override System configuration + state: overridden + input: + hostname: SONIC-ov + interface_naming: standard + anycast_address: + ipv4: true + mac_address: 00:09:5B:EC:EE:F2 + + - name: test_case_07 + description: Replace some System configuration + state: replaced + input: + anycast_address: + ipv4: true + ipv6: false + mac_address: 00:09:5B:EC:EE:F2 + + - name: test_case_08 + description: Replace System configuration + state: replaced + input: + hostname: SONIC + interface_naming: native + anycast_address: + ipv4: true + test_delete_all: - name: del_all_test_case_01 description: Delete System properties diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/defaults/main.yml index 0f9b3e3c4..e964fcf60 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/defaults/main.yml @@ -64,7 +64,7 @@ tests: servers: host: - name: test_case_05 - description: merge parameter of tacacs servers + description: Merge parameter of tacacs servers state: merged input: servers: @@ -85,7 +85,67 @@ tests: timeout: 14 priority: 4 -test_delete_all: - name: test_case_06 - description: delete all the configurations of tacacs server + description: Replace some parameter of tacacs servers + state: replaced + input: + auth_type: mschap + source_interface: "{{ interface3 }}" + timeout: 36 + servers: + host: + - name: my_host + auth_type: chap + port: 55 + timeout: 12 + priority: 3 + + - name: test_case_07 + description: Replace hosts of tacacs servers + state: replaced + input: + auth_type: mschap + source_interface: "{{ interface3 }}" + timeout: 36 + servers: + host: + - name: my_host + auth_type: chap + port: 55 + timeout: 12 + priority: 3 + - name: 20.21.22.23 + auth_type: login + port: 50 + timeout: 38 + priority: 4 + - name: 18.21.22.23 + auth_type: chap + port: 20 + timeout: 19 + priority: 8 + + - name: test_case_08 + description: Override parameter of tacacs servers + state: overridden + input: + auth_type: chap + source_interface: "{{ interface2 }}" + timeout: 20 + servers: + host: + - name: 10.11.11.11 + auth_type: pap + port: 55 + timeout: 12 + priority: 3 + - name: your_host + auth_type: login + port: 50 + timeout: 30 + priority: 6 + +test_delete_all: + - name: test_case_09 + description: Delete all the configurations of tacacs server state: deleted diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/defaults/main.yml index 342cb2d40..069b1d729 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/defaults/main.yml @@ -2,23 +2,6 @@ ansible_connection: httpapi module_name: users -tests_cli: - - name: cli_test_case_01 - description: Configure users - state: merged - expected_cli: - - username sysadmin password - - username operator1 password - input: - - name: sysadmin - role: admin - password: admin - update_password: always - - name: operator1 - role: operator - password: admin - update_password: always - tests_single_run: - name: test_case_sr_01 description: Configure users @@ -53,9 +36,8 @@ tests_single_run: - name: user2 role: operator password: admin - update_password: on_create - - + update_password: on_create + tests: - name: test_case_01 description: Configure users @@ -74,7 +56,7 @@ tests: password: admin update_password: on_create - name: test_case_02 - description: Configure users role + description: Update user roles state: merged input: - name: user1 @@ -86,11 +68,43 @@ tests: password: admin update_password: on_create - name: test_case_03 + description: Replace user roles + state: replaced + input: + - name: user2 + role: secadmin + password: admin + update_password: on_create + - name: user3 + role: netadmin + password: admin + update_password: on_create + - name: test_case_04 + description: Override users configuration with existing user + state: overridden + input: + - name: user2 + role: secadmin + password: admin + update_password: on_create + - name: test_case_05 + description: Override users configuration + state: overridden + input: + - name: user4 + role: admin + password: admin + update_password: on_create + - name: user5 + role: operator + password: admin + update_password: on_create + - name: test_case_06 description: Delete user state: deleted input: - - name: user1 - - name: test_case_04 - description: Update users role + - name: user4 + - name: test_case_07 + description: Delete all users configurations state: deleted input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/cli_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/cli_tests.yaml deleted file mode 100644 index 1e9bfc240..000000000 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/cli_tests.yaml +++ /dev/null @@ -1,14 +0,0 @@ -- name: Test sonic multiple command with wait_for - vars: - ansible_connection: network_cli - sonic_command: - commands: - - show running-configuration - register: cli_contains_output - -- set_fact: - cli_contains_condition: "{{ 'username operator1 password' in cli_contains_output.stdout.0 }}" - -- import_role: - name: common - tasks_from: cli.contains.test.facts.report.yaml
\ No newline at end of file diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/main.yml index 987174b91..5b1b54c77 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/main.yml @@ -3,9 +3,6 @@ - set_fact: base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}" -# - name: CLI test test -# include_tasks: cli_tests.yaml - - name: Preparations test include_tasks: preparation_tests.yaml @@ -19,21 +16,3 @@ - name: "Test {{ module_name }} started ..." include_tasks: tasks_template.yaml loop: "{{ tests }}" - -# - name: "Test CLI validation started ..." -# include_role: -# name: common -# tasks_from: cli_tasks_template.yaml -# loop: "{{ tests_cli }}" - -# - name: Preparations test -# include_tasks: preparation_tests.yaml - - - -# - name: Preparations test -# include_tasks: preparation_tests.yaml - -# - name: Display all variables/facts known for a host -# debug: -# var: hostvars[inventory_hostname].ansible_facts.test_reports diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/defaults/main.yml new file mode 100644 index 000000000..1ba48941e --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/defaults/main.yml @@ -0,0 +1,195 @@ +--- + +po1: PortChannel2 +po2: PortChannel4 + +ansible_connection: httpapi +module_name: vlan_mapping + +preparations_tests: + delete_port_configurations: + - name: "{{ interface2 }}" + - name: "{{ interface4 }}" + - name: "{{ interface6 }}" + init_portchannel: + - "interface Portchannel 2" + - "interface Portchannel 4" + +tests: + - name: test_case_01 + description: Add vlan mapping configurations + state: merged + input: + - name: '{{ interface2 }}' + mapping: + - service_vlan: 2755 + vlan_ids: + - 392 + dot1q_tunnel: false + inner_vlan: 590 + - service_vlan: 2855 + vlan_ids: + - 393 + - 395 + dot1q_tunnel: true + priority: 6 + - name: test_case_02 + description: Add vlan mapping configurations + state: merged + input: + - name: '{{ interface4 }}' + mapping: + - service_vlan: 2567 + vlan_ids: + - 300 + dot1q_tunnel: true + priority: 3 + - service_vlan: 2436 + vlan_ids: + - 400-402 + - 412 + - 420 + - 422 + - 430-431 + dot1q_tunnel: true + - name: '{{ po1 }}' + mapping: + - service_vlan: 3000 + dot1q_tunnel: true + vlan_ids: + - 506-512 + - 561 + priority: 5 + - name: test_case_03 + description: Update existing vlan mapping configurations + state: merged + input: + - name: '{{ interface2 }}' + mapping: + - service_vlan: 2755 + priority: 3 + - service_vlan: 2855 + vlan_ids: + - 397 + - 399 + dot1q_tunnel: true + - name: '{{ po1 }}' + mapping: + - service_vlan: 3000 + dot1q_tunnel: true + vlan_ids: + - 506-514 + - 501 + - 561 + priority: 1 + - name: test_case_04 + description: Update existing and add new vlan mapping configurations + state: merged + input: + - name: '{{ interface2 }}' + mapping: + - service_vlan: 2758 + vlan_ids: + - 2857 + - service_vlan: 2855 + priority: 2 + dot1q_tunnel: true + - name: '{{ po2 }}' + mapping: + - service_vlan: 3200 + dot1q_tunnel: true + vlan_ids: + - 576-584 + - 591 + - name: test_case_05 + description: Replace vlan mapping configurations + state: replaced + input: + - name: '{{ interface2 }}' + mapping: + - service_vlan: 2768 + vlan_ids: + - 2923 + - name: '{{ interface4 }}' + mapping: + - service_vlan: 2567 + vlan_ids: + - 310 + - service_vlan: 2436 + vlan_ids: + - 400-402 + - 422 + - 430-431 + dot1q_tunnel: true + priority: 1 + - name: '{{ po1 }}' + mapping: + - service_vlan: 3000 + dot1q_tunnel: true + vlan_ids: + - 506-512 + - 561 + priority: 7 + - name: test_case_06 + description: Delete vlan mapping configurations + state: deleted + input: + - name: '{{ interface2 }}' + - name: '{{ interface4 }}' + mapping: + - service_vlan: 2567 + - service_vlan: 2436 + vlan_ids: + - 422 + - 400-405 + priority: 1 + - name: '{{ po1 }}' + mapping: + - service_vlan: 3000 + priority: 7 + - name: '{{ po2 }}' + mapping: + - service_vlan: 3200 + dot1q_tunnel: true + vlan_ids: + - 578-582 + - name: test_case_07 + description: Add vlan mapping configurations + state: merged + input: + - name: '{{ interface2 }}' + mapping: + - service_vlan: 2755 + vlan_ids: + - 392 + dot1q_tunnel: false + inner_vlan: 590 + - service_vlan: 2855 + vlan_ids: + - 393 + - 395 + dot1q_tunnel: true + priority: 6 + - name: test_case_08 + description: Override vlan mapping configurations + state: overridden + input: + - name: '{{ interface2 }}' + mapping: + - service_vlan: 2754 + vlan_ids: + - 392 + dot1q_tunnel: false + inner_vlan: 590 + - name: '{{ interface6 }}' + mapping: + - service_vlan: 2700 + vlan_ids: + - 132-145 + - 120 + dot1q_tunnel: true + priority: 3 + - name: test_case_09 + description: Delete all vlan mapping configurations + state: deleted + input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/meta/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/meta/main.yml new file mode 100644 index 000000000..d0ceaf6f5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/meta/main.yml @@ -0,0 +1,5 @@ +--- +collections: + - dellemc.enterprise_sonic +dependencies: + - { role: common } diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/tasks/cleanup_tests.yaml new file mode 100644 index 000000000..4d7828e08 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/tasks/cleanup_tests.yaml @@ -0,0 +1,6 @@ +--- +- name: Delete vlan mapping configurations + dellemc.enterprise_sonic.sonic_vlan_mapping: + config: [] + state: deleted + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/tasks/main.yml new file mode 100644 index 000000000..45178b754 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/tasks/main.yml @@ -0,0 +1,17 @@ +--- +- ansible.builtin.debug: + msg: "sonic_vlan_mapping Test started ..." + +- name: "Preparations for {{ module_name }}" + ansible.builtin.include_tasks: preparation_tests.yaml + +- name: "Test {{ module_name }} started" + ansible.builtin.include_tasks: tasks_template.yaml + loop: "{{ tests }}" + +- name: "Cleanup of {{ module_name }}" + ansible.builtin.include_tasks: cleanup_tests.yaml + +- name: Display all variables/facts known for a host + ansible.builtin.debug: + var: hostvars[inventory_hostname].ansible_facts.test_reports diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/tasks/preparation_tests.yaml new file mode 100644 index 000000000..c73996e30 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/tasks/preparation_tests.yaml @@ -0,0 +1,21 @@ +--- +- name: Delete old vlan mapping configurations + dellemc.enterprise_sonic.sonic_vlan_mapping: + config: [] + state: deleted + ignore_errors: yes + +- name: "initialize default interfaces" + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ default_interface_cli }}" + register: output + ignore_errors: yes + +- name: "initialize init_portchannel" + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparations_tests.init_portchannel }}" + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/tasks/tasks_template.yaml new file mode 100644 index 000000000..1e64a42bf --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlan_mapping/tasks/tasks_template.yaml @@ -0,0 +1,22 @@ +--- +- name: "{{ item.name }} , {{ item.description }}" + dellemc.enterprise_sonic.sonic_vlan_mapping: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: action_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: action.facts.report.yaml + +- name: "{{ item.name }} , {{ item.description }} Idempotent" + dellemc.enterprise_sonic.sonic_vlan_mapping: + config: "{{ item.input }}" + state: "{{ item.state }}" + register: idempotent_task_output + ignore_errors: yes + +- ansible.builtin.import_role: + name: common + tasks_from: idempotent.facts.report.yaml diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/defaults/main.yml index b7deed026..62f6dcc74 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/defaults/main.yml @@ -8,7 +8,9 @@ preparations_tests: - vlan_id: 501 - vlan_id: 502 - vlan_id: 503 - + - vlan_id: 504 + - vlan_id: 505 + - vlan_id: 506 tests: # merge test cases started @@ -36,7 +38,54 @@ tests: - vlan_id: 500 description: "modified vlan500 description" - vlan_id: 501 + # replace test cases started - name: test_case_04 + description: Replace VLANs + state: replaced + input: + - vlan_id: 500 + - vlan_id: 502 + description: "modified vlan502 description" + # overridden test cases started + - name: test_case_05 + description: Override VLANs configuration + state: overridden + input: + - vlan_id: 501 + - vlan_id: 502 + description: "overridden vlan502 description" + - vlan_id: 504 + - name: test_case_06 + description: Override VLANs + state: overridden + input: + - vlan_id: 505 + - vlan_id: 506 + description: "overridden vlan506 description" + - name: test_case_07 + description: Create VLANs for the folowing replaced and overriden cases + state: merged + input: + - vlan_id: 510 + description: "new vlan510 description" + - vlan_id: 511 + description: "new vlan511 description" + - vlan_id: 512 + description: "new vlan512 description" + # more replace test cases started + - name: test_case_08 + description: Replace VLANs + state: replaced + input: + - vlan_id: 510 + # more overridden test cases started + - name: test_case_09 + description: Override VLANs configuration + state: overridden + input: + - vlan_id: 511 + # Clean up + - name: test_case_10 description: Delete specific trunk VLANs state: deleted input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/defaults/main.yml index afbfd754c..e4f79f4a8 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/defaults/main.yml @@ -4,6 +4,7 @@ module_name: vrf vrf_1: VrfReg1 vrf_2: VrfReg2 +vrf_3: VrfReg3 po1: Portchannel 100 vlan1: Vlan 100 @@ -13,6 +14,10 @@ po2: Portchannel 101 vlan2: Vlan 101 looopback2: Loopback 101 +po3: Portchannel 103 +vlan3: Vlan 103 +looopback3: Loopback 103 + preparations_tests: delete_interfaces: - "no interface {{ po1 }}" @@ -21,6 +26,9 @@ preparations_tests: - "no interface {{ po2 }}" - "no interface {{ vlan2 }}" - "no interface {{ looopback2 }}" + - "no interface {{ po3 }}" + - "no interface {{ vlan3 }}" + - "no interface {{ looopback3 }}" init_interfaces: - "interface {{ po1 }}" - "interface {{ vlan1 }}" @@ -28,20 +36,9 @@ preparations_tests: - "interface {{ po2 }}" - "interface {{ vlan2 }}" - "interface {{ looopback2 }}" - -tests_cli: - - name: cli_test_case_01 - description: creates VRF properties - state: merged - input: - - name: "{{ vrf_1 }}" - members: - interfaces: - - name: "{{ interface1 }}" - - name: "{{ po1 }}" - - name: "{{ vlan1 }}" - - name: "{{ looopback1 }}" - - name: "{{ vrf_2 }}" + - "interface {{ po3 }}" + - "interface {{ vlan3 }}" + - "interface {{ looopback3 }}" tests: - name: test_case_01 @@ -87,7 +84,7 @@ tests: - name: "{{ vlan2 }}" - name: "{{ looopback2 }}" - - name: del_test_case_04 + - name: test_case_04 description: Delete VRF properties state: deleted input: @@ -101,7 +98,7 @@ tests: - name: "{{ interface2 }}" - name: "{{ po2 }}" - - name: del_test_case_05 + - name: test_case_05 description: Delete VRF properties state: deleted input: @@ -109,7 +106,7 @@ tests: members: interfaces: - - name: del_test_case_06 + - name: test_case_06 description: Delete VRF properties state: deleted input: @@ -133,7 +130,58 @@ tests: - name: "{{ vlan2 }}" - name: "{{ looopback2 }}" - - name: del_test_case_08 + - name: test_case_08 + description: Overridden VRF properties + state: overridden + input: + - name: "{{ vrf_3 }}" + members: + interfaces: + - name: "{{ interface3 }}" + - name: "{{ vlan3 }}" + - name: "{{ looopback3 }}" + - name: "{{ vrf_1 }}" + members: + interfaces: + - name: "{{ interface1 }}" + - name: "{{ po3 }}" + + - name: test_case_09 + description: Replace VRF properties + state: replaced + input: + - name: "{{ vrf_3 }}" + members: + interfaces: + - name: "{{ interface3 }}" + - name: "{{ looopback3 }}" + - name: "{{ vrf_1 }}" + members: + interfaces: + - name: "{{ interface2 }}" + - name: "{{ po2 }}" + + - name: test_case_10 + description: Replace VRF properties with new VRF + state: replaced + input: + - name: "{{ vrf_3 }}" + members: + interfaces: + - name: "{{ interface3 }}" + - name: "{{ vlan3 }}" + - name: "{{ vrf_2 }}" + members: + interfaces: + - name: "{{ interface1 }}" + - name: "{{ vlan2 }}" + - name: "{{ vrf_1 }}" + members: + interfaces: + - name: "{{ interface2 }}" + - name: "{{ po2 }}" + + - name: test_case_11 description: Delete VRF properties state: deleted input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/main.yml index 8e165bc67..88ceae783 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/main.yml @@ -6,29 +6,14 @@ - name: Preparations test include_tasks: preparation_tests.yaml -- name: "Test {{ module_name }} CLI validation started ..." - include_tasks: tasks_template.yaml - loop: "{{ tests_cli }}" - -- name: "Test CLI validation started ..." - include_role: - name: common - tasks_from: cli_tasks_template.yaml - loop: "{{ tests_cli }}" - -- name: Preparations test - include_tasks: preparation_tests.yaml - -- name: Cleanup tests - include_tasks: cleanup_tests.yaml - - name: "Test {{ module_name }} started ..." include_tasks: tasks_template.yaml loop: "{{ tests }}" -- name: Cleanup tests - include_tasks: cleanup_tests.yaml - -# - name: Display all variables/facts known for a host -# debug: -# var: hostvars[inventory_hostname].ansible_facts.test_reports +- name: "Delete interfaces" + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparations_tests.delete_interfaces }}" + register: output + ignore_errors: yes diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/preparation_tests.yaml index c6a33af48..b05800722 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/preparation_tests.yaml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/preparation_tests.yaml @@ -1,13 +1,3 @@ -- name: Delete existing mclag - sonic_mclag: - config: - state: deleted - ignore_errors: yes -- name: Deletes old vxlans - sonic_vxlans: - config: [] - state: deleted - ignore_errors: yes - name: "initialize default interfaces" vars: ansible_connection: network_cli @@ -15,10 +5,12 @@ commands: "{{ default_interface_cli }}" register: output ignore_errors: yes -- name: Deletes old VRFs - sonic_vrfs: - config: [] - state: deleted +- name: "delete interfaces" + vars: + ansible_connection: network_cli + sonic_config: + commands: "{{ preparations_tests.delete_interfaces }}" + register: output ignore_errors: yes - name: "initialize interfaces" vars: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/templates/cli_test_case_01.cfg b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/templates/cli_test_case_01.cfg index 35cd6ded0..90695d538 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/templates/cli_test_case_01.cfg +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/templates/cli_test_case_01.cfg @@ -4,7 +4,7 @@ interface Vlan100 ip vrf forwarding VrfReg1 interface Loopback 100 ip vrf forwarding VrfReg1 -interface PortChannel 100 +interface PortChannel100 ip vrf forwarding VrfReg1 interface {{ interface1 }} ip vrf forwarding VrfReg1 diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/defaults/main.yml index f2687a09d..6a330afa7 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/defaults/main.yml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/defaults/main.yml @@ -45,7 +45,7 @@ tests_cli: tests: - name: test_case_01 - description: creates Vxlan properties + description: creates VXLAN properties state: merged input: - name: vtep1 @@ -56,12 +56,12 @@ tests: - name: vtep1 source_ip: 1.1.1.1 primary_ip: 2.2.2.2 - evpn_nvo: nvo5 - name: test_case_03 description: Update VRF properties state: merged input: - name: vtep1 + evpn_nvo: nvo5 vlan_map: - vni: 101 vlan: "{{vlan1}}" @@ -87,35 +87,67 @@ tests: vrf_map: - vni: 102 vrf: "{{vrf2}}" - - name: del_test_case_06 - description: Delete VRF properties - state: deleted + - name: test_case_06 + description: Override VXLAN properties + state: overridden input: - - name: vtep1 - source_ip: 1.1.1.1 - primary_ip: 2.2.2.2 + - name: vtep2 + source_ip: 3.3.3.3 + primary_ip: 4.4.4.4 evpn_nvo: nvo5 vlan_map: - vni: 101 vlan: "{{vlan1}}" - - vni: 102 + vrf_map: + - vni: 101 + vrf: "{{vrf1}}" + - name: test_case_07 + description: Replace VXLAN properties + state: replaced + input: + - name: vtep2 + source_ip: 5.5.5.5 + primary_ip: 6.6.6.6 + evpn_nvo: nvo6 + vlan_map: + - vni: 101 vlan: "{{vlan2}}" + - vni: 102 + vlan: "{{vlan1}}" vrf_map: - vni: 101 + vrf: "{{vrf2}}" + - vni: 102 vrf: "{{vrf1}}" + - name: test_case_08 + description: Delete VRF properties + state: deleted + input: + - name: vtep2 + source_ip: 5.5.5.5 + primary_ip: 6.6.6.6 + evpn_nvo: nvo6 + vlan_map: + - vni: 101 + vlan: "{{vlan2}}" - vni: 102 + vlan: "{{vlan1}}" + vrf_map: + - vni: 101 vrf: "{{vrf2}}" - - name: del_test_case_07 + - vni: 102 + vrf: "{{vrf1}}" + - name: test_case_09 description: Delete VRF properties state: deleted input: - - name: vtep1 - - name: test_case_08 - description: Recreate Vxlan + - name: vtep2 + - name: test_case_10 + description: Recreate VXLAN state: merged input: - name: vtep1 - - name: del_test_case_09 + - name: test_case_11 description: Delete VRF properties state: deleted input: [] diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/templates/regression_html_report.j2 b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/templates/regression_html_report.j2 index 0695c86bc..6937eb8a9 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/templates/regression_html_report.j2 +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/templates/regression_html_report.j2 @@ -176,23 +176,21 @@ color: red; </style> <h2 style="color:blue;">Regression report </h2> -<h3 style="color:blue;"> Date: <i style="color:black;">{{ date}} </i> Time: <i style="color:black;">{{ time }}</i></h3> - +<h3 style="color:blue;"> Date: <i style="color:black;">{{ date }} </i> Time: <i style="color:black;">{{ time }}</i></h3> +<h3 style="color:blue;">Summary report </h3> +<table class="table table-striped table-bordered"> + <thead> + <tr> + <th>Module</th> + <th>Total testcases</th> + <th>Passed</th> + <th>Failed</th> + </tr> + </thead> + <tbody> {% set complete_passed = [0] %} {% set complete_failed = [0] %} {% set complete_total = [0] %} -<h3 style="color:blue;">Summary report </h3> -<table class="table table-striped table-bordered"> - <thead> - <tr> - <th>Module</th> - <th>Total testcases</th> - <th>Passed</th> - <th>Failed</th> - </tr> - </thead> -<tbody> - {% for module_name, test_data_list in ansible_facts.test_reports.items() %} {% set passed = [0] %} {% set failed = [0] %} @@ -200,106 +198,85 @@ color: red; {% for testcase_name, test_data in test_data_list.items() %} {% if total.append(total.pop() + 1) %}{% endif %} {% if complete_total.append(complete_total.pop() + 1) %}{% endif %} - {% if 'Passed' in test_data.status %} {% if passed.append(passed.pop() + 1) %}{% endif %} {% if complete_passed.append(complete_passed.pop() + 1) %}{% endif %} {% endif %} - {% if 'Failed' in test_data.status %} {% if failed.append(failed.pop() + 1) %}{% endif %} {% if complete_failed.append(complete_failed.pop() + 1) %}{% endif %} {% endif %} {% endfor %} -<tr> -<td>{{ module_name }}</td> -<td>{{ total[0] }}</td> -<td>{{ passed[0] }}</td> -<td>{{ failed[0] }}</td> -</tr> + <tr> + <td>{{ module_name }}</td> + <td>{{ total[0] }}</td> + <td>{{ passed[0] }}</td> + <td>{{ failed[0] }}</td> + </tr> {% endfor %} -</tbody> + </tbody> </table> <h2> Testcase Complete Summary: Total: {{ complete_total[0] }}, Passed: {{ complete_passed[0] }}, Failed: {{ complete_failed[0] }} </h2> - - </br> </br> </br> - - - - - - - - - - - - - - - - - - - - - {% for module_name, test_data_list in ansible_facts.test_reports.items() %} {% set passed = [0] %} {% set failed = [0] %} {% set total = [0] %} - {% for testcase_name, test_data in test_data_list.items() %} {% if total.append(total.pop() + 1) %}{% endif %} - {% if 'Passed' in test_data.status %} {% if passed.append(passed.pop() + 1) %}{% endif %} {% endif %} - {% if 'Failed' in test_data.status %} {% if failed.append(failed.pop() + 1) %}{% endif %} {% endif %} {% endfor %} -<h3 style="color:blue;">Detailed report of <i style="color:black;"> {{module_name }} </i> </h3> +<h3 style="color:blue;">Detailed report of <i style="color:black;"> {{ module_name }} </i> </h3> <h2> Testcase summary: Total: {{ total[0] }}, Passed: {{ passed[0] }}, Failed: {{ failed[0] }} </h2> <table class="table table-striped table-bordered"> - <thead> - <tr> - <th>Testcase name</th> - <th>Status</th> - <th>User Input</th> - <th>Commands</th> - <th>Before</th> - <th>After</th> - <th>Module exception</th> - </tr> - </thead> -<tbody> + <thead> + <tr> + <th>Testcase name</th> + <th>Status</th> + <th>User Input</th> + <th>Commands</th> + <th>Before</th> + <th>After</th> + <th>Module exception</th> + </tr> + </thead> + <tbody> {% for name, test_data in test_data_list.items() %} -<tr> -<td>{{ name}}</td> -<td>{{ test_data.status | default('Template Error')}}</td> + <tr> + <td>{{ name }}</td> + <td>{{ test_data.status | default('Template Error') }}</td> {% if 'Passed' in test_data.status %} -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> +{% if ansible_verbosity >= 3 %} + <td><pre>Input: {{ test_data.configs | default('Template Error') | to_nice_json(indent=3) }}</pre></td> + <td><pre>Commands: {{ test_data.commands | default('Template Error') | to_nice_json(indent=3) }}</pre></td> + <td><pre>Before: {{ test_data.before | default('Template Error') | to_nice_json(indent=3) }}</pre></td> + <td><pre>After: {{ test_data.after | default('Template Error') | to_nice_json(indent=3) }}</pre></td> + <td><pre>Error: {{ test_data.module_stderr | default('Template Error') | to_nice_json(indent=3) }}</pre></td> +{% else %} + <td></td> + <td></td> + <td></td> + <td></td> + <td></td> +{% endif %} {% else %} -<td>Input: {{ test_data.configs | default('Template Error') | to_nice_json(indent=3) }}</td> -<td>Commands: {{ test_data.commands | default('Template Error') | to_nice_json(indent=3) }}</td> -<td>Before: {{ test_data.before | default('Template Error') | to_nice_json(indent=3) }}</td> -<td>After: {{ test_data.after | default('Template Error') | to_nice_json(indent=3) }}</td> -<td>Error: {{ test_data.module_stderr | default('Template Error') | to_nice_json(indent=3) }}</td> + <td><pre>Input: {{ test_data.configs | default('Template Error') | to_nice_json(indent=3) }}</pre></td> + <td><pre>Commands: {{ test_data.commands | default('Template Error') | to_nice_json(indent=3) }}</pre></td> + <td><pre>Before: {{ test_data.before | default('Template Error') | to_nice_json(indent=3) }}</pre></td> + <td><pre>After: {{ test_data.after | default('Template Error') | to_nice_json(indent=3) }}</pre></td> + <td><pre>Error: {{ test_data.module_stderr | default('Template Error') | to_nice_json(indent=3) }}</pre></td> {% endif %} -</tr> + </tr> {% endfor %} -</tbody> + </tbody> </table> {% endfor %} @@ -308,21 +285,21 @@ color: red; <script> window.onload = function() { -var ele = document.getElementsByTagName('table'); -for(var tb = 0; tb < ele.length;tb++) { - tbody = ele[tb].getElementsByTagName('tbody') - for(var tbd = 0; tbd < tbody.length;tbd++) { - tr = tbody[tbd].getElementsByTagName('tr'); - for (var trs = 0; trs < tr.length;trs++) { - td = tr[trs].getElementsByTagName('td') - if(td[1].innerHTML === 'Passed') { - td[1].className ='passed'; - } - if(td[1].innerHTML === 'Failed') { - td[1].className ='failed'; - } - } - } - } + var ele = document.getElementsByTagName('table'); + for(var tb = 0; tb < ele.length;tb++) { + tbody = ele[tb].getElementsByTagName('tbody') + for(var tbd = 0; tbd < tbody.length;tbd++) { + tr = tbody[tbd].getElementsByTagName('tr'); + for (var trs = 0; trs < tr.length;trs++) { + td = tr[trs].getElementsByTagName('td') + if(td[1].innerHTML === 'Passed') { + td[1].className ='passed'; + } + if(td[1].innerHTML === 'Failed') { + td[1].className ='failed'; + } + } + } + } } </script> diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/test.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/test.yaml index c34c286cc..e3f6c492b 100644 --- a/ansible_collections/dellemc/enterprise_sonic/tests/regression/test.yaml +++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/test.yaml @@ -25,6 +25,8 @@ - sonic_bgp_af - sonic_bgp_neighbors - sonic_bgp_neighbors_af + - sonic_dhcp_snooping + - sonic_vlan_mapping - sonic_vrfs - sonic_vxlan - sonic_port_breakout @@ -36,4 +38,17 @@ - sonic_prefix_lists - sonic_static_routes - sonic_ntp + - sonic_logging + - sonic_ip_neighbor + - sonic_port_group + - sonic_dhcp_relay + - sonic_acl_interfaces + - sonic_l2_acls + - sonic_l3_acls + - sonic_lldp_global + - sonic_mac + - sonic_bfd + - sonic_copp + - sonic_route_maps + - sonic_stp - test_reports diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.15.txt b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.15.txt new file mode 100644 index 000000000..c2cf4ded5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.15.txt @@ -0,0 +1 @@ +plugins/action/sonic.py action-plugin-docs #action plugin for base class diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.16.txt b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.16.txt new file mode 100644 index 000000000..c2cf4ded5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.16.txt @@ -0,0 +1 @@ +plugins/action/sonic.py action-plugin-docs #action plugin for base class diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.17.txt b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.17.txt new file mode 100644 index 000000000..c2cf4ded5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.17.txt @@ -0,0 +1 @@ +plugins/action/sonic.py action-plugin-docs #action plugin for base class diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/compat/__init__.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/compat/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/compat/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/compat/mock.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/compat/mock.py new file mode 100644 index 000000000..031546093 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/compat/mock.py @@ -0,0 +1,23 @@ +""" +Compatibility shim for mock imports in modules and module_utils. +This can be removed once support for Python 2.7 is dropped. +""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +try: + from unittest.mock import ( # pylint: disable=unused-import + call, + patch, + mock_open, + MagicMock, + Mock, + ) +except ImportError: + from mock import ( + call, + patch, + mock_open, + MagicMock, + Mock, + ) diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/compat/unittest.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/compat/unittest.py new file mode 100644 index 000000000..b41677417 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/compat/unittest.py @@ -0,0 +1,29 @@ +# (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 + +# 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 +from unittest import * + +if not hasattr(TestCase, 'assertRaisesRegex'): + # added in Python 3.2 + TestCase.assertRaisesRegex = TestCase.assertRaisesRegexp diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/__init__.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/conftest.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/conftest.py new file mode 100644 index 000000000..a7d1e0475 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/conftest.py @@ -0,0 +1,31 @@ +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json + +import pytest + +from ansible.module_utils.six import string_types +from ansible.module_utils._text import to_bytes +from ansible.module_utils.common._collections_compat import MutableMapping + + +@pytest.fixture +def patch_ansible_module(request, mocker): + 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 patch_ansible_module pytest fixture') + + mocker.patch('ansible.module_utils.basic._ANSIBLE_ARGS', to_bytes(args)) diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/__init__.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/__init__.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/__init__.py diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml new file mode 100644 index 000000000..e7ca4429a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml @@ -0,0 +1,78 @@ +--- +merged_01: + module_args: + config: + authentication: + data: + local: true + fail_through: true + group: radius + existing_aaa_config: + - path: "data/openconfig-system:system/aaa" + response: + code: 200 + - path: "data/sonic-system-aaa:sonic-system-aaa" + response: + code: 200 + value: + expected_config_requests: + - path: "data/openconfig-system:system/aaa" + method: "patch" + data: + openconfig-system:aaa: + authentication: + config: + authentication-method: + - local + - radius + failthrough: 'True' +deleted_01: + module_args: + state: deleted + existing_aaa_config: + - path: "data/openconfig-system:system/aaa" + response: + code: 200 + value: + openconfig-system:aaa: + authentication: + config: + authentication-method: + - radius + - local + failthrough: true + expected_config_requests: + - path: "data/openconfig-system:system/aaa/authentication/config/authentication-method" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/authentication/config/failthrough" + method: "delete" + data: +deleted_02: + module_args: + config: + authentication: + data: + local: true + fail_through: true + group: radius + state: deleted + existing_aaa_config: + - path: "data/openconfig-system:system/aaa" + response: + code: 200 + value: + openconfig-system:aaa: + authentication: + config: + authentication-method: + - radius + - local + failthrough: true + expected_config_requests: + - path: "data/openconfig-system:system/aaa/authentication/config/authentication-method" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/authentication/config/failthrough" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_acl_interfaces.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_acl_interfaces.yaml new file mode 100644 index 000000000..84741e219 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_acl_interfaces.yaml @@ -0,0 +1,511 @@ +--- +merged_01: + module_args: + state: 'merged' + config: + - name: 'Eth1/1' + access_groups: + - type: 'mac' + acls: + - name: 'mac-acl-1' + direction: 'in' + - type: 'ipv4' + acls: + - name: 'ipv4-acl-1' + direction: 'in' + - name: 'ipv4-acl-2' + direction: 'out' + - name: 'Portchannel1.10' + access_groups: + - type: 'ipv6' + acls: + - name: 'ipv6-acl-1' + direction: 'out' + facts_get_requests: + - path: 'data/openconfig-acl:acl/interfaces' + response: + code: 200 + value: + openconfig-acl:interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + ingress-acl-sets: + ingress-acl-set: + - set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + config_requests: + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f1' + method: 'post' + data: + openconfig-acl:config: + id: 'Eth1/1' + openconfig-acl:interface-ref: + config: + interface: 'Eth1/1' + openconfig-acl:ingress-acl-sets: + ingress-acl-set: + - config: + set-name: 'mac-acl-1' + type: 'ACL_L2' + set-name: 'mac-acl-1' + type: 'ACL_L2' + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f1' + method: 'post' + data: + openconfig-acl:config: + id: 'Eth1/1' + openconfig-acl:interface-ref: + config: + interface: 'Eth1/1' + openconfig-acl:egress-acl-sets: + egress-acl-set: + - config: + set-name: 'ipv4-acl-2' + type: 'ACL_IPV4' + set-name: 'ipv4-acl-2' + type: 'ACL_IPV4' + - path: 'data/openconfig-acl:acl/interfaces/interface=PortChannel1.10' + method: 'post' + data: + openconfig-acl:config: + id: 'PortChannel1.10' + openconfig-acl:interface-ref: + config: + interface: 'PortChannel1' + subinterface: 10 + openconfig-acl:egress-acl-sets: + egress-acl-set: + - config: + set-name: 'ipv6-acl-1' + type: 'ACL_IPV6' + set-name: 'ipv6-acl-1' + type: 'ACL_IPV6' + +merged_02: + module_args: + state: 'merged' + config: + - name: 'Eth1/1' + access_groups: + - type: 'mac' + acls: + - name: 'mac-acl-1' + direction: 'in' + - type: 'ipv4' + acls: + - name: 'ipv4-acl-1' + direction: 'in' + - name: 'ipv4-acl-2' + direction: 'out' + - name: 'Portchannel1.10' + access_groups: + - type: 'ipv6' + acls: + - name: 'ipv6-acl-1' + direction: 'out' + facts_get_requests: + - path: 'data/openconfig-acl:acl/interfaces' + response: + code: 200 + value: + openconfig-acl:interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + ingress-acl-sets: + ingress-acl-set: + - set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + config: + set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + - set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + egress-acl-sets: + egress-acl-set: + - set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + - id: 'PortChannel1.10' + config: + id: 'PortChannel1.10' + egress-acl-sets: + egress-acl-set: + - set-name: 'ipv6-acl-1' + type: 'openconfig-acl:ACL_IPV6' + config: + set-name: 'ipv6-acl-1' + type: 'openconfig-acl:ACL_IPV6' + config_requests: [] + +replaced_01: + module_args: + state: 'replaced' + config: + - name: 'Eth1/1' + access_groups: + - type: 'mac' + acls: + - name: 'mac-acl-1' + direction: 'in' + - type: 'ipv4' + acls: + - name: 'ipv4-acl-1' + direction: 'in' + - type: 'ipv6' + acls: + - name: 'ipv6-acl-2' + direction: 'out' + - name: 'Eth1/2' + facts_get_requests: + - path: 'data/openconfig-acl:acl/interfaces' + response: + code: 200 + value: + openconfig-acl:interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + egress-acl-sets: + egress-acl-set: + - set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + ingress-acl-sets: + ingress-acl-set: + - set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + config: + set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + - set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + - id: 'Eth1/2' + config: + id: 'Eth1/2' + egress-acl-sets: + egress-acl-set: + - set-name: 'ipv6-acl-2' + type: 'openconfig-acl:ACL_IPV6' + config: + set-name: 'ipv6-acl-2' + type: 'openconfig-acl:ACL_IPV6' + ingress-acl-sets: + ingress-acl-set: + - set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + config: + set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + - id: 'Eth1/3' + config: + id: 'Eth1/3' + egress-acl-sets: + egress-acl-set: + - set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + ingress-acl-sets: + ingress-acl-set: + - set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + config: + set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + config_requests: + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f1/egress-acl-sets/egress-acl-set=ipv4-acl-2,ACL_IPV4' + method: 'delete' + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f2/egress-acl-sets/egress-acl-set=ipv6-acl-2,ACL_IPV6' + method: 'delete' + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f2/ingress-acl-sets/ingress-acl-set=mac-acl-1,ACL_L2' + method: 'delete' + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f1' + method: 'post' + data: + openconfig-acl:config: + id: 'Eth1/1' + openconfig-acl:interface-ref: + config: + interface: 'Eth1/1' + openconfig-acl:egress-acl-sets: + egress-acl-set: + - config: + set-name: 'ipv6-acl-2' + type: 'ACL_IPV6' + set-name: 'ipv6-acl-2' + type: 'ACL_IPV6' + +overridden_01: + module_args: + state: 'overridden' + config: + - name: 'Eth1/1' + access_groups: + - type: 'mac' + acls: + - name: 'mac-acl-1' + direction: 'in' + - type: 'ipv4' + acls: + - name: 'ipv4-acl-1' + direction: 'in' + - type: 'ipv6' + acls: + - name: 'ipv6-acl-2' + direction: 'out' + - name: 'Eth1/3' + access_groups: + - type: 'mac' + acls: + - name: 'mac-acl-2' + direction: 'out' + facts_get_requests: + - path: 'data/openconfig-acl:acl/interfaces' + response: + code: 200 + value: + openconfig-acl:interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + egress-acl-sets: + egress-acl-set: + - set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + ingress-acl-sets: + ingress-acl-set: + - set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + config: + set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + - set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + - id: 'Eth1/2' + config: + id: 'Eth1/2' + egress-acl-sets: + egress-acl-set: + - set-name: 'ipv6-acl-2' + type: 'openconfig-acl:ACL_IPV6' + config: + set-name: 'ipv6-acl-2' + type: 'openconfig-acl:ACL_IPV6' + ingress-acl-sets: + ingress-acl-set: + - set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + config: + set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + config_requests: + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f1/egress-acl-sets/egress-acl-set=ipv4-acl-2,ACL_IPV4' + method: 'delete' + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f2' + method: 'delete' + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f1' + method: 'post' + data: + openconfig-acl:config: + id: 'Eth1/1' + openconfig-acl:interface-ref: + config: + interface: 'Eth1/1' + openconfig-acl:egress-acl-sets: + egress-acl-set: + - config: + set-name: 'ipv6-acl-2' + type: 'ACL_IPV6' + set-name: 'ipv6-acl-2' + type: 'ACL_IPV6' + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f3' + method: 'post' + data: + openconfig-acl:config: + id: 'Eth1/3' + openconfig-acl:interface-ref: + config: + interface: 'Eth1/3' + openconfig-acl:egress-acl-sets: + egress-acl-set: + - config: + set-name: 'mac-acl-2' + type: 'ACL_L2' + set-name: 'mac-acl-2' + type: 'ACL_L2' + +deleted_01: + module_args: + state: 'deleted' + config: + - name: 'Eth1/1' + access_groups: + - type: 'mac' + acls: + - name: 'mac-acl-1' + direction: 'in' + - type: 'ipv4' + acls: + - name: 'ipv4-acl-1' + direction: 'in' + - name: 'Eth1/2' + access_groups: + - type: 'ipv4' + - type: 'ipv6' + - name: 'Eth1/3' + facts_get_requests: + - path: 'data/openconfig-acl:acl/interfaces' + response: + code: 200 + value: + openconfig-acl:interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + egress-acl-sets: + egress-acl-set: + - set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + ingress-acl-sets: + ingress-acl-set: + - set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + config: + set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + - set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + - id: 'Eth1/2' + config: + id: 'Eth1/2' + egress-acl-sets: + egress-acl-set: + - set-name: 'ipv6-acl-2' + type: 'openconfig-acl:ACL_IPV6' + config: + set-name: 'ipv6-acl-2' + type: 'openconfig-acl:ACL_IPV6' + ingress-acl-sets: + ingress-acl-set: + - set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + - id: 'Eth1/3' + config: + id: 'Eth1/3' + egress-acl-sets: + egress-acl-set: + - set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + ingress-acl-sets: + ingress-acl-set: + - set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + config: + set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + config_requests: + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f1/ingress-acl-sets/ingress-acl-set=mac-acl-1,ACL_L2' + method: 'delete' + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f1/ingress-acl-sets/ingress-acl-set=ipv4-acl-1,ACL_IPV4' + method: 'delete' + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f2/egress-acl-sets/egress-acl-set=ipv6-acl-2,ACL_IPV6' + method: 'delete' + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f2/ingress-acl-sets/ingress-acl-set=ipv4-acl-1,ACL_IPV4' + method: 'delete' + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f3' + method: 'delete' + +deleted_02: + module_args: + config: [] + state: 'deleted' + facts_get_requests: + - path: 'data/openconfig-acl:acl/interfaces' + response: + code: 200 + value: + openconfig-acl:interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + egress-acl-sets: + egress-acl-set: + - set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-2' + type: 'openconfig-acl:ACL_IPV4' + ingress-acl-sets: + ingress-acl-set: + - set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + config: + set-name: 'mac-acl-1' + type: 'openconfig-acl:ACL_L2' + - set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + - id: 'Eth1/2' + config: + id: 'Eth1/2' + egress-acl-sets: + egress-acl-set: + - set-name: 'ipv6-acl-2' + type: 'openconfig-acl:ACL_IPV6' + config: + set-name: 'ipv6-acl-2' + type: 'openconfig-acl:ACL_IPV6' + ingress-acl-sets: + ingress-acl-set: + - set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + config: + set-name: 'ipv4-acl-1' + type: 'openconfig-acl:ACL_IPV4' + config_requests: + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f1' + method: 'delete' + - path: 'data/openconfig-acl:acl/interfaces/interface=Eth1%2f2' + method: 'delete' diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_api.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_api.yaml new file mode 100644 index 000000000..f243dd5ab --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_api.yaml @@ -0,0 +1,7 @@ +--- +merged_01: + module_args: + url: data/openconfig-network-instance:network-instances/network-instance=Vlan100/ + method: "PUT" + body: {"openconfig-network-instance:network-instance": [{"name": "Vlan100", "config": {"name": "Vlan100"}}]} + status_code: 204 diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bfd.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bfd.yaml new file mode 100644 index 000000000..ed6c441b1 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bfd.yaml @@ -0,0 +1,598 @@ +--- +merged_01: + module_args: + config: + profiles: + - profile_name: 'p1' + enabled: True + transmit_interval: 120 + receive_interval: 200 + detect_multiplier: 2 + passive_mode: True + min_ttl: 140 + echo_interval: 150 + echo_mode: True + single_hops: + - remote_address: '196.88.6.1' + vrf: 'default' + interface: 'Ethernet20' + local_address: '1.1.1.1' + enabled: True + transmit_interval: 50 + receive_interval: 80 + detect_multiplier: 4 + passive_mode: True + echo_interval: 110 + echo_mode: True + profile_name: 'p1' + multi_hops: + - remote_address: '192.40.1.3' + vrf: 'default' + local_address: '3.3.3.3' + enabled: True + transmit_interval: 75 + receive_interval: 100 + detect_multiplier: 3 + passive_mode: True + min_ttl: 125 + profile_name: 'p1' + existing_bfd_config: + - path: "/data/openconfig-bfd:bfd" + response: + code: 200 + expected_config_requests: + - path: "/data/openconfig-bfd:bfd" + method: "patch" + data: + openconfig-bfd:bfd: + openconfig-bfd-ext:bfd-profile: + profile: + - profile-name: 'p1' + config: + profile-name: 'p1' + enabled: True + desired-minimum-tx-interval: 120 + required-minimum-receive: 200 + detection-multiplier: 2 + passive-mode: True + minimum-ttl: 140 + desired-minimum-echo-receive: 150 + echo-active: True + openconfig-bfd-ext:bfd-shop-sessions: + single-hop: + - remote-address: '196.88.6.1' + vrf: 'default' + interface: 'Ethernet20' + local-address: '1.1.1.1' + config: + remote-address: '196.88.6.1' + vrf: 'default' + interface: 'Ethernet20' + local-address: '1.1.1.1' + enabled: True + desired-minimum-tx-interval: 50 + required-minimum-receive: 80 + detection-multiplier: 4 + passive-mode: True + desired-minimum-echo-receive: 110 + echo-active: True + profile-name: 'p1' + openconfig-bfd-ext:bfd-mhop-sessions: + multi-hop: + - remote-address: '192.40.1.3' + vrf: 'default' + local-address: '3.3.3.3' + interface: 'null' + config: + remote-address: '192.40.1.3' + vrf: 'default' + local-address: '3.3.3.3' + enabled: True + desired-minimum-tx-interval: 75 + required-minimum-receive: 100 + detection-multiplier: 3 + passive-mode: True + minimum-ttl: 125 + profile-name: 'p1' + interface: 'null' + +replaced_01: + module_args: + config: + profiles: + - profile_name: 'p2' + enabled: False + single_hops: + - remote_address: '194.56.2.1' + vrf: 'default' + interface: 'Ethernet24' + local_address: '2.2.2.2' + echo_interval: 125 + echo_mode: True + profile_name: 'p1' + multi_hops: + - remote_address: '198.72.1.4' + vrf: 'default' + local_address: '4.4.4.4' + enabled: True + transmit_interval: 71 + receive_interval: 286 + detect_multiplier: 9 + state: replaced + existing_bfd_config: + - path: "/data/openconfig-bfd:bfd" + response: + code: 200 + value: + openconfig-bfd:bfd: + openconfig-bfd-ext:bfd-profile: + profile: + - profile-name: 'p2' + config: + profile-name: 'p2' + enabled: True + desired-minimum-tx-interval: 135 + required-minimum-receive: 225 + detection-multiplier: 10 + passive-mode: True + minimum-ttl: 250 + desired-minimum-echo-receive: 250 + echo-active: True + openconfig-bfd-ext:bfd-shop-sessions: + single-hop: + - remote-address: '194.56.2.1' + vrf: 'default' + interface: 'Ethernet24' + local-address: '2.2.2.2' + config: + remote-address: '194.56.2.1' + vrf: 'default' + interface: 'Ethernet24' + local-address: '2.2.2.2' + enabled: False + desired-minimum-tx-interval: 65 + required-minimum-receive: 95 + detection-multiplier: 7 + passive-mode: False + desired-minimum-echo-receive: 145 + echo-active: False + profile-name: 'p2' + openconfig-bfd-ext:bfd-mhop-sessions: + multi-hop: + - remote-address: '198.72.1.4' + vrf: 'default' + local-address: '4.4.4.4' + config: + remote-address: '198.72.1.4' + vrf: 'default' + local-address: '4.4.4.4' + enabled: False + desired-minimum-tx-interval: 70 + required-minimum-receive: 285 + detection-multiplier: 8 + passive-mode: False + minimum-ttl: 155 + profile-name: 'p2' + expected_config_requests: + - path: "/data/openconfig-bfd:bfd" + method: "patch" + data: + openconfig-bfd:bfd: + openconfig-bfd-ext:bfd-profile: + profile: + - profile-name: 'p2' + config: + profile-name: 'p2' + enabled: False + desired-minimum-tx-interval: 300 + required-minimum-receive: 300 + detection-multiplier: 3 + passive-mode: False + minimum-ttl: 254 + desired-minimum-echo-receive: 300 + echo-active: False + openconfig-bfd-ext:bfd-shop-sessions: + single-hop: + - remote-address: '194.56.2.1' + vrf: 'default' + interface: 'Ethernet24' + local-address: '2.2.2.2' + config: + remote-address: '194.56.2.1' + vrf: 'default' + interface: 'Ethernet24' + local-address: '2.2.2.2' + enabled: True + desired-minimum-tx-interval: 300 + required-minimum-receive: 300 + detection-multiplier: 3 + passive-mode: False + desired-minimum-echo-receive: 125 + echo-active: True + profile-name: 'p1' + openconfig-bfd-ext:bfd-mhop-sessions: + multi-hop: + - remote-address: '198.72.1.4' + vrf: 'default' + local-address: '4.4.4.4' + interface: 'null' + config: + remote-address: '198.72.1.4' + vrf: 'default' + local-address: '4.4.4.4' + enabled: True + desired-minimum-tx-interval: 71 + required-minimum-receive: 286 + detection-multiplier: 9 + passive-mode: False + minimum-ttl: 254 + interface: 'null' +overridden_01: + module_args: + config: + profiles: + - profile_name: 'p3' + enabled: True + transmit_interval: 110 + receive_interval: 230 + detect_multiplier: 10 + passive_mode: True + min_ttl: 170 + echo_interval: 140 + echo_mode: True + - profile_name: 'p4' + single_hops: + - remote_address: '182.98.4.1' + vrf: 'default' + interface: 'Ethernet28' + local_address: '3.3.3.3' + enabled: True + transmit_interval: 42 + receive_interval: 84 + detect_multiplier: 8 + passive_mode: True + echo_interval: 115 + echo_mode: True + profile_name: 'p3' + - remote_address: '183.98.3.2' + vrf: 'default' + interface: 'Ethernet24' + local_address: '1.2.3.4' + multi_hops: + - remote_address: '182.44.1.2' + vrf: 'default' + local_address: '2.2.2.2' + enabled: True + transmit_interval: 74 + receive_interval: 101 + detect_multiplier: 6 + passive_mode: True + min_ttl: 127 + profile_name: 'p3' + - remote_address: '162.45.5.1' + vrf: 'default' + local_address: '2.1.1.1' + state: overridden + existing_bfd_config: + - path: "/data/openconfig-bfd:bfd" + response: + code: 200 + value: + openconfig-bfd:bfd: + openconfig-bfd-ext:bfd-profile: + profile: + - profile-name: 'p2' + config: + profile-name: 'p2' + enabled: True + desired-minimum-tx-interval: 135 + required-minimum-receive: 225 + detection-multiplier: 10 + passive-mode: True + minimum-ttl: 250 + desired-minimum-echo-receive: 250 + echo-active: True + openconfig-bfd-ext:bfd-shop-sessions: + single-hop: + - remote-address: '194.56.2.1' + vrf: 'default' + interface: 'Ethernet24' + local-address: '2.2.2.2' + config: + remote-address: '194.56.2.1' + vrf: 'default' + interface: 'Ethernet24' + local-address: '2.2.2.2' + enabled: False + desired-minimum-tx-interval: 65 + required-minimum-receive: 95 + detection-multiplier: 7 + passive-mode: False + desired-minimum-echo-receive: 145 + echo-active: False + profile-name: 'p2' + openconfig-bfd-ext:bfd-mhop-sessions: + multi-hop: + - remote-address: '198.72.1.4' + vrf: 'default' + local-address: '4.4.4.4' + config: + remote-address: '198.72.1.4' + vrf: 'default' + local-address: '4.4.4.4' + enabled: False + desired-minimum-tx-interval: 70 + required-minimum-receive: 285 + detection-multiplier: 8 + passive-mode: False + minimum-ttl: 155 + profile-name: 'p2' + expected_config_requests: + - path: "/data/openconfig-bfd:bfd" + method: "patch" + data: + openconfig-bfd:bfd: + openconfig-bfd-ext:bfd-profile: + profile: + - profile-name: 'p3' + config: + profile-name: 'p3' + enabled: True + desired-minimum-tx-interval: 110 + required-minimum-receive: 230 + detection-multiplier: 10 + passive-mode: True + minimum-ttl: 170 + desired-minimum-echo-receive: 140 + echo-active: True + openconfig-bfd-ext:bfd-shop-sessions: + single-hop: + - remote-address: '182.98.4.1' + vrf: 'default' + interface: 'Ethernet28' + local-address: '3.3.3.3' + config: + remote-address: '182.98.4.1' + vrf: 'default' + interface: 'Ethernet28' + local-address: '3.3.3.3' + enabled: True + desired-minimum-tx-interval: 42 + required-minimum-receive: 84 + detection-multiplier: 8 + passive-mode: True + desired-minimum-echo-receive: 115 + echo-active: True + profile-name: 'p3' + openconfig-bfd-ext:bfd-mhop-sessions: + multi-hop: + - remote-address: '162.45.5.1' + vrf: 'default' + local-address: '2.1.1.1' + interface: 'null' + config: + remote-address: '162.45.5.1' + vrf: 'default' + local-address: '2.1.1.1' + enabled: True + desired-minimum-tx-interval: 300 + required-minimum-receive: 300 + detection-multiplier: 3 + passive-mode: False + minimum-ttl: 254 + interface: 'null' + +deleted_01: + module_args: + config: + profiles: + - profile_name: 'p3' + enabled: True + transmit_interval: 110 + receive_interval: 230 + detect_multiplier: 10 + passive_mode: True + min_ttl: 170 + echo_interval: 140 + echo_mode: True + - profile_name: 'p4' + single_hops: + - remote_address: '182.98.4.1' + vrf: 'default' + interface: 'Ethernet28' + local_address: '3.3.3.3' + enabled: True + transmit_interval: 42 + receive_interval: 84 + detect_multiplier: 8 + passive_mode: True + echo_interval: 115 + echo_mode: True + profile_name: 'p3' + - remote_address: '183.98.3.2' + vrf: 'default' + interface: 'Ethernet24' + local_address: '1.2.3.4' + multi_hops: + - remote_address: '182.44.1.2' + vrf: 'default' + local_address: '2.2.2.2' + enabled: True + transmit_interval: 74 + receive_interval: 101 + detect_multiplier: 6 + passive_mode: True + min_ttl: 127 + profile_name: 'p3' + - remote_address: '162.45.5.1' + vrf: 'default' + local_address: '2.1.1.1' + state: deleted + existing_bfd_config: + - path: "/data/openconfig-bfd:bfd" + response: + code: 200 + value: + openconfig-bfd:bfd: + openconfig-bfd-ext:bfd-mhop-sessions: + multi-hop: + - config: + desired-minimum-tx-interval: 300 + detection-multiplier: 3 + enabled: true + interface: 'null' + local-address: 2.1.1.1 + minimum-ttl: 254 + passive-mode: false + remote-address: 162.45.5.1 + required-minimum-receive: 300 + vrf: default + interface: 'null' + local-address: 2.1.1.1 + remote-address: 162.45.5.1 + vrf: default + - config: + desired-minimum-tx-interval: 74 + detection-multiplier: 6 + enabled: true + interface: 'null' + local-address: 2.2.2.2 + minimum-ttl: 127 + passive-mode: true + profile-name: p3 + remote-address: 182.44.1.2 + required-minimum-receive: 101 + vrf: default + interface: 'null' + local-address: 2.2.2.2 + remote-address: 182.44.1.2 + vrf: default + openconfig-bfd-ext:bfd-profile: + profile: + - config: + desired-minimum-echo-receive: 140 + desired-minimum-tx-interval: 110 + detection-multiplier: 10 + echo-active: true + enabled: true + minimum-ttl: 170 + passive-mode: true + profile-name: p3 + required-minimum-receive: 230 + profile-name: p3 + - config: + desired-minimum-echo-receive: 300 + desired-minimum-tx-interval: 300 + detection-multiplier: 3 + echo-active: false + enabled: true + minimum-ttl: 254 + passive-mode: false + profile-name: p4 + required-minimum-receive: 300 + profile-name: p4 + openconfig-bfd-ext:bfd-shop-sessions: + single-hop: + - config: + desired-minimum-echo-receive: 115 + desired-minimum-tx-interval: 42 + detection-multiplier: 8 + echo-active: true + enabled: true + interface: Ethernet28 + local-address: 3.3.3.3 + passive-mode: true + profile-name: p3 + remote-address: 182.98.4.1 + required-minimum-receive: 84 + vrf: default + interface: Ethernet28 + local-address: 3.3.3.3 + remote-address: 182.98.4.1 + vrf: default + - config: + desired-minimum-echo-receive: 300 + desired-minimum-tx-interval: 300 + detection-multiplier: 3 + echo-active: false + enabled: true + interface: Ethernet24 + local-address: 1.2.3.4 + passive-mode: false + remote-address: 183.98.3.2 + required-minimum-receive: 300 + vrf: default + interface: Ethernet24 + local-address: 1.2.3.4 + remote-address: 183.98.3.2 + vrf: default + expected_config_requests: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-profile/profile=p3/config/desired-minimum-tx-interval" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-profile/profile=p3/config/required-minimum-receive" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-profile/profile=p3/config/detection-multiplier" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-profile/profile=p3/config/passive-mode" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-profile/profile=p3/config/minimum-ttl" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-profile/profile=p3/config/desired-minimum-echo-receive" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-profile/profile=p3/config/echo-active" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-profile/profile=p4" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-shop-sessions/single-hop=182.98.4.1,Ethernet28,default,3.3.3.3/config/desired-minimum-tx-interval" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-shop-sessions/single-hop=182.98.4.1,Ethernet28,default,3.3.3.3/config/required-minimum-receive" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-shop-sessions/single-hop=182.98.4.1,Ethernet28,default,3.3.3.3/config/detection-multiplier" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-shop-sessions/single-hop=182.98.4.1,Ethernet28,default,3.3.3.3/config/passive-mode" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-shop-sessions/single-hop=182.98.4.1,Ethernet28,default,3.3.3.3/config/desired-minimum-echo-receive" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-shop-sessions/single-hop=182.98.4.1,Ethernet28,default,3.3.3.3/config/echo-active" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-shop-sessions/single-hop=182.98.4.1,Ethernet28,default,3.3.3.3/config/profile-name" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-shop-sessions/single-hop=183.98.3.2,Ethernet24,default,1.2.3.4" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-mhop-sessions/multi-hop=182.44.1.2,null,default,2.2.2.2/config/desired-minimum-tx-interval" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-mhop-sessions/multi-hop=182.44.1.2,null,default,2.2.2.2/config/required-minimum-receive" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-mhop-sessions/multi-hop=182.44.1.2,null,default,2.2.2.2/config/detection-multiplier" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-mhop-sessions/multi-hop=182.44.1.2,null,default,2.2.2.2/config/passive-mode" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-mhop-sessions/multi-hop=182.44.1.2,null,default,2.2.2.2/config/minimum-ttl" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-mhop-sessions/multi-hop=182.44.1.2,null,default,2.2.2.2/config/profile-name" + method: "delete" + data: + - path: "/data/openconfig-bfd:bfd/openconfig-bfd-ext:bfd-mhop-sessions/multi-hop=162.45.5.1,null,default,2.1.1.1" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp.yaml new file mode 100644 index 000000000..1c5d59fba --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp.yaml @@ -0,0 +1,473 @@ +--- +merged_01: + module_args: + config: + - bgp_as: 4 + router_id: 10.2.2.4 + rt_delay: 10 + log_neighbor_changes: False + timers: + holdtime: 20 + keepalive_interval: 30 + bestpath: + as_path: + confed: True + ignore: True + multipath_relax: False + multipath_relax_as_set: True + compare_routerid: True + med: + confed: True + missing_as_worst: True + always_compare_med: True + max_med: + on_startup: + timer: 667 + med_val: 7878 + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 4 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config" + method: "patch" + data: + openconfig-network-instance:config: + router-id: "10.2.2.4" + as: 4.0 + route-map-process-delay: 10 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config/hold-time" + method: "patch" + data: + hold-time: 20 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config/keepalive-interval" + method: "patch" + data: + keepalive-interval: 30 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/max-med" + method: "patch" + data: + max-med: + config: + max-med-val: 7878 + time: 667 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options" + method: "patch" + data: + route-selection-options: + config: + external-compare-router-id: True + compare-confed-as-path: True + ignore-as-path-length: True + med-confed: True + med-missing-as-worst: True + always-compare-med: True + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/use-multiple-paths/ebgp/config" + method: "patch" + data: + openconfig-network-instance:config: + as-set: True +deleted_01: + module_args: + config: + - bgp_as: 4 + router_id: 10.2.2.4 + rt_delay: 10 + log_neighbor_changes: False + bestpath: + as_path: + confed: True + ignore: True + multipath_relax: False + multipath_relax_as_set: True + compare_routerid: True + med: + confed: True + missing_as_worst: True + always_compare_med: True + timers: + holdtime: 20 + keepalive_interval: 30 + max_med: + on_startup: + timer: 667 + med_val: 7878 + state: deleted + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 4 + router-id: "10.2.2.4" + route-map-process-delay: 10 + hold-time: 20 + keepalive-interval: 30 + logging-options: + config: + log-neighbor-state-changes: False + route-selection-options: + config: + always-compare-med: True + external-compare-router-id: True + ignore-as-path-length: True + compare-confed-as-path: True + med-confed: True + med-missing-as-worst: True + use-multiple-paths: + ebgp: + config: + allow-multiple-as: False + as-set: False + max-med: + config: + time: 667 + max-med-val: 7878 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config/hold-time" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config/keepalive-interval" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config/router-id" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config/route-map-process-delay" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/max-med/config/max-med-val" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/max-med/config/time" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options" + method: "patch" + data: + route-selection-options: + config: + external-compare-router-id: False + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options/config/always-compare-med" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options/config/compare-confed-as-path" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options/config/ignore-as-path-length" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options/config/med-confed" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options/config/med-missing-as-worst" + method: "delete" + +deleted_02: + module_args: + config: + state: deleted + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 4 + router-id: "10.2.2.4" + route-map-process-delay: 10 + hold-time: 20 + keepalive-interval: 30 + logging-options: + config: + log-neighbor-state-changes: False + route-selection-options: + config: + always-compare-med: True + external-compare-router-id: True + ignore-as-path-length: True + compare-confed-as-path: True + med-confed: True + med-missing-as-worst: True + use-multiple-paths: + ebgp: + config: + allow-multiple-as: False + as-set: False + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp" + method: "delete" + +replaced_01: + module_args: + config: + - bgp_as: 5 + vrf_name: 'VrfReg1' + router_id: 10.2.2.5 + timers: + holdtime: 20 + keepalive_interval: 30 + - bgp_as: 4 + router_id: 10.2.2.4 + max_med: + on_startup: + timer: 776 + med_val: 8787 + state: replaced + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 4 + router-id: "10.2.2.4" + route-map-process-delay: 10 + hold-time: 20 + keepalive-interval: 30 + logging-options: + config: + log-neighbor-state-changes: False + route-selection-options: + config: + always-compare-med: True + external-compare-router-id: True + ignore-as-path-length: True + compare-confed-as-path: True + med-confed: True + med-missing-as-worst: True + use-multiple-paths: + ebgp: + config: + allow-multiple-as: False + as-set: False + max-med: + config: + time: 667 + max-med-val: 7878 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config/hold-time" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config/keepalive-interval" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config/route-map-process-delay" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/max-med/config/max-med-val" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/max-med/config/time" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/logging-options/config/log-neighbor-state-changes" + method: "patch" + data: + log-neighbor-state-changes: True + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options" + method: "patch" + data: + route-selection-options: + config: + external-compare-router-id: False + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options/config/always-compare-med" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options/config/compare-confed-as-path" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options/config/ignore-as-path-length" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options/config/med-confed" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options/config/med-missing-as-worst" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config" + method: "patch" + data: + openconfig-network-instance:config: + as: 4.0 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/max-med" + method: "patch" + data: + max-med: + config: + max-med-val: 8787 + time: 776 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol/" + method: "patch" + data: + openconfig-network-instance:protocol: + - name: "bgp" + identifier: "openconfig-policy-types:BGP" + bgp: + global: + config: + as: 5.0 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/global/config" + method: "patch" + data: + openconfig-network-instance:config: + router-id: "10.2.2.5" + as: 5.0 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/global/config/hold-time" + method: "patch" + data: + hold-time: 20 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/global/config/keepalive-interval" + method: "patch" + data: + keepalive-interval: 30 + +overridden_01: + module_args: + config: + - bgp_as: 5 + vrf_name: 'VrfReg2' + router_id: 10.2.2.6 + rt_delay: 10 + log_neighbor_changes: True + - bgp_as: 4 + router_id: 10.2.2.5 + rt_delay: 10 + bestpath: + as_path: + confed: True + ignore: True + compare_routerid: True + med: + confed: True + missing_as_worst: True + max_med: + on_startup: + timer: 776 + med_val: 8787 + state: overridden + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 4 + router-id: "10.2.2.4" + route-map-process-delay: 10 + hold-time: 180 + keepalive-interval: 60 + logging-options: + config: + log-neighbor-state-changes: True + route-selection-options: + config: + always-compare-med: True + external-compare-router-id: True + ignore-as-path-length: True + compare-confed-as-path: True + med-confed: True + med-missing-as-worst: True + use-multiple-paths: + ebgp: + config: + allow-multiple-as: False + as-set: False + max-med: + config: + time: 667 + max-med-val: 7878 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 5 + router-id: "10.2.2.6" + hold-time: 180 + keepalive-interval: 60 + logging-options: + config: + log-neighbor-state-changes: True + route-selection-options: + config: + always-compare-med: False + external-compare-router-id: False + ignore-as-path-length: False + use-multiple-paths: + ebgp: + config: + allow-multiple-as: False + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg2/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + - vrf_name: VrfReg2 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/max-med/config/max-med-val" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/max-med/config/time" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/route-selection-options/config/always-compare-med" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config" + method: "patch" + data: + openconfig-network-instance:config: + router-id: "10.2.2.5" + as: 4.0 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/max-med" + method: "patch" + data: + max-med: + config: + max-med-val: 8787 + time: 776 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg2/protocols/protocol/" + method: "patch" + data: + openconfig-network-instance:protocol: + - name: "bgp" + identifier: "openconfig-policy-types:BGP" + bgp: + global: + config: + as: 5.0 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg2/protocols/protocol=BGP,bgp/bgp/global/config" + method: "patch" + data: + openconfig-network-instance:config: + router-id: "10.2.2.6" + route-map-process-delay: 10 + as: 5.0 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg2/protocols/protocol=BGP,bgp/bgp/global/logging-options/config/log-neighbor-state-changes" + method: "patch" + data: + log-neighbor-state-changes: True diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_af.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_af.yaml new file mode 100644 index 000000000..22539357c --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_af.yaml @@ -0,0 +1,1007 @@ +--- +merged_01: + module_args: + config: + - bgp_as: 51 + address_family: + afis: + - afi: l2vpn + safi: evpn + advertise_pip: True + advertise_pip_ip: "3.3.3.3" + advertise_pip_peer_ip: "4.4.4.4" + advertise_svi_ip: True + advertise_all_vni: False + advertise_default_gw: False + route_advertise_list: + - advertise_afi: ipv4 + route_map: aaa + - advertise_afi: ipv6 + route_map: bbb + rd: "3.3.3.3:33" + rt_in: + - "22:22" + dampening: True + - afi: ipv4 + safi: unicast + network: + - 2.2.2.2/16 + - 192.168.10.1/32 + dampening: True + - afi: ipv6 + safi: unicast + dampening: True + max_path: + ebgp: 4 + ibgp: 5 + redistribute: + - metric: "21" + protocol: connected + route_map: bb + - metric: "27" + protocol: ospf + route_map: aa + - metric: "26" + protocol: static + route_map: bb + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 51 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections" + response: + code: 200 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + method: "patch" + data: + openconfig-network-instance:global: + afi-safis: + afi-safi: + - afi-safi-name: 'openconfig-bgp-types:L2VPN_EVPN' + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + method: "patch" + data: + openconfig-network-instance:global: + afi-safis: + afi-safi: + - afi-safi-name: 'openconfig-bgp-types:L2VPN_EVPN' + l2vpn-evpn: + openconfig-bgp-evpn-ext:config: + advertise-pip: True + advertise-pip-ip: 3.3.3.3 + advertise-pip-peer-ip: 4.4.4.4 + advertise-svi-ip: True + advertise-all-vni: False + advertise-default-gw: False + route-distinguisher: 3.3.3.3:33 + import-rts: + - '22:22' + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:route-advertise/route-advertise-list" + method: "patch" + data: + openconfig-bgp-evpn-ext:route-advertise-list: + - advertise-afi-safi: IPV4_UNICAST + config: + advertise-afi-safi: IPV4_UNICAST + route-map: + - aaa + - advertise-afi-safi: IPV6_UNICAST + config: + advertise-afi-safi: IPV6_UNICAST + route-map: + - bbb + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + method: "patch" + data: + openconfig-network-instance:global: + afi-safis: + afi-safi: + - afi-safi-name: 'openconfig-bgp-types:IPV4_UNICAST' + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + method: "patch" + data: + openconfig-network-instance:global: + afi-safis: + afi-safi: + - afi-safi-name: 'openconfig-bgp-types:IPV6_UNICAST' + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV4_UNICAST/network-config" + method: "patch" + data: + network-config: + network: + - config: + prefix: '2.2.2.2/16' + prefix: '2.2.2.2/16' + - config: + prefix: '192.168.10.1/32' + prefix: '192.168.10.1/32' + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV4_UNICAST/route-flap-damping" + method: "patch" + data: + route-flap-damping: + config: + enabled: True + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV6_UNICAST/use-multiple-paths" + method: "patch" + data: + openconfig-network-instance:use-multiple-paths: + ebgp: + config: + maximum-paths: 4 + ibgp: + config: + maximum-paths: 5 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections" + method: "patch" + data: + openconfig-network-instance:table-connections: + table-connection: + - address-family: openconfig-types:IPV6 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:DIRECTLY_CONNECTED + config: + address-family: openconfig-types:IPV6 + metric: 21.0 + import-policy: + - bb + - address-family: openconfig-types:IPV6 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:OSPF + config: + address-family: openconfig-types:IPV6 + metric: 27.0 + import-policy: + - aa + - address-family: openconfig-types:IPV6 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:STATIC + config: + address-family: openconfig-types:IPV6 + metric: 26.0 + import-policy: + - bb +merged_02: + module_args: + config: + - bgp_as: 51 + address_family: + afis: + - afi: ipv4 + safi: unicast + dampening: false + max_path: + ebgp: 2 + ibgp: 1 + advertise_all_vni: false + redistribute: + - metric: "20" + protocol: connected + route_map: aa + - metric: "26" + protocol: ospf + route_map: bb + - metric: "25" + protocol: static + route_map: aa + - afi: ipv6 + safi: unicast + max_path: + ebgp: 1 + ibgp: 1 + advertise_all_vni: true + redistribute: + - metric: "21" + protocol: connected + route_map: bb + - metric: "27" + protocol: ospf + route_map: aa + - metric: "28" + protocol: static + route_map: aa + - afi: l2vpn + safi: evpn + advertise_pip: True + advertise_pip_ip: "3.3.3.3" + advertise_pip_peer_ip: "4.4.4.4" + advertise_svi_ip: True + advertise_all_vni: True + advertise_default_gw: False + dampening: True + max_path: + ebgp: 4 + ibgp: 5 + route_advertise_list: + - advertise_afi: ipv4 + route_map: aaNew + - advertise_afi: ipv6 + route_map: bbNew + state: merged + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 51 + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + route-flap-damping: + config: + enabled: false + use-multiple-paths: + ebgp: + config: + maximum-paths: 2 + ibgp: + config: + maximum-paths: 1 + - afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + use-multiple-paths: + ebgp: + config: + maximum-paths: 1 + ibgp: + config: + maximum-paths: 1 + - afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + config: + afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + l2vpn-evpn: + openconfig-bgp-evpn-ext:config: + advertise-all-vni: true + advertise-default-gw: true + advertise-pip: true + advertise-pip-ip: 3.3.3.3 + advertise-pip-peer-ip: 4.4.4.4 + advertise-svi-ip: true + openconfig-bgp-evpn-ext:route-advertise: + route-advertise-list: + - advertise-afi-safi: openconfig-bgp-types:IPV4_UNICAST + config: + advertise-afi-safi: openconfig-bgp-types:IPV4_UNICAST + route-map: + - aa + - advertise-afi-safi: openconfig-bgp-types:IPV6_UNICAST + config: + advertise-afi-safi: openconfig-bgp-types:IPV6_UNICAST + route-map: + - bb + openconfig-bgp-evpn-ext:vnis: + vni: + - config: + vni-number: 600 + vni-number: 600 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections" + response: + code: 200 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + method: "patch" + data: + openconfig-network-instance:global: + afi-safis: + afi-safi: + - afi-safi-name: 'openconfig-bgp-types:L2VPN_EVPN' + l2vpn-evpn: + openconfig-bgp-evpn-ext:config: + advertise-default-gw: False + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:route-advertise/route-advertise-list" + method: "patch" + data: + openconfig-bgp-evpn-ext:route-advertise-list: + - advertise-afi-safi: IPV4_UNICAST + config: + advertise-afi-safi: IPV4_UNICAST + route-map: + - aaNew + - advertise-afi-safi: IPV6_UNICAST + config: + advertise-afi-safi: IPV6_UNICAST + route-map: + - bbNew + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections" + method: "patch" + data: + openconfig-network-instance:table-connections: + table-connection: + - address-family: openconfig-types:IPV4 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:DIRECTLY_CONNECTED + config: + address-family: openconfig-types:IPV4 + metric: 20.0 + import-policy: + - aa + - address-family: openconfig-types:IPV4 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:OSPF + config: + address-family: openconfig-types:IPV4 + metric: 26.0 + import-policy: + - bb + - address-family: openconfig-types:IPV4 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:STATIC + config: + address-family: openconfig-types:IPV4 + metric: 25.0 + import-policy: + - aa + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections" + method: "patch" + data: + openconfig-network-instance:table-connections: + table-connection: + - address-family: openconfig-types:IPV6 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:DIRECTLY_CONNECTED + config: + address-family: openconfig-types:IPV6 + metric: 21.0 + import-policy: + - bb + - address-family: openconfig-types:IPV6 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:OSPF + config: + address-family: openconfig-types:IPV6 + metric: 27.0 + import-policy: + - aa + - address-family: openconfig-types:IPV6 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:STATIC + config: + address-family: openconfig-types:IPV6 + metric: 28.0 + import-policy: + - aa +merged_03: + module_args: + config: + - bgp_as: 51 + address_family: + afis: + - afi: ipv4 + safi: unicast + dampening: false + max_path: + ebgp: 2 + ibgp: 1 + advertise_all_vni: false + - afi: ipv6 + safi: unicast + max_path: + ebgp: 1 + ibgp: 1 + advertise_all_vni: true + - afi: l2vpn + safi: evpn + advertise_pip: True + advertise_pip_ip: "3.3.3.99" + advertise_pip_peer_ip: "4.4.4.4" + advertise_svi_ip: True + advertise_all_vni: True + advertise_default_gw: True + dampening: True + max_path: + ebgp: 4 + ibgp: 5 + route_advertise_list: + - advertise_afi: ipv4 + route_map: aa + - advertise_afi: ipv6 + route_map: bb + state: merged + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 51 + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + route-flap-damping: + config: + enabled: false + use-multiple-paths: + ebgp: + config: + maximum-paths: 2 + ibgp: + config: + maximum-paths: 1 + - afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + use-multiple-paths: + ebgp: + config: + maximum-paths: 1 + ibgp: + config: + maximum-paths: 1 + - afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + config: + afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + l2vpn-evpn: + openconfig-bgp-evpn-ext:config: + advertise-all-vni: true + advertise-default-gw: true + advertise-pip: true + advertise-pip-ip: 3.3.3.3 + advertise-pip-peer-ip: 4.4.4.4 + advertise-svi-ip: true + openconfig-bgp-evpn-ext:route-advertise: + route-advertise-list: + - advertise-afi-safi: openconfig-bgp-types:IPV4_UNICAST + config: + advertise-afi-safi: openconfig-bgp-types:IPV4_UNICAST + route-map: + - aa + - advertise-afi-safi: openconfig-bgp-types:IPV6_UNICAST + config: + advertise-afi-safi: openconfig-bgp-types:IPV6_UNICAST + route-map: + - bb + openconfig-bgp-evpn-ext:vnis: + vni: + - config: + vni-number: 600 + vni-number: 600 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections" + response: + code: 200 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + method: "patch" + data: + openconfig-network-instance:global: + afi-safis: + afi-safi: + - afi-safi-name: 'openconfig-bgp-types:L2VPN_EVPN' + l2vpn-evpn: + openconfig-bgp-evpn-ext:config: + advertise-pip-ip: 3.3.3.99 + +deleted_01: + module_args: + config: + - bgp_as: 51 + address_family: + afis: + - afi: ipv4 + safi: unicast + dampening: true + max_path: + ebgp: 2 + ibgp: 1 + advertise_all_vni: false + redistribute: + - metric: "20" + protocol: connected + route_map: aa + - metric: "26" + protocol: ospf + route_map: bb + - metric: "25" + protocol: static + route_map: aa + - afi: ipv6 + safi: unicast + max_path: + ebgp: 2 + ibgp: 1 + advertise_all_vni: true + redistribute: + - metric: "21" + protocol: connected + route_map: bb + - metric: "27" + protocol: ospf + route_map: aa + - metric: "28" + protocol: static + route_map: aa + - afi: l2vpn + safi: evpn + advertise_pip: True + advertise_pip_ip: "3.3.3.3" + advertise_pip_peer_ip: "4.4.4.4" + advertise_svi_ip: True + advertise_all_vni: True + advertise_default_gw: False + dampening: True + route_advertise_list: + - advertise_afi: ipv4 + route_map: aa + - advertise_afi: ipv6 + route_map: bb + state: deleted + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 51 + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + route-flap-damping: + config: + enabled: true + use-multiple-paths: + ebgp: + config: + maximum-paths: 2 + ibgp: + config: + maximum-paths: 2 + - afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + use-multiple-paths: + ebgp: + config: + maximum-paths: 2 + ibgp: + config: + maximum-paths: 2 + - afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + config: + afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + l2vpn-evpn: + openconfig-bgp-evpn-ext:config: + advertise-all-vni: true + advertise-default-gw: true + advertise-pip: true + advertise-pip-ip: 3.3.3.3 + advertise-pip-peer-ip: 4.4.4.4 + advertise-svi-ip: true + openconfig-bgp-evpn-ext:route-advertise: + route-advertise-list: + - advertise-afi-safi: openconfig-bgp-types:IPV4_UNICAST + config: + advertise-afi-safi: openconfig-bgp-types:IPV4_UNICAST + route-map: + - aa + - advertise-afi-safi: openconfig-bgp-types:IPV6_UNICAST + config: + advertise-afi-safi: openconfig-bgp-types:IPV6_UNICAST + route-map: + - bb + openconfig-bgp-evpn-ext:vnis: + vni: + - config: + vni-number: 600 + vni-number: 600 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections" + response: + code: 200 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV4_UNICAST/route-flap-damping/config/enabled" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV4_UNICAST/use-multiple-paths/ebgp/config/maximum-paths" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV4_UNICAST/use-multiple-paths/ibgp/config/maximum-paths" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV6_UNICAST/use-multiple-paths/ebgp/config/maximum-paths" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV6_UNICAST/use-multiple-paths/ibgp/config/maximum-paths" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-all-vni" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-pip" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-pip-ip" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-pip-peer-ip" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-svi-ip" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:route-advertise/route-advertise-list=IPV4_UNICAST/config/route-map=aa" + method: "delete" + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:route-advertise/route-advertise-list=IPV6_UNICAST/config/route-map=bb" + method: "delete" + +deleted_02: + module_args: + config: + state: deleted + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 51 + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + network-config: + network: + - config: + prefix: 22.22.22.22/16 + prefix: 22.22.22.22/16 + route-flap-damping: + config: + enabled: true + use-multiple-paths: + ebgp: + config: + maximum-paths: 2 + ibgp: + config: + maximum-paths: 1 + - afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + use-multiple-paths: + ebgp: + config: + maximum-paths: 1 + ibgp: + config: + maximum-paths: 1 + - afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + config: + afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + route-flap-damping: + config: + enabled: true + network-config: + network: + - config: + prefix: 22.22.22.22/16 + prefix: 22.22.22.22/16 + l2vpn-evpn: + openconfig-bgp-evpn-ext:config: + advertise-all-vni: true + advertise-default-gw: true + advertise-pip: true + advertise-pip-ip: 3.3.3.3 + advertise-pip-peer-ip: 4.4.4.4 + advertise-svi-ip: true + openconfig-bgp-evpn-ext:route-advertise: + route-advertise-list: + - advertise-afi-safi: openconfig-bgp-types:IPV4_UNICAST + config: + advertise-afi-safi: openconfig-bgp-types:IPV4_UNICAST + route-map: + - aa + - advertise-afi-safi: openconfig-bgp-types:IPV6_UNICAST + config: + advertise-afi-safi: openconfig-bgp-types:IPV6_UNICAST + route-map: + - bb + openconfig-bgp-evpn-ext:vnis: + vni: + - config: + vni-number: 600 + vni-number: 600 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections" + response: + code: 200 + value: + openconfig-network-instance:table-connections: + table-connection: + - address-family: openconfig-types:IPV6 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:DIRECTLY_CONNECTED + config: + address-family: openconfig-types:IPV6 + metric: 21.0 + import-policy: + - bb + - address-family: openconfig-types:IPV6 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:OSPF + config: + address-family: openconfig-types:IPV6 + metric: 27.0 + import-policy: + - aa + - address-family: openconfig-types:IPV6 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:STATIC + config: + address-family: openconfig-types:IPV6 + metric: 26.0 + import-policy: + - bb + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV4_UNICAST/network-config/network=22.22.22.22%2f16" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV4_UNICAST/route-flap-damping/config/enabled" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV4_UNICAST/use-multiple-paths/ebgp/config/maximum-paths" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-all-vni" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-default-gw" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-pip" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-pip-ip" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-pip-peer-ip" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-svi-ip" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:route-advertise" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:vnis/vni=600" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/network-config/network=22.22.22.22%2f16" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/route-flap-damping/config/enabled" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=openconfig-bgp-types:IPV6_UNICAST" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=openconfig-bgp-types:L2VPN_EVPN" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections/table-connection=openconfig-policy-types:DIRECTLY_CONNECTED,openconfig-policy-types:BGP,openconfig-types:IPV6" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections/table-connection=openconfig-policy-types:OSPF,openconfig-policy-types:BGP,openconfig-types:IPV6" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections/table-connection=openconfig-policy-types:STATIC,openconfig-policy-types:BGP,openconfig-types:IPV6" + method: "delete" + data: + +deleted_03: + module_args: + config: + - bgp_as: 51 + address_family: + afis: + - afi: ipv4 + safi: unicast + - afi: ipv6 + safi: unicast + max_path: + ebgp: 2 + ibgp: 1 + advertise_all_vni: true + redistribute: + - metric: "17" + protocol: connected + route_map: bb + - metric: "18" + protocol: ospf + route_map: aa + - metric: "19" + protocol: static + route_map: aa + - afi: l2vpn + safi: evpn + state: deleted + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 51 + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + route-flap-damping: + config: + enabled: true + use-multiple-paths: + ebgp: + config: + maximum-paths: 2 + ibgp: + config: + maximum-paths: 1 + - afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + use-multiple-paths: + ebgp: + config: + maximum-paths: 2 + ibgp: + config: + maximum-paths: 1 + - afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + config: + afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + route-flap-damping: + config: + enabled: true + l2vpn-evpn: + openconfig-bgp-evpn-ext:config: + advertise-all-vni: true + advertise-default-gw: true + advertise-pip: true + advertise-pip-ip: 3.3.3.3 + advertise-pip-peer-ip: 4.4.4.4 + advertise-svi-ip: true + openconfig-bgp-evpn-ext:route-advertise: + route-advertise-list: + - advertise-afi-safi: openconfig-bgp-types:IPV4_UNICAST + config: + advertise-afi-safi: openconfig-bgp-types:IPV4_UNICAST + route-map: + - aa + - advertise-afi-safi: openconfig-bgp-types:IPV6_UNICAST + config: + advertise-afi-safi: openconfig-bgp-types:IPV6_UNICAST + route-map: + - bb + openconfig-bgp-evpn-ext:vnis: + vni: + - config: + vni-number: 600 + vni-number: 600 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections" + response: + code: 200 + value: + openconfig-network-instance:table-connections: + table-connection: + - address-family: openconfig-types:IPV6 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:DIRECTLY_CONNECTED + config: + address-family: openconfig-types:IPV6 + metric: 21.0 + import-policy: + - bb + - address-family: openconfig-types:IPV6 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:OSPF + config: + address-family: openconfig-types:IPV6 + metric: 27.0 + import-policy: + - aa + - address-family: openconfig-types:IPV6 + dst-protocol: openconfig-policy-types:BGP + src-protocol: openconfig-policy-types:STATIC + config: + address-family: openconfig-types:IPV6 + metric: 26.0 + import-policy: + - bb + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV4_UNICAST/route-flap-damping/config/enabled" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV4_UNICAST/use-multiple-paths/ebgp/config/maximum-paths" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=IPV6_UNICAST/use-multiple-paths/ebgp/config/maximum-paths" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-all-vni" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-default-gw" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-pip" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-pip-ip" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-pip-peer-ip" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:config/advertise-svi-ip" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:route-advertise/route-advertise-list=IPV4_UNICAST/config/route-map=aa" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:route-advertise/route-advertise-list=IPV6_UNICAST/config/route-map=bb" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/l2vpn-evpn/openconfig-bgp-evpn-ext:vnis/vni=600" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=L2VPN_EVPN/route-flap-damping/config/enabled" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/afi-safis/afi-safi=openconfig-bgp-types:L2VPN_EVPN" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections/table-connection=openconfig-policy-types:DIRECTLY_CONNECTED,openconfig-policy-types:BGP,openconfig-types:IPV6" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections/table-connection=openconfig-policy-types:OSPF,openconfig-policy-types:BGP,openconfig-types:IPV6" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/table-connections/table-connection=openconfig-policy-types:STATIC,openconfig-policy-types:BGP,openconfig-types:IPV6" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_as_paths.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_as_paths.yaml new file mode 100644 index 000000000..4f8f14589 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_as_paths.yaml @@ -0,0 +1,494 @@ +--- +merged_01: + module_args: + config: + - name: test + members: + - 909.* + permit: true + - name: test_1 + members: + - 908.* + - name: test_2 + members: + - 907.* + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 4 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + response: + code: 200 + value: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test_2' + config: + as-path-set-name: 'test_2' + as-path-set-member: + - 800.* + openconfig-bgp-policy-ext:action: 'PERMIT' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + method: "patch" + data: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test' + config: + as-path-set-name: 'test' + as-path-set-member: + - 909.* + openconfig-bgp-policy-ext:action: 'PERMIT' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + method: "patch" + data: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test_1' + config: + as-path-set-name: 'test_1' + as-path-set-member: + - 908.* + openconfig-bgp-policy-ext:action: 'DENY' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + method: "patch" + data: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test_2' + config: + as-path-set-name: 'test_2' + as-path-set-member: + - 907.* + openconfig-bgp-policy-ext:action: 'PERMIT' + +deleted_01: + module_args: + config: + - name: test + members: + - 808.* + permit: true + - name: test_1 + members: + - 807.* + permit: true + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + response: + code: 200 + value: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test' + config: + as-path-set-name: 'test' + as-path-set-member: + - 808.* + openconfig-bgp-policy-ext:action: 'PERMIT' + - as-path-set-name: 'test_1' + config: + as-path-set-name: 'test_1' + as-path-set-member: + - 806.* + - 807.* + openconfig-bgp-policy-ext:action: 'PERMIT' + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test_1/config/as-path-set-member=807.%2A" + method: "delete" + +deleted_02: + module_args: + config: + - name: test + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + response: + code: 200 + value: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test' + config: + as-path-set-name: 'test' + as-path-set-member: + - 808.* + openconfig-bgp-policy-ext:action: 'PERMIT' + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test" + method: "delete" + +replaced_01: + module_args: + state: replaced + config: + - name: test + - name: test_1 + members: + - "301.301" + permit: False + - name: test_2 + members: + - "111*" + - "120" + - "^800" + - "25$" + permit: True + - name: test_3 + members: + - "900.*" + - "910.*" + permit: True + - name: test_4 + members: + - "200" + - "210" + - "220" + - name: test_5 + members: + - "300" + - name: test_6 + members: + - "800.*" + permit: True + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 4 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + response: + code: 200 + value: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test' + config: + as-path-set-name: 'test' + as-path-set-member: + - "11" + - "22" + - "44" + openconfig-bgp-policy-ext:action: 'PERMIT' + - as-path-set-name: 'test_1' + config: + as-path-set-name: 'test_1' + as-path-set-member: + - "100.*" + - "200.*" + openconfig-bgp-policy-ext:action: 'DENY' + - as-path-set-name: 'test_2' + config: + as-path-set-name: 'test_2' + as-path-set-member: + - "110" + - "120" + - "^800" + - "25$" + openconfig-bgp-policy-ext:action: 'PERMIT' + - as-path-set-name: 'test_3' + config: + as-path-set-name: 'test_3' + as-path-set-member: + - "900.*" + - "910.*" + openconfig-bgp-policy-ext:action: 'DENY' + - as-path-set-name: 'test_4' + config: + as-path-set-name: 'test_4' + as-path-set-member: + - "200" + - "210" + openconfig-bgp-policy-ext:action: 'DENY' + - as-path-set-name: 'test_5' + config: + as-path-set-name: 'test_5' + as-path-set-member: + - "300" + - "310" + openconfig-bgp-policy-ext:action: 'DENY' + - as-path-set-name: 'test_def' + config: + as-path-set-name: 'test_def' + as-path-set-member: + - "50.*" + - "60.*" + openconfig-bgp-policy-ext:action: 'DENY' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test_1" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test_2/config/as-path-set-member=110" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test_3" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test_5/config/as-path-set-member=310" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + method: "patch" + data: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test_1' + config: + as-path-set-name: 'test_1' + as-path-set-member: + - "301.301" + openconfig-bgp-policy-ext:action: 'DENY' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + method: "patch" + data: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test_2' + config: + as-path-set-name: 'test_2' + as-path-set-member: + - "111*" + openconfig-bgp-policy-ext:action: 'PERMIT' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + method: "patch" + data: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test_3' + config: + as-path-set-name: 'test_3' + as-path-set-member: + - "900.*" + - "910.*" + openconfig-bgp-policy-ext:action: 'PERMIT' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + method: "patch" + data: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test_4' + config: + as-path-set-name: 'test_4' + as-path-set-member: + - "220" + openconfig-bgp-policy-ext:action: 'DENY' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + method: "patch" + data: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test_6' + config: + as-path-set-name: 'test_6' + as-path-set-member: + - "800.*" + openconfig-bgp-policy-ext:action: 'PERMIT' + +overridden_01: + module_args: + state: overridden + config: + - name: test + - name: test_1 + members: + - "301.301" + permit: False + - name: test_2 + members: + - "111*" + - "120" + - "^800" + - "25$" + permit: True + - name: test_3 + members: + - "900.*" + - "910.*" + permit: True + - name: test_4 + members: + - "200" + - "210" + - "220" + - name: test_5 + members: + - "300" + - name: test_6 + members: + - "800.*" + permit: True + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 4 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + response: + code: 200 + value: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test' + config: + as-path-set-name: 'test' + as-path-set-member: + - "11" + - "22" + - "44" + openconfig-bgp-policy-ext:action: 'PERMIT' + - as-path-set-name: 'test_1' + config: + as-path-set-name: 'test_1' + as-path-set-member: + - "100.*" + - "200.*" + openconfig-bgp-policy-ext:action: 'DENY' + - as-path-set-name: 'test_2' + config: + as-path-set-name: 'test_2' + as-path-set-member: + - "110" + - "120" + - "^800" + - "25$" + openconfig-bgp-policy-ext:action: 'PERMIT' + - as-path-set-name: 'test_3' + config: + as-path-set-name: 'test_3' + as-path-set-member: + - "900.*" + - "910.*" + openconfig-bgp-policy-ext:action: 'DENY' + - as-path-set-name: 'test_4' + config: + as-path-set-name: 'test_4' + as-path-set-member: + - "200" + - "210" + openconfig-bgp-policy-ext:action: 'DENY' + - as-path-set-name: 'test_5' + config: + as-path-set-name: 'test_5' + as-path-set-member: + - "300" + - "310" + openconfig-bgp-policy-ext:action: 'DENY' + - as-path-set-name: 'test_def' + config: + as-path-set-name: 'test_def' + as-path-set-member: + - "50.*" + - "60.*" + openconfig-bgp-policy-ext:action: 'DENY' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test_1" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test_2/config/as-path-set-member=110" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test_3" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test_5/config/as-path-set-member=310" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=test_def" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + method: "patch" + data: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test_1' + config: + as-path-set-name: 'test_1' + as-path-set-member: + - "301.301" + openconfig-bgp-policy-ext:action: 'DENY' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + method: "patch" + data: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test_2' + config: + as-path-set-name: 'test_2' + as-path-set-member: + - "111*" + openconfig-bgp-policy-ext:action: 'PERMIT' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + method: "patch" + data: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test_3' + config: + as-path-set-name: 'test_3' + as-path-set-member: + - "900.*" + - "910.*" + openconfig-bgp-policy-ext:action: 'PERMIT' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + method: "patch" + data: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test_4' + config: + as-path-set-name: 'test_4' + as-path-set-member: + - "220" + openconfig-bgp-policy-ext:action: 'DENY' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets" + method: "patch" + data: + openconfig-bgp-policy:as-path-sets: + as-path-set: + - as-path-set-name: 'test_6' + config: + as-path-set-name: 'test_6' + as-path-set-member: + - "800.*" + openconfig-bgp-policy-ext:action: 'PERMIT' diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_communities.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_communities.yaml new file mode 100644 index 000000000..5caca21b4 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_communities.yaml @@ -0,0 +1,390 @@ +--- +merged_01: + module_args: + config: + - name: test + members: + regex: + - 808.* + match: ALL + permit: True + type: expanded + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" + response: + code: 200 + value: + openconfig-network-instance:global: + config: + as: 5 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test2' + config: + community-set-name: 'test2' + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + method: "patch" + data: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - REGEX:808.* + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' +merged_02: + module_args: + config: + - name: test + type: standard + permit: True + local_as: False + no_export: True + no_peer: True + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - NO_ADVERTISE + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + method: "patch" + data: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - NO_ADVERTISE + - NOPEER + - NO_EXPORT + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' +deleted_01: + module_args: + config: + - name: test + type: expanded + members: + regex: + - 808.* + permit: true + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - REGEX:808.* + - REGEX:919.* + - REGEX:930.* + - REGEX:772.* + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test/config/community-member=REGEX%3A808.%2A" + method: "delete" +deleted_02: + module_args: + config: + - name: test + type: expanded + match: ALL + permit: True + members: + regex: + - name: test2 + type: standard + match: ANY + permit: False + local_as: True + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - REGEX:808.* + - REGEX:919.* + - REGEX:700.* + - REGEX:888.* + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - community-set-name: 'test2' + config: + community-set-name: 'test2' + community-member: + - NO_ADVERTISE + - NOPEER + - NO_EXPORT_SUBCONFED + - NO_EXPORT + openconfig-bgp-policy-ext:action: 'DENY' + match-set-options: 'ANY' + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test2/config/community-member=NO_EXPORT_SUBCONFED" + method: "delete" +deleted_03: + module_args: + config: + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - NOPEER + - NO_EXPORT + - NO_ADVERTISE + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + method: "delete" + +replaced_01: + module_args: + config: + - name: test + members: + regex: + - 808.* + match: ALL + permit: True + type: expanded + state: replaced + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - NO_ADVERTISE + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' + - community-set-name: 'test2' + config: + community-set-name: 'test2' + community-member: + - REGEX:808.* + - REGEX:919.* + - REGEX:700.* + - REGEX:888.* + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + method: "patch" + data: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - REGEX:808.* + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + +replaced_02: + module_args: + config: + - name: test2 + members: + regex: + - 808.* + match: ALL + permit: False + type: expanded + state: replaced + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - NO_ADVERTISE + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' + - community-set-name: 'test2' + config: + community-set-name: 'test2' + community-member: + - REGEX:808.* + - REGEX:919.* + - REGEX:700.* + - REGEX:888.* + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test2" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + method: "patch" + data: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test2' + config: + community-set-name: 'test2' + community-member: + - REGEX:808.* + openconfig-bgp-policy-ext:action: 'DENY' + match-set-options: 'ALL' + +overridden_01: + module_args: + config: + - name: test + type: standard + permit: True + local_as: True + no_export: True + no_peer: True + - name: test1 + no_advertise: True + permit: true + type: standard + state: overridden + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test1' + config: + community-set-name: 'test1' + community-member: + - NO_ADVERTISE + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' + - community-set-name: 'test3' + config: + community-set-name: 'test3' + community-member: + - NO_ADVERTISE + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - REGEX:808.* + - REGEX:919.* + - REGEX:700.* + - REGEX:888.* + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test3" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + method: "patch" + data: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - NO_EXPORT_SUBCONFED + - NO_EXPORT + - NOPEER + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_ext_communities.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_ext_communities.yaml new file mode 100644 index 000000000..d860c4900 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_ext_communities.yaml @@ -0,0 +1,499 @@ +--- +merged_01: + module_args: + config: + - name: test1 + members: + regex: + - 808 + match: all + permit: True + type: expanded + state: merged + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "patch" + data: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test1' + config: + ext-community-set-name: 'test1' + ext-community-member: + - REGEX:808 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' +merged_02: + module_args: + config: + - name: test1 + members: + route_origin: + - "500:500" + type: standard + permit: true + match: all + - name: test_ext + members: + regex: + - 800 + match: any + permit: True + type: expanded + state: merged + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test1' + config: + ext-community-set-name: 'test1' + ext-community-member: + - "route-origin:200:200" + - "route-target:400:400" + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - ext-community-set-name: 'test_ext' + config: + ext-community-set-name: 'test_ext' + ext-community-member: + - "REGEX:808" + - "REGEX:608" + - "REGEX:908" + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "patch" + data: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test1' + config: + ext-community-set-name: 'test1' + ext-community-member: + - route-origin:500:500 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "patch" + data: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test_ext' + config: + ext-community-set-name: 'test_ext' + ext-community-member: + - REGEX:800 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' +merged_03: + module_args: + config: + - name: test1 + members: + route_target: + - "2.2.2.2:201" + match: all + permit: True + type: standard + state: merged + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "patch" + data: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test1' + config: + ext-community-set-name: 'test1' + ext-community-member: + - route-target:2.2.2.2:201 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' +deleted_01: + module_args: + config: + - name: test1 + type: expanded + match: all + members: + regex: + - 808 + permit: true + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test1' + config: + ext-community-set-name: 'test1' + ext-community-member: + - REGEX:808 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test1" + method: "delete" +deleted_02: + module_args: + config: + - name: test2 + type: expanded + members: + regex: + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test2' + config: + ext-community-set-name: 'test2' + ext-community-member: + - REGEX:808 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test2" + method: "delete" +deleted_03: + module_args: + config: + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test3' + config: + ext-community-set-name: 'test3' + ext-community-member: + - REGEX:710 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "delete" +deleted_04: + module_args: + config: + - name: test4 + members: + route_origin: + - 600:600 + permit: true + match: all + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test4' + config: + ext-community-set-name: 'test4' + ext-community-member: + - route-origin:600:600 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test4" + method: "delete" +deleted_05: + module_args: + config: + - name: test5 + members: + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test5' + config: + ext-community-set-name: 'test5' + ext-community-member: + - route-origin:4403:301 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test5" + method: "delete" +deleted_06: + module_args: + config: + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test6' + config: + ext-community-set-name: 'test6' + ext-community-member: + - route-origin:3303:201 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "delete" +deleted_07: + module_args: + config: + - name: test7 + members: + route_target: + - 1.1.1.1:33 + permit: true + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test7' + config: + ext-community-set-name: 'test7' + ext-community-member: + - route-target:1.1.1.1:33 + - route-target:2.2.2.2:33 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test7/config/ext-community-member=route-target%3A1.1.1.1%3A33" + method: "delete" +deleted_08: + module_args: + config: + - name: test8 + members: + route_target: + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test8' + config: + ext-community-set-name: 'test8' + ext-community-member: + - route-target:2.2.2.2:33 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test8" + method: "delete" +deleted_09: + module_args: + config: + state: deleted + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test9' + config: + ext-community-set-name: 'test9' + ext-community-member: + - route-target:30.30.30.1:12 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "delete" + +replaced_01: + module_args: + config: + - name: replace_test1 + members: + regex: + - 919 + match: all + permit: True + type: expanded + - name: replace_test3 + members: + route_origin: + - "101:101" + match: any + permit: False + type: standard + state: replaced + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'replace_test2' + config: + ext-community-set-name: 'replace_test2' + ext-community-member: + - REGEX:808 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - ext-community-set-name: 'replace_test1' + config: + ext-community-set-name: 'replace_test1' + ext-community-member: + - route-target:120.1.1.1:32 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - ext-community-set-name: 'replace_test3' + config: + ext-community-set-name: 'replace_test3' + ext-community-member: + - route-target:808:808 + - route-origin:101:101 + - route-origin:201:201 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=replace_test1" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=replace_test3" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "patch" + data: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'replace_test1' + config: + ext-community-set-name: 'replace_test1' + ext-community-member: + - REGEX:919 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "patch" + data: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'replace_test3' + config: + ext-community-set-name: 'replace_test3' + ext-community-member: + - route-origin:101:101 + openconfig-bgp-policy-ext:action: 'DENY' + match-set-options: 'ANY' + +overridden_01: + module_args: + config: + - name: test1 + members: + regex: + - 919 + match: all + permit: True + type: expanded + - name: test4 + members: + route_origin: + - "101:101" + match: any + permit: False + type: standard + state: overridden + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test2' + config: + ext-community-set-name: 'test2' + ext-community-member: + - REGEX:808 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - ext-community-set-name: 'test1' + config: + ext-community-set-name: 'test1' + ext-community-member: + - route-target:2.2.2.2:11 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - ext-community-set-name: 'test3' + config: + ext-community-set-name: 'test3' + ext-community-member: + - route-target:808:301 + - route-origin:101:101 + - route-origin:201:201 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test1" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test2" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test3" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "patch" + data: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test1' + config: + ext-community-set-name: 'test1' + ext-community-member: + - REGEX:919 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "patch" + data: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test4' + config: + ext-community-set-name: 'test4' + ext-community-member: + - route-origin:101:101 + openconfig-bgp-policy-ext:action: 'DENY' + match-set-options: 'ANY' diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_neighbors.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_neighbors.yaml new file mode 100644 index 000000000..0ba93b7e0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_neighbors.yaml @@ -0,0 +1,1114 @@ +--- +merged_01: + module_args: + config: + - bgp_as: 51 + neighbors: + - neighbor: Eth1/2 + auth_pwd: + pwd: 'pw123' + encrypted: false + dont_negotiate_capability: true + ebgp_multihop: + enabled: true + multihop_ttl: 1 + enforce_first_as: true + enforce_multihop: true + local_address: 'Ethernet4' + local_as: + as: 2 + no_prepend: true + replace_as: true + nbr_description: "description 1" + override_capability: true + passive: true + port: 3 + shutdown_msg: 'msg1' + solo: true + - neighbor: 1.1.1.1 + disable_connected_check: true + ttl_security: 5 + - bgp_as: 51 + vrf_name: VrfReg1 + peer_group: + - name: SPINE + bfd: + check_failure: true + enabled: true + profile: 'profile 1' + capability: + dynamic: true + extended_nexthop: true + auth_pwd: + pwd: 'U2FsdGVkX1/4sRsZ624wbAJfDmagPLq2LsGDOcW/47M=' + encrypted: true + dont_negotiate_capability: true + ebgp_multihop: + enabled: true + multihop_ttl: 1 + enforce_first_as: true + enforce_multihop: true + local_address: 'Ethernet4' + local_as: + as: 2 + no_prepend: true + replace_as: true + pg_description: 'description 1' + override_capability: true + passive: true + solo: true + remote_as: + peer_as: 4 + - name: SPINE1 + disable_connected_check: true + shutdown_msg: "msg1" + strict_capability_match: true + timers: + keepalive: 30 + holdtime: 15 + connect_retry: 25 + ttl_security: 5 + address_family: + afis: + - afi: ipv4 + safi: unicast + activate: true + allowas_in: + origin: true + - afi: ipv6 + safi: unicast + activate: true + allowas_in: + value: 5 + neighbors: + - neighbor: Eth1/3 + remote_as: + peer_type: internal + peer_group: SPINE + advertisement_interval: 15 + timers: + keepalive: 30 + holdtime: 15 + connect_retry: 25 + bfd: + check_failure: true + enabled: true + profile: 'profile 1' + capability: + dynamic: true + extended_nexthop: true + auth_pwd: + pwd: 'U2FsdGVkX199MZ7YOPkOR9O6wEZmtGSgiDfnlcN9hBg=' + encrypted: true + nbr_description: 'description 2' + strict_capability_match: true + v6only: true + - neighbor: 192.168.1.4 + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/peer-groups" + response: + code: 200 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups" + response: + code: 200 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors" + method: "patch" + data: + openconfig-network-instance:neighbors: + neighbor: + - auth-password: + config: + password: U2FsdGVkX199MZ7YOPkOR9O6wEZmtGSgiDfnlcN9hBg= + encrypted: True + neighbor-address: Eth1/3 + enable-bfd: + config: + enabled: True + check-control-plane-failure: True + bfd-profile: 'profile 1' + timers: + config: + hold-time: 15 + keepalive-interval: 30 + connect-retry: 25 + minimum-advertisement-interval: 15 + transport: + config: + passive-mode: False + config: + neighbor-address: Eth1/3 + peer-group: SPINE + description: 'description 2' + strict-capability-match: True + openconfig-bgp-ext:v6only: True + capability-dynamic: True + capability-extended-nexthop: True + peer-type: INTERNAL + - neighbor-address: 192.168.1.4 + transport: + config: + passive-mode: False + config: + neighbor-address: 192.168.1.4 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups" + method: "patch" + data: + openconfig-network-instance:peer-groups: + peer-group: + - peer-group-name: SPINE + auth-password: + config: + password: U2FsdGVkX1/4sRsZ624wbAJfDmagPLq2LsGDOcW/47M= + encrypted: True + enable-bfd: + config: + enabled: True + check-control-plane-failure: True + bfd-profile: 'profile 1' + ebgp-multihop: + config: + enabled: True + multihop-ttl: 1 + transport: + config: + local-address: Ethernet4 + passive-mode: True + config: + peer-group-name: SPINE + description: 'description 1' + dont-negotiate-capability: True + enforce-first-as: True + enforce-multihop: True + override-capability: True + solo-peer: True + local-as: 2 + local-as-no-prepend: True + local-as-replace-as: True + capability-dynamic: True + capability-extended-nexthop: True + peer-as: 4 + - peer-group-name: SPINE1 + timers: + config: + hold-time: 15 + keepalive-interval: 30 + connect-retry: 25 + transport: + config: + passive-mode: False + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + config: + enabled: True + allow-own-as: + config: + origin: True + enabled: True + - afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + config: + enabled: True + allow-own-as: + config: + as-count: 5 + enabled: True + config: + peer-group-name: SPINE1 + disable-ebgp-connected-route-check: True + shutdown-message: msg1 + strict-capability-match: True + ttl-security-hops: 5 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors" + method: "patch" + data: + openconfig-network-instance:neighbors: + neighbor: + - auth-password: + config: + password: pw123 + encrypted: False + neighbor-address: Eth1/2 + ebgp-multihop: + config: + enabled: True + multihop-ttl: 1 + transport: + config: + local-address: Ethernet4 + passive-mode: True + config: + neighbor-address: Eth1/2 + description: 'description 1' + dont-negotiate-capability: True + enforce-first-as: True + enforce-multihop: True + override-capability: True + peer-port: 3 + shutdown-message: msg1 + solo-peer: True + local-as: 2 + local-as-no-prepend: True + local-as-replace-as: True + - neighbor-address: 1.1.1.1 + transport: + config: + passive-mode: False + config: + neighbor-address: 1.1.1.1 + disable-ebgp-connected-route-check: True + ttl-security-hops: 5 + +merged_02: + module_args: + config: + - bgp_as: 51 + neighbors: + - neighbor: Eth1/2 + auth_pwd: + pwd: 'pw123' + encrypted: false + dont_negotiate_capability: true + ebgp_multihop: + enabled: true + multihop_ttl: 1 + enforce_first_as: true + enforce_multihop: true + local_address: 'Ethernet4' + local_as: + as: 2 + no_prepend: true + replace_as: true + nbr_description: "description 1" + override_capability: true + passive: true + port: 3 + shutdown_msg: 'msg1' + solo: true + - neighbor: 1.1.1.1 + disable_connected_check: true + ttl_security: 5 + - bgp_as: 51 + vrf_name: VrfReg1 + peer_group: + - name: SPINE + bfd: + check_failure: true + enabled: true + profile: 'profile 1' + capability: + dynamic: true + extended_nexthop: true + auth_pwd: + pwd: 'U2FsdGVkX1/4sRsZ624wbAJfDmagPLq2LsGDOcW/47M=' + encrypted: true + dont_negotiate_capability: true + ebgp_multihop: + enabled: true + multihop_ttl: 1 + enforce_first_as: true + enforce_multihop: true + local_address: 'Ethernet4' + local_as: + as: 2 + no_prepend: true + replace_as: true + pg_description: 'description 1' + override_capability: true + passive: true + solo: true + remote_as: + peer_as: 4 + - name: SPINE1 + disable_connected_check: true + shutdown_msg: "msg1" + strict_capability_match: true + timers: + keepalive: 30 + holdtime: 15 + connect_retry: 25 + ttl_security: 5 + address_family: + afis: + - afi: ipv4 + safi: unicast + activate: true + allowas_in: + origin: true + - afi: ipv6 + safi: unicast + activate: true + allowas_in: + value: 5 + - name: SPINE5 + remote_as: + peer_type: internal + neighbors: + - neighbor: Eth1/3 + remote_as: + peer_type: internal + peer_group: SPINE + advertisement_interval: 15 + timers: + keepalive: 30 + holdtime: 15 + connect_retry: 25 + bfd: + check_failure: true + enabled: true + profile: 'profile 1' + capability: + dynamic: true + extended_nexthop: true + auth_pwd: + pwd: 'U2FsdGVkX199MZ7YOPkOR9O6wEZmtGSgiDfnlcN9hBg=' + encrypted: true + nbr_description: 'description 2' + strict_capability_match: true + v6only: true + - neighbor: Eth1/4 + remote_as: + peer_as: 700 + peer_group: SPINE + - neighbor: 192.168.1.4 + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/peer-groups" + response: + code: 200 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + value: + openconfig-network-instance:neighbors: + neighbor: + - neighbor-address: Eth1/3 + config: + neighbor-address: Eth1/3 + peer-group: SPINE + local-as: 51 + peer-as: 65399 + - neighbor-address: Eth1/4 + config: + neighbor-address: Eth1/4 + peer-group: SPINE + local-as: 51 + peer-type: INTERNAL + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups" + response: + code: 200 + value: + openconfig-network-instance:peer-groups: + peer-group: + - config: + peer-group-name: SPINE + peer-type: INTERNAL + timers: + config: + connect-retry: 30 + minimum-advertisement-interval: 0 + - config: + peer-group-name: SPINE5 + peer-as: 55 + timers: + config: + connect-retry: 40 + minimum-advertisement-interval: 50 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors" + method: "patch" + data: + openconfig-network-instance:neighbors: + neighbor: + - auth-password: + config: + password: U2FsdGVkX199MZ7YOPkOR9O6wEZmtGSgiDfnlcN9hBg= + encrypted: True + neighbor-address: Eth1/3 + enable-bfd: + config: + enabled: True + check-control-plane-failure: True + bfd-profile: 'profile 1' + timers: + config: + hold-time: 15 + keepalive-interval: 30 + connect-retry: 25 + minimum-advertisement-interval: 15 + config: + neighbor-address: Eth1/3 + description: 'description 2' + strict-capability-match: True + openconfig-bgp-ext:v6only: True + capability-dynamic: True + capability-extended-nexthop: True + peer-type: INTERNAL + - neighbor-address: Eth1/4 + config: + neighbor-address: Eth1/4 + peer-as: 700 + - neighbor-address: 192.168.1.4 + transport: + config: + passive-mode: False + config: + neighbor-address: 192.168.1.4 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f3/config/peer-as" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f4/config/peer-type" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups" + method: "patch" + data: + openconfig-network-instance:peer-groups: + peer-group: + - peer-group-name: SPINE + auth-password: + config: + password: U2FsdGVkX1/4sRsZ624wbAJfDmagPLq2LsGDOcW/47M= + encrypted: True + enable-bfd: + config: + enabled: True + check-control-plane-failure: True + bfd-profile: 'profile 1' + ebgp-multihop: + config: + enabled: True + multihop-ttl: 1 + transport: + config: + local-address: Ethernet4 + passive-mode: True + config: + peer-group-name: SPINE + description: 'description 1' + dont-negotiate-capability: True + enforce-first-as: True + enforce-multihop: True + override-capability: True + solo-peer: True + local-as: 2 + local-as-no-prepend: True + local-as-replace-as: True + capability-dynamic: True + capability-extended-nexthop: True + peer-as: 4 + - peer-group-name: SPINE1 + timers: + config: + hold-time: 15 + keepalive-interval: 30 + connect-retry: 25 + transport: + config: + passive-mode: False + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + config: + enabled: True + allow-own-as: + config: + origin: True + enabled: True + - afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + config: + enabled: True + allow-own-as: + config: + as-count: 5 + enabled: True + config: + peer-group-name: SPINE1 + disable-ebgp-connected-route-check: True + shutdown-message: msg1 + strict-capability-match: True + ttl-security-hops: 5 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINE/config/peer-type" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINE5/config/peer-as" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors" + method: "patch" + data: + openconfig-network-instance:neighbors: + neighbor: + - auth-password: + config: + password: pw123 + encrypted: False + neighbor-address: Eth1/2 + ebgp-multihop: + config: + enabled: True + multihop-ttl: 1 + transport: + config: + local-address: Ethernet4 + passive-mode: True + config: + neighbor-address: Eth1/2 + description: 'description 1' + dont-negotiate-capability: True + enforce-first-as: True + enforce-multihop: True + override-capability: True + peer-port: 3 + shutdown-message: msg1 + solo-peer: True + local-as: 2 + local-as-no-prepend: True + local-as-replace-as: True + - neighbor-address: 1.1.1.1 + transport: + config: + passive-mode: False + config: + neighbor-address: 1.1.1.1 + disable-ebgp-connected-route-check: True + ttl-security-hops: 5 + +deleted_01: + module_args: + config: + state: deleted + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + router-id: "10.2.2.4" + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + router-id: "10.2.2.4" + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + value: + openconfig-network-instance:neighbors: + neighbor: + - auth-password: + config: + password: U2FsdGVkX199MZ7YOPkOR9O6wEZmtGSgiDfnlcN9hBg= + encrypted: True + neighbor-address: Eth1/3 + enable-bfd: + config: + enabled: True + check-control-plane-failure: True + bfd-profile: 'profile 1' + timers: + config: + hold-time: 15 + keepalive-interval: 30 + connect-retry: 25 + minimum-advertisement-interval: 15 + transport: + config: + passive-mode: False + config: + neighbor-address: Eth1/3 + peer-group: SPINE + description: 'description 2' + strict-capability-match: True + openconfig-bgp-ext:v6only: True + capability-dynamic: True + capability-extended-nexthop: True + peer-as: 10 + - neighbor-address: 192.168.1.4 + transport: + config: + passive-mode: False + config: + neighbor-address: 192.168.1.4 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + value: + openconfig-network-instance:neighbors: + neighbor: + - auth-password: + config: + password: pw123 + encrypted: False + neighbor-address: Eth1/2 + ebgp-multihop: + config: + enabled: True + multihop-ttl: 1 + transport: + config: + local-address: Ethernet4 + passive-mode: True + config: + neighbor-address: Eth1/2 + description: 'description 1' + dont-negotiate-capability: True + enforce-first-as: True + enforce-multihop: True + override-capability: True + peer-port: 3 + shutdown-message: msg1 + solo-peer: True + local-as: 2 + local-as-no-prepend: True + local-as-replace-as: True + - neighbor-address: 1.1.1.1 + transport: + config: + passive-mode: False + config: + neighbor-address: 1.1.1.1 + disable-ebgp-connected-route-check: True + ttl-security-hops: 5 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/peer-groups" + response: + code: 200 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups" + response: + code: 200 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=192.168.1.4/" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f3/" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=1.1.1.1/" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/" + method: "delete" + data: + +deleted_02: + module_args: + config: + - bgp_as: 51 + vrf_name: VrfReg1 + neighbors: + - neighbor: 192.168.1.4 + - neighbor: Eth1/3 + peer_group: + - name: SPINETEST1 + capability: + dynamic: true + extended_nexthop: true + remote_as: + peer_as: 65399 + advertisement_interval: 15 + timers: + keepalive: 77 + holdtime: 78 + connect_retry: 11 + bfd: + enabled: true + check_failure: true + profile: 'kvsk_bfd_profile' + ebgp_multihop: + enabled: true + multihop_ttl: 22 + auth_pwd: + encrypted: true + pwd: 'U2FsdGVkX1+LHXncDf0uAxQrs4CN7H5yDKT5sht6Ga4=' + enforce_first_as: true + enforce_multihop: true + pg_description: 'pg_kvsk_description' + disable_connected_check: true + dont_negotiate_capability: true + local_as: + as: 65299 + no_prepend: true + replace_as: true + override_capability: true + shutdown_msg: pg_kvsk_shutdown_msg + passive: true + local_address: 5.5.5.5 + solo: true + address_family: + afis: + - afi: ipv4 + safi: unicast + activate: true + allowas_in: + value: 8 + - afi: ipv6 + safi: unicast + activate: true + allowas_in: + origin: true + prefix_limit: + max_prefixes: 20 + prevent_teardown: true + warning_threshold: 40 + restart_timer: 60 + - afi: l2vpn + safi: evpn + prefix_list_in: p1 + prefix_list_out: p2 + - bgp_as: 51 + neighbors: + - neighbor: 1.1.1.1 + - neighbor: Eth1/2 + state: deleted + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + router-id: "10.2.2.4" + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + router-id: "10.2.2.4" + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups" + response: + code: 200 + value: + openconfig-network-instance:peer-groups: + peer-group: + - config: + capability-dynamic: true + capability-extended-nexthop: true + enabled: true + peer-group-name: SPINETEST1 + description: "pg_kvsk_description" + disable-ebgp-connected-route-check: true + dont-negotiate-capability: true + enforce-first-as: true + enforce-multihop: true + local-as: 65299 + shutdown-message: pg_kvsk_shutdown_msg + local-as-no-prepend: true + local-as-replace-as: true + override-capability: true + peer-as: 65399 + solo-peer: true + peer-group-name: SPINETEST1 + enable-bfd: + config: + enabled: true + check-control-plane-failure: true + bfd-profile: 'kvsk_bfd_profile' + ebgp-multihop: + config: + enabled: true + multihop-ttl: 22 + auth-password: + config: + encrypted: true + password: 'U2FsdGVkX1+LHXncDf0uAxQrs4CN7H5yDKT5sht6Ga4=' + advertisement-interval: 15 + timers: + config: + keepalive-interval: 77 + hold-time: 78 + connect-retry: 11 + transport: + config: + passive-mode: true + local-address: 5.5.5.5 + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + allow-own-as: + config: + as-count: 8 + config: + afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + - afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + ipv6-unicast: + prefix-limit: + config: + max-prefixes: 20 + prevent-teardown: true + warning-threshold-pct: 40 + restart-time: 60 + allow-own-as: + config: + origin: True + config: + afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + enabled: true + - afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + config: + afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + prefix-list: + config: + import-policy: p1 + export-policy: p2 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + value: + openconfig-network-instance:neighbors: + neighbor: + - auth-password: + config: + password: U2FsdGVkX199MZ7YOPkOR9O6wEZmtGSgiDfnlcN9hBg= + encrypted: True + neighbor-address: Eth1/3 + enable-bfd: + config: + enabled: True + check-control-plane-failure: True + bfd-profile: 'profile 1' + timers: + config: + hold-time: 15 + keepalive-interval: 30 + connect-retry: 25 + minimum-advertisement-interval: 15 + transport: + config: + passive-mode: False + config: + neighbor-address: Eth1/3 + peer-group: SPINETEST1 + description: 'description 2' + strict-capability-match: True + openconfig-bgp-ext:v6only: True + capability-dynamic: True + capability-extended-nexthop: True + peer-as: 10 + - neighbor-address: 192.168.1.4 + config: + neighbor-address: 192.168.1.4 + capability-extended-nexthop: True + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + value: + openconfig-network-instance:neighbors: + neighbor: + - auth-password: + config: + password: pw123 + encrypted: False + neighbor-address: Eth1/2 + ebgp-multihop: + config: + enabled: True + multihop-ttl: 1 + transport: + config: + local-address: Ethernet4 + passive-mode: False + config: + neighbor-address: Eth1/2 + description: 'description 1' + dont-negotiate-capability: True + enforce-first-as: True + enforce-multihop: True + override-capability: True + peer-port: 3 + shutdown-message: msg1 + solo-peer: True + local-as: 2 + local-as-no-prepend: True + local-as-replace-as: True + capability-extended-nexthop: True + - neighbor-address: 1.1.1.1 + transport: + config: + passive-mode: False + config: + neighbor-address: 1.1.1.1 + disable-ebgp-connected-route-check: True + ttl-security-hops: 5 + capability-extended-nexthop: True + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/peer-groups" + response: + code: 200 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=192.168.1.4/" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f3/" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST/allow-own-as/config/as-count" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/afi-safis/afi-safi=openconfig-bgp-types:IPV6_UNICAST/allow-own-as/config/origin" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/afi-safis/afi-safi=openconfig-bgp-types:IPV6_UNICAST/config/enabled" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/afi-safis/afi-safi=openconfig-bgp-types:IPV6_UNICAST/ipv6-unicast/prefix-limit/config/max-prefixes" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/afi-safis/afi-safi=openconfig-bgp-types:IPV6_UNICAST/ipv6-unicast/prefix-limit/config/prevent-teardown" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/afi-safis/afi-safi=openconfig-bgp-types:IPV6_UNICAST/ipv6-unicast/prefix-limit/config/warning-threshold-pct" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/afi-safis/afi-safi=openconfig-bgp-types:L2VPN_EVPN/prefix-list/config/export-policy" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/afi-safis/afi-safi=openconfig-bgp-types:L2VPN_EVPN/prefix-list/config/import-policy" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/auth-password/config/encrypted" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/auth-password/config/password" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/capability-dynamic" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/capability-extended-nexthop" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/description" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/disable-ebgp-connected-route-check" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/dont-negotiate-capability" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/enforce-first-as" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/enforce-multihop" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/local-as" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/local-as-no-prepend" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/local-as-replace-as" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/override-capability" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/peer-as" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/shutdown-message" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/config/solo-peer" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/ebgp-multihop/config/enabled" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/ebgp-multihop/config/multihop-ttl" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/enable-bfd/config/bfd-profile" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/enable-bfd/config/check-control-plane-failure" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/enable-bfd/config/enabled" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/timers/config/connect-retry" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/timers/config/hold-time" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/timers/config/keepalive-interval" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/transport/config/local-address" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups/peer-group=SPINETEST1/transport/config/passive-mode" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=1.1.1.1/" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_neighbors_af.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_neighbors_af.yaml new file mode 100644 index 000000000..2b2dd7be4 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_bgp_neighbors_af.yaml @@ -0,0 +1,471 @@ +--- +merged_01: + module_args: + config: + - bgp_as: 51 + neighbors: + - neighbor: Eth1/2 + address_family: + - afi: ipv4 + safi: unicast + allowas_in: + origin: true + ip_afi: + default_policy_name: rmap_reg1 + send_default_route: true + prefix_limit: + max_prefixes: 1 + prevent_teardown: true + warning_threshold: 99 + restart_timer: 88 + prefix_list_in: p1 + prefix_list_out: p2 + route_map: + - direction: in + name: neigh_af_rmap1 + - direction: out + name: neigh_af_rmap2 + route_reflector_client: true + route_server_client: true + - bgp_as: 51 + vrf_name: VrfReg1 + neighbors: + - neighbor: 1.1.1.1 + address_family: + - afi: ipv6 + safi: unicast + allowas_in: + value: 55 + ip_afi: + default_policy_name: rmap_reg2 + send_default_route: true + prefix_limit: + max_prefixes: 1 + prevent_teardown: true + warning_threshold: 44 + prefix_list_in: p3 + prefix_list_out: p4 + - neighbor: 2.2.2.2 + address_family: + - afi: l2vpn + safi: evpn + allowas_in: + value: 22 + prefix_list_in: p5 + prefix_list_out: p6 + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/peer-groups" + response: + code: 200 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/peer-groups" + response: + code: 200 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=1.1.1.1/afi-safis" + method: "patch" + data: + openconfig-network-instance:afi-safis: + afi-safi: + - afi-safi-name: IPV6_UNICAST + config: + afi-safi-name: IPV6_UNICAST + prefix-list: + config: + import-policy: p3 + export-policy: p4 + ipv6-unicast: + config: + default-policy-name: rmap_reg2 + send-default-route: True + prefix-limit: + config: + max-prefixes: 1 + prevent-teardown: True + warning-threshold-pct: 44 + allow-own-as: + config: + as-count: 55 + enabled: true + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=2.2.2.2/afi-safis" + method: "patch" + data: + openconfig-network-instance:afi-safis: + afi-safi: + - afi-safi-name: L2VPN_EVPN + config: + afi-safi-name: L2VPN_EVPN + prefix-list: + config: + import-policy: p5 + export-policy: p6 + allow-own-as: + config: + as-count: 22 + enabled: true + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis" + method: "patch" + data: + openconfig-network-instance:afi-safis: + afi-safi: + - afi-safi-name: IPV4_UNICAST + config: + afi-safi-name: IPV4_UNICAST + route-reflector-client: true + route-server-client: true + apply-policy: + config: + import-policy: + - neigh_af_rmap1 + export-policy: + - neigh_af_rmap2 + prefix-list: + config: + import-policy: p1 + export-policy: p2 + ipv4-unicast: + config: + default-policy-name: rmap_reg1 + send-default-route: True + prefix-limit: + config: + max-prefixes: 1 + prevent-teardown: True + warning-threshold-pct: 99 + restart-timer: 88 + allow-own-as: + config: + origin: true + enabled: true +deleted_01: + module_args: + config: + state: deleted + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + router-id: "10.2.2.4" + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + router-id: "10.2.2.4" + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + value: + openconfig-network-instance:neighbors: + neighbor: + - neighbor-address: 1.1.1.1 + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + - afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + - afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + config: + afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=1.1.1.1/afi-safis/afi-safi=openconfig-bgp-types:IPV6_UNICAST" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=1.1.1.1/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=1.1.1.1/afi-safis/afi-safi=openconfig-bgp-types:L2VPN_EVPN" + method: "delete" + data: +deleted_02: + module_args: + config: + - bgp_as: 51 + neighbors: + - neighbor: Eth1/2 + address_family: + - afi: ipv4 + safi: unicast + allowas_in: + origin: true + ip_afi: + default_policy_name: rmap_reg1 + send_default_route: true + prefix_limit: + max_prefixes: 1 + prevent_teardown: true + warning_threshold: 99 + restart_timer: 88 + prefix_list_in: p1 + prefix_list_out: p2 + route_map: + - direction: in + name: neigh_af_rmap1 + - direction: out + name: neigh_af_rmap2 + route_reflector_client: true + route_server_client: true + state: deleted + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + router-id: "10.2.2.4" + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + router-id: "10.2.2.4" + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + value: + openconfig-network-instance:neighbors: + neighbor: + - neighbor-address: Eth1/2 + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + route-reflector-client: true + route-server-client: true + apply-policy: + config: + import-policy: + - neigh_af_rmap1 + export-policy: + - neigh_af_rmap2 + prefix-list: + config: + import-policy: p1 + export-policy: p2 + ipv4-unicast: + config: + default-policy-name: rmap_reg1 + send-default-route: True + prefix-limit: + config: + max-prefixes: 1 + prevent-teardown: True + warning-threshold-pct: 99 + restart-timer: 88 + allow-own-as: + config: + origin: true + enabled: true + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + value: + openconfig-network-instance:neighbors: + neighbor: + - neighbor-address: 1.1.1.1 + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + - neighbor-address: 2.2.2.2 + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + config: + afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=IPV4_UNICAST/apply-policy/config/export-policy" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=IPV4_UNICAST/apply-policy/config/import-policy" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST/allow-own-as/config/enabled" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST/allow-own-as/config/origin" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST/config/route-reflector-client" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST/config/route-server-client" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST/ipv4-unicast/config/default-policy-name" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST/ipv4-unicast/config/send-default-route" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST/ipv4-unicast/prefix-limit/config/max-prefixes" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST/ipv4-unicast/prefix-limit/config/prevent-teardown" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST/ipv4-unicast/prefix-limit/config/restart-timer" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST/ipv4-unicast/prefix-limit/config/warning-threshold-pct" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST/prefix-list/config/export-policy" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=Eth1%2f2/afi-safis/afi-safi=openconfig-bgp-types:IPV4_UNICAST/prefix-list/config/import-policy" + method: "delete" + data: +deleted_03: + module_args: + config: + - bgp_as: 51 + vrf_name: VrfReg1 + neighbors: + - neighbor: 1.1.1.1 + address_family: + - afi: ipv6 + safi: unicast + - neighbor: 2.2.2.2 + address_family: + - afi: l2vpn + safi: evpn + state: deleted + existing_bgp_config: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + router-id: "10.2.2.4" + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/global/config" + response: + code: 200 + value: + openconfig-network-instance:config: + as: 51 + router-id: "10.2.2.4" + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + value: + openconfig-network-instance:neighbors: + neighbor: + - neighbor-address: Eth1/2 + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV4_UNICAST + route-reflector-client: true + route-server-client: true + apply-policy: + config: + import-policy: + - neigh_af_rmap1 + export-policy: + - neigh_af_rmap2 + prefix-list: + config: + import-policy: p1 + export-policy: p2 + ipv4-unicast: + config: + default-policy-name: rmap_reg1 + send-default-route: True + prefix-limit: + config: + max-prefixes: 1 + prevent-teardown: True + warning-threshold-pct: 99 + restart-timer: 88 + allow-own-as: + config: + origin: true + enabled: true + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors" + response: + code: 200 + value: + openconfig-network-instance:neighbors: + neighbor: + - neighbor-address: 1.1.1.1 + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + config: + afi-safi-name: openconfig-bgp-types:IPV6_UNICAST + - neighbor-address: 2.2.2.2 + afi-safis: + afi-safi: + - afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + config: + afi-safi-name: openconfig-bgp-types:L2VPN_EVPN + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=1.1.1.1/afi-safis/afi-safi=openconfig-bgp-types:IPV6_UNICAST" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=BGP,bgp/bgp/neighbors/neighbor=2.2.2.2/afi-safis/afi-safi=openconfig-bgp-types:L2VPN_EVPN" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_command.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_command.yaml new file mode 100644 index 000000000..5241da840 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_command.yaml @@ -0,0 +1,12 @@ +--- +merged_01: + module_args: + commands: + - show version + retries: 5 + interval: 3 + wait_for: + - result[0] contains Version + match: any + expected_command_requests: + - show version diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_config.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_config.yaml new file mode 100644 index 000000000..66b30dced --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_config.yaml @@ -0,0 +1,21 @@ +--- +merged_01: + module_args: + commands: ['snmp-server community public group ro'] + save: yes + expected_commands_to_device: + - snmp-server community public group ro + - write memory + +merged_02: + module_args: + lines: + - seq 2 permit udp any any + - seq 3 deny icmp any any + parents: ['ip access-list test'] + before: ['no ip access-list test'] + expected_commands_to_device: + - no ip access-list test + - ip access-list test + - seq 2 permit udp any any + - seq 3 deny icmp any any diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_copp.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_copp.yaml new file mode 100644 index 000000000..c0bd0557c --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_copp.yaml @@ -0,0 +1,203 @@ +--- +merged_01: + module_args: + config: + copp_groups: + - copp_name: 'copp-1' + trap_priority: 1 + trap_action: 'DROP' + queue: 1 + cir: '45' + cbs: '45' + - copp_name: 'copp-2' + trap_priority: 2 + trap_action: 'FORWARD' + queue: 2 + cir: '90' + cbs: '90' + existing_copp_config: + - path: "/data/openconfig-copp-ext:copp" + response: + code: 200 + expected_config_requests: + - path: "/data/openconfig-copp-ext:copp/copp-groups" + method: "patch" + data: + openconfig-copp-ext:copp-groups: + copp-group: + - name: 'copp-1' + config: + name: 'copp-1' + trap-priority: 1 + trap-action: 'DROP' + queue: 1 + cir: '45' + cbs: '45' + - name: 'copp-2' + config: + name: 'copp-2' + trap-priority: 2 + trap-action: 'FORWARD' + queue: 2 + cir: '90' + cbs: '90' + +replaced_01: + module_args: + config: + copp_groups: + - copp_name: 'copp-2' + trap_action: 'FORWARD' + cir: '60' + cbs: '60' + - copp_name: 'copp-3' + trap_priority: 3 + trap_action: 'DROP' + queue: 3 + cir: '70' + cbs: '70' + - copp_name: 'copp-4' + trap_priority: 5 + trap_action: 'DROP' + queue: 5 + cir: '75' + cbs: '75' + state: replaced + existing_copp_config: + - path: "/data/openconfig-copp-ext:copp" + response: + code: 200 + value: + openconfig-copp-ext:copp: + copp-groups: + copp-group: + - name: 'copp-1' + config: + name: 'copp-1' + trap-priority: 1 + trap-action: 'DROP' + queue: 1 + cir: '45' + cbs: '45' + expected_config_requests: + - path: "/data/openconfig-copp-ext:copp/copp-groups" + method: "patch" + data: + openconfig-copp-ext:copp-groups: + copp-group: + - name: 'copp-2' + config: + name: 'copp-2' + trap-action: 'FORWARD' + cir: '60' + cbs: '60' + - name: 'copp-3' + config: + name: 'copp-3' + trap-priority: 3 + trap-action: 'DROP' + queue: 3 + cir: '70' + cbs: '70' + - name: 'copp-4' + config: + name: 'copp-4' + trap-priority: 5 + trap-action: 'DROP' + queue: 5 + cir: '75' + cbs: '75' + +overridden_01: + module_args: + config: + copp_groups: + - copp_name: 'copp-5' + trap_priority: 1 + trap_action: 'FORWARD' + queue: 1 + cir: '15' + cbs: '15' + state: overridden + existing_copp_config: + - path: "/data/openconfig-copp-ext:copp" + response: + code: 200 + value: + openconfig-copp-ext:copp: + copp-groups: + copp-group: + - name: 'copp-1' + config: + name: 'copp-1' + trap-priority: 1 + trap-action: 'DROP' + queue: 1 + cir: '45' + cbs: '45' + expected_config_requests: + - path: "/data/openconfig-copp-ext:copp/copp-groups" + method: "patch" + data: + openconfig-copp-ext:copp-groups: + copp-group: + - name: 'copp-5' + config: + name: 'copp-5' + trap-priority: 1 + trap-action: 'FORWARD' + queue: 1 + cir: '15' + cbs: '15' + +deleted_01: + module_args: + config: + copp_groups: + - copp_name: 'copp-1' + trap_priority: 8 + queue: 8 + cir: '20' + cbs: '20' + - copp_name: 'copp-2' + state: deleted + existing_copp_config: + - path: "/data/openconfig-copp-ext:copp" + response: + code: 200 + value: + openconfig-copp-ext:copp: + copp-groups: + copp-group: + - name: 'copp-1' + config: + name: 'copp-1' + trap-priority: 8 + trap-action: 'DROP' + queue: 8 + cir: '20' + cbs: '20' + - name: 'copp-2' + config: + name: 'copp-2' + trap-priority: 2 + trap-action: 'FORWARD' + queue: 2 + cir: '60' + cbs: '60' + expected_config_requests: + - path: "/data/openconfig-copp-ext:copp/copp-groups/copp-group=copp-1/config/trap-priority" + method: "delete" + data: + - path: "/data/openconfig-copp-ext:copp/copp-groups/copp-group=copp-1/config/queue" + method: "delete" + data: + - path: "/data/openconfig-copp-ext:copp/copp-groups/copp-group=copp-1/config/cir" + method: "delete" + data: + - path: "/data/openconfig-copp-ext:copp/copp-groups/copp-group=copp-1/config/cbs" + method: "delete" + data: + - path: "/data/openconfig-copp-ext:copp/copp-groups/copp-group=copp-2" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_dhcp_relay.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_dhcp_relay.yaml new file mode 100644 index 000000000..e07bf9da7 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_dhcp_relay.yaml @@ -0,0 +1,917 @@ +--- +merged_01: + module_args: + config: + - name: 'Eth1/5' + ipv4: + server_addresses: + - address: 100.1.1.2 + - address: 100.1.1.3 + source_interface: "Vlan 101" + vrf_name: "VrfReg1" + vrf_select: true + link_select: true + policy_action: "replace" + circuit_id: "%h:%p" + ipv6: + server_addresses: + - address: 100::2 + - address: 100::3 + source_interface: "Vlan 101" + vrf_name: "VrfReg2" + vrf_select: true + - name: 'Eth1/31' + ipv4: + max_hop_count: 8 + - name: 'Eth1/32' + ipv6: + max_hop_count: 8 + facts_get_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp" + response: + code: 200 + value: + openconfig-relay-agent:dhcp: + interfaces: + interface: + - id: 'Eth1/31' + config: + id: 'Eth1/31' + helper-address: + - '131.1.1.2' + openconfig-relay-agent-ext:max-hop-count: 10 + openconfig-relay-agent-ext:policy-action: 'DISCARD' + agent-information-option: + config: + circuit-id: '%p' + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + openconfig-relay-agent-ext:link-select: 'DISABLE' + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6" + response: + code: 200 + value: + openconfig-relay-agent:dhcpv6: + interfaces: + interface: + - id: 'Eth1/32' + config: + id: 'Eth1/32' + helper-address: + - '131::2' + openconfig-relay-agent-ext:max-hop-count: 10 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + config_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f5/config/helper-address" + method: "patch" + data: + openconfig-relay-agent:helper-address: + - '100.1.1.2' + - '100.1.1.3' + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f5/config/openconfig-relay-agent-ext:src-intf" + method: "patch" + data: + openconfig-relay-agent-ext:src-intf: "Vlan101" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f5/config/openconfig-relay-agent-ext:vrf" + method: "patch" + data: + openconfig-relay-agent-ext:vrf: "VrfReg1" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f5/agent-information-option/config/openconfig-relay-agent-ext:vrf-select" + method: "patch" + data: + openconfig-relay-agent-ext:vrf-select: "ENABLE" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f5/agent-information-option/config/openconfig-relay-agent-ext:link-select" + method: "patch" + data: + openconfig-relay-agent-ext:link-select: "ENABLE" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f5/config/openconfig-relay-agent-ext:policy-action" + method: "patch" + data: + openconfig-relay-agent-ext:policy-action: "REPLACE" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f5/agent-information-option/config/circuit-id" + method: "patch" + data: + openconfig-relay-agent:circuit-id: "%h:%p" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f5/config/helper-address" + method: "patch" + data: + openconfig-relay-agent:helper-address: + - '100::2' + - '100::3' + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f5/config/openconfig-relay-agent-ext:src-intf" + method: "patch" + data: + openconfig-relay-agent-ext:src-intf: "Vlan101" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f5/config/openconfig-relay-agent-ext:vrf" + method: "patch" + data: + openconfig-relay-agent-ext:vrf: "VrfReg2" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f5/options/config/openconfig-relay-agent-ext:vrf-select" + method: "patch" + data: + openconfig-relay-agent-ext:vrf-select: "ENABLE" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f31/config/openconfig-relay-agent-ext:max-hop-count" + method: "patch" + data: + openconfig-relay-agent-ext:max-hop-count: 8 + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f32/config/openconfig-relay-agent-ext:max-hop-count" + method: "patch" + data: + openconfig-relay-agent-ext:max-hop-count: 8 +merged_02: + module_args: + config: + - name: 'Eth1/32' + ipv4: + server_addresses: + - address: '132.1.1.2' + ipv6: + server_addresses: + - address: '132::2' + facts_get_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp" + response: + code: 200 + value: + openconfig-relay-agent:dhcp: + interfaces: + interface: + - id: 'Eth1/32' + config: + id: 'Eth1/32' + helper-address: + - '132.1.1.2' + openconfig-relay-agent-ext:max-hop-count: 10 + openconfig-relay-agent-ext:policy-action: 'DISCARD' + agent-information-option: + config: + circuit-id: '%p' + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + openconfig-relay-agent-ext:link-select: 'DISABLE' + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6" + response: + code: 200 + value: + openconfig-relay-agent:dhcpv6: + interfaces: + interface: + - id: 'Eth1/32' + config: + id: 'Eth1/32' + helper-address: + - '132::2' + openconfig-relay-agent-ext:max-hop-count: 10 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + config_requests: [] +deleted_01: + module_args: + config: + - name: 'Eth1/1' + ipv4: + server_addresses: + - address: '100.1.1.2' + vrf_select: true + source_interface: 'Vlan100' + link_select: true + policy_action: 'replace' + circuit_id: '%i' + - name: 'Eth1/2' + ipv6: + server_addresses: + - address: '101::2' + vrf_select: true + source_interface: 'Vlan100' + - name: 'Eth1/3' + ipv4: + max_hop_count: 12 + ipv6: + max_hop_count: 12 + state: deleted + facts_get_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp" + response: + code: 200 + value: + openconfig-relay-agent:dhcp: + interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + helper-address: + - '100.1.1.2' + - '100.1.1.3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + openconfig-relay-agent-ext:policy-action: 'REPLACE' + agent-information-option: + config: + circuit-id: '%i' + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + openconfig-relay-agent-ext:link-select: 'ENABLE' + - id: 'Eth1/2' + config: + id: 'Eth1/2' + helper-address: + - '101.1.1.2' + - '101.1.1.3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + openconfig-relay-agent-ext:policy-action: 'REPLACE' + agent-information-option: + config: + circuit-id: '%i' + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + openconfig-relay-agent-ext:link-select: 'ENABLE' + - id: 'Eth1/3' + config: + id: 'Eth1/3' + helper-address: + - '102.1.1.2' + - '102.1.1.3' + openconfig-relay-agent-ext:max-hop-count: 12 + openconfig-relay-agent-ext:policy-action: 'DISCARD' + agent-information-option: + config: + circuit-id: '%p' + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + openconfig-relay-agent-ext:link-select: 'DISABLE' + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6" + response: + code: 200 + value: + openconfig-relay-agent:dhcpv6: + interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + helper-address: + - '100::2' + - '100::3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + - id: 'Eth1/2' + config: + id: 'Eth1/2' + helper-address: + - '101::2' + - '101::3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + - id: 'Eth1/3' + config: + id: 'Eth1/3' + helper-address: + - '102::2' + - '102::3' + openconfig-relay-agent-ext:max-hop-count: 12 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + config_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f1/config/helper-address=100.1.1.2" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f1/agent-information-option/config/openconfig-relay-agent-ext:vrf-select" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f1/config/openconfig-relay-agent-ext:src-intf" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f1/agent-information-option/config/openconfig-relay-agent-ext:link-select" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f1/config/openconfig-relay-agent-ext:policy-action" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f1/agent-information-option/config/circuit-id" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f2/config/helper-address=101::2" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f2/options/config/openconfig-relay-agent-ext:vrf-select" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f2/config/openconfig-relay-agent-ext:src-intf" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f3/config/openconfig-relay-agent-ext:max-hop-count" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f3/config/openconfig-relay-agent-ext:max-hop-count" + method: "delete" +deleted_02: + module_args: + config: + - name: 'Eth1/1' + ipv4: + server_addresses: + - address: + ipv6: + server_addresses: + - address: + - name: 'Eth1/2' + ipv4: + server_addresses: + - address: '101.1.1.2' + - address: '101.1.1.3' + ipv6: + server_addresses: + - address: '101::2' + - address: '101::3' + - name: 'Eth1/3' + state: deleted + facts_get_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp" + response: + code: 200 + value: + openconfig-relay-agent:dhcp: + interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + helper-address: + - '100.1.1.2' + - '100.1.1.3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + openconfig-relay-agent-ext:policy-action: 'REPLACE' + agent-information-option: + config: + circuit-id: '%i' + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + openconfig-relay-agent-ext:link-select: 'ENABLE' + - id: 'Eth1/2' + config: + id: 'Eth1/2' + helper-address: + - '101.1.1.2' + - '101.1.1.3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + openconfig-relay-agent-ext:policy-action: 'REPLACE' + agent-information-option: + config: + circuit-id: '%i' + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + openconfig-relay-agent-ext:link-select: 'ENABLE' + - id: 'Eth1/3' + config: + id: 'Eth1/3' + helper-address: + - '102.1.1.2' + - '102.1.1.3' + openconfig-relay-agent-ext:max-hop-count: 10 + openconfig-relay-agent-ext:policy-action: 'DISCARD' + agent-information-option: + config: + circuit-id: '%p' + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + openconfig-relay-agent-ext:link-select: 'DISABLE' + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6" + response: + code: 200 + value: + openconfig-relay-agent:dhcpv6: + interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + helper-address: + - '100::2' + - '100::3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + - id: 'Eth1/2' + config: + id: 'Eth1/2' + helper-address: + - '101::2' + - '101::3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + - id: 'Eth1/3' + config: + id: 'Eth1/3' + helper-address: + - '102::2' + - '102::3' + openconfig-relay-agent-ext:max-hop-count: 10 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + config_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f1/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f1/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f2/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f2/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f3/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f3/config/helper-address" + method: "delete" +deleted_03: + module_args: + config: + state: deleted + facts_get_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp" + response: + code: 200 + value: + openconfig-relay-agent:dhcp: + interfaces: + interface: + - id: 'Eth1/32' + config: + id: 'Eth1/32' + helper-address: + - '132.1.1.2' + - '132.1.1.3' + openconfig-relay-agent-ext:max-hop-count: 10 + openconfig-relay-agent-ext:policy-action: 'DISCARD' + agent-information-option: + config: + circuit-id: '%p' + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + openconfig-relay-agent-ext:link-select: 'DISABLE' + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6" + response: + code: 200 + value: + openconfig-relay-agent:dhcpv6: + interfaces: + interface: + - id: 'Eth1/32' + config: + id: 'Eth1/32' + helper-address: + - '132::2' + - '132::3' + openconfig-relay-agent-ext:max-hop-count: 10 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + config_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f32/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f32/config/helper-address" + method: "delete" +deleted_04: + module_args: + config: + - name: 'Eth1/1' + ipv4: + server_addresses: + - address: '100.1.1.2' + vrf_select: true + max_hop_count: 8 + source_interface: 'Vlan100' + link_select: true + policy_action: 'replace' + ipv6: + server_addresses: + - address: '100::2' + source_interface: 'Vlan100' + state: deleted + facts_get_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp" + response: + code: 200 + value: {} + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6" + response: + code: 200 + value: {} + config_requests: [] +replaced_01: + module_args: + config: + - name: 'Eth1/1' + ipv4: + server_addresses: + - address: '100.1.1.2' + - address: '100.1.1.3' + source_interface: 'Vlan100' + policy_action: 'append' + ipv6: + server_addresses: + - address: '100::2' + - address: '100::3' + - name: 'Eth1/2' + ipv4: + server_addresses: + - address: '101.1.1.2' + - address: '101.1.1.4' + - address: '101.1.1.6' + vrf_name: 'VrfReg2' + vrf_select: false + max_hop_count: 10 + - name: 'Eth1/3' + ipv6: + server_addresses: + - address: '102::2' + - address: '102::4' + - address: '102::6' + vrf_name: 'VrfReg2' + vrf_select: false + state: replaced + facts_get_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp" + response: + code: 200 + value: + openconfig-relay-agent:dhcp: + interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + helper-address: + - '100.1.1.2' + - '100.1.1.3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + openconfig-relay-agent-ext:policy-action: 'REPLACE' + agent-information-option: + config: + circuit-id: '%i' + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + openconfig-relay-agent-ext:link-select: 'ENABLE' + - id: 'Eth1/2' + config: + id: 'Eth1/2' + helper-address: + - '101.1.1.2' + - '101.1.1.3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + openconfig-relay-agent-ext:policy-action: 'DISCARD' + agent-information-option: + config: + circuit-id: '%i' + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + openconfig-relay-agent-ext:link-select: 'ENABLE' + - id: 'Eth1/3' + config: + id: 'Eth1/3' + helper-address: + - '102.1.1.2' + - '102.1.1.3' + openconfig-relay-agent-ext:max-hop-count: 10 + openconfig-relay-agent-ext:policy-action: 'DISCARD' + agent-information-option: + config: + circuit-id: '%p' + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + openconfig-relay-agent-ext:link-select: 'DISABLE' + - id: 'Eth1/4' + config: + id: 'Eth1/4' + helper-address: + - '103.1.1.2' + - '103.1.1.3' + openconfig-relay-agent-ext:max-hop-count: 10 + openconfig-relay-agent-ext:policy-action: 'DISCARD' + agent-information-option: + config: + circuit-id: '%p' + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + openconfig-relay-agent-ext:link-select: 'DISABLE' + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6" + response: + code: 200 + value: + openconfig-relay-agent:dhcpv6: + interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + helper-address: + - '100::2' + - '100::3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + - id: 'Eth1/2' + config: + id: 'Eth1/2' + helper-address: + - '101::2' + - '101::3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + - id: 'Eth1/3' + config: + id: 'Eth1/3' + helper-address: + - '102::2' + - '102::3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:max-hop-count: 8 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + config_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f1/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f1/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f2/config/helper-address=101.1.1.3" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f2/config/openconfig-relay-agent-ext:src-intf" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f2/agent-information-option/config/openconfig-relay-agent-ext:link-select" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f2/agent-information-option/config/circuit-id" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f2/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f3/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f3/config/helper-address=102::3" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f3/config/openconfig-relay-agent-ext:src-intf" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f3/config/openconfig-relay-agent-ext:max-hop-count" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f1/config/helper-address" + method: "patch" + data: + openconfig-relay-agent:helper-address: + - '100.1.1.2' + - '100.1.1.3' + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f1/config/openconfig-relay-agent-ext:src-intf" + method: "patch" + data: + openconfig-relay-agent-ext:src-intf: "Vlan100" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f1/config/openconfig-relay-agent-ext:policy-action" + method: "patch" + data: + openconfig-relay-agent-ext:policy-action: "APPEND" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f1/config/helper-address" + method: "patch" + data: + openconfig-relay-agent:helper-address: + - '100::2' + - '100::3' + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f2/config/helper-address" + method: "patch" + data: + openconfig-relay-agent:helper-address: + - '101.1.1.4' + - '101.1.1.6' + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f2/config/openconfig-relay-agent-ext:vrf" + method: "patch" + data: + openconfig-relay-agent-ext:vrf: "VrfReg2" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f2/agent-information-option/config/openconfig-relay-agent-ext:vrf-select" + method: "patch" + data: + openconfig-relay-agent-ext:vrf-select: "DISABLE" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f2/config/openconfig-relay-agent-ext:max-hop-count" + method: "patch" + data: + openconfig-relay-agent-ext:max-hop-count: 10 + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f3/config/helper-address" + method: "patch" + data: + openconfig-relay-agent:helper-address: + - '102::4' + - '102::6' + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f3/config/openconfig-relay-agent-ext:vrf" + method: "patch" + data: + openconfig-relay-agent-ext:vrf: "VrfReg2" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f3/options/config/openconfig-relay-agent-ext:vrf-select" + method: "patch" + data: + openconfig-relay-agent-ext:vrf-select: "DISABLE" +replaced_02: + module_args: + config: + - name: 'Eth1/2' + ipv4: + server_addresses: + - address: '101.1.1.2' + - address: '101.1.1.4' + vrf_name: 'VrfReg1' + max_hop_count: 12 + - name: 'Eth1/3' + ipv6: + server_addresses: + - address: '102::2' + - address: '102::4' + vrf_name: 'VrfReg1' + state: replaced + facts_get_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp" + response: + code: 200 + value: + openconfig-relay-agent:dhcp: + interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + helper-address: + - '100.1.1.2' + - '100.1.1.3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + openconfig-relay-agent-ext:policy-action: 'REPLACE' + agent-information-option: + config: + circuit-id: '%i' + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + openconfig-relay-agent-ext:link-select: 'ENABLE' + - id: 'Eth1/2' + config: + id: 'Eth1/2' + helper-address: + - '101.1.1.2' + - '101.1.1.4' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 12 + openconfig-relay-agent-ext:policy-action: 'DISCARD' + agent-information-option: + config: + circuit-id: '%p' + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + openconfig-relay-agent-ext:link-select: 'DISABLE' + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6" + response: + code: 200 + value: + openconfig-relay-agent:dhcpv6: + interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + helper-address: + - '100::2' + - '100::3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + - id: 'Eth1/3' + config: + id: 'Eth1/3' + helper-address: + - '102::2' + - '102::4' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 10 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + config_requests: [] +overridden_01: + module_args: + config: + - name: 'Eth1/2' + ipv4: + server_addresses: + - address: '110.1.1.2' + - address: '110.1.1.3' + vrf_name: 'VrfReg1' + - name: 'Eth1/3' + ipv6: + server_addresses: + - address: '120::2' + - address: '120::3' + vrf_name: 'VrfReg1' + state: overridden + facts_get_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp" + response: + code: 200 + value: + openconfig-relay-agent:dhcp: + interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + helper-address: + - '100.1.1.2' + - '100.1.1.3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + openconfig-relay-agent-ext:policy-action: 'REPLACE' + agent-information-option: + config: + circuit-id: '%i' + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + openconfig-relay-agent-ext:link-select: 'ENABLE' + - id: 'Eth1/2' + config: + id: 'Eth1/2' + helper-address: + - '101.1.1.2' + - '101.1.1.4' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 12 + openconfig-relay-agent-ext:policy-action: 'DISCARD' + agent-information-option: + config: + circuit-id: '%p' + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + openconfig-relay-agent-ext:link-select: 'DISABLE' + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6" + response: + code: 200 + value: + openconfig-relay-agent:dhcpv6: + interfaces: + interface: + - id: 'Eth1/1' + config: + id: 'Eth1/1' + helper-address: + - '100::2' + - '100::3' + openconfig-relay-agent-ext:src-intf: 'Vlan100' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 8 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'ENABLE' + - id: 'Eth1/3' + config: + id: 'Eth1/3' + helper-address: + - '102::2' + - '102::4' + openconfig-relay-agent-ext:vrf: 'VrfReg1' + openconfig-relay-agent-ext:max-hop-count: 10 + options: + config: + openconfig-relay-agent-ext:vrf-select: 'DISABLE' + config_requests: + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f1/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f1/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f2/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f3/config/helper-address" + method: "delete" + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f2/config/helper-address" + method: "patch" + data: + openconfig-relay-agent:helper-address: + - '110.1.1.2' + - '110.1.1.3' + - path: "data/openconfig-relay-agent:relay-agent/dhcp/interfaces/interface=Eth1%2f2/config/openconfig-relay-agent-ext:vrf" + method: "patch" + data: + openconfig-relay-agent-ext:vrf: "VrfReg1" + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f3/config/helper-address" + method: "patch" + data: + openconfig-relay-agent:helper-address: + - '120::2' + - '120::3' + - path: "data/openconfig-relay-agent:relay-agent/dhcpv6/interfaces/interface=Eth1%2f3/config/openconfig-relay-agent-ext:vrf" + method: "patch" + data: + openconfig-relay-agent-ext:vrf: "VrfReg1" diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_dhcp_snooping.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_dhcp_snooping.yaml new file mode 100644 index 000000000..3fedc7eea --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_dhcp_snooping.yaml @@ -0,0 +1,1128 @@ +merged_01: + module_args: + config: + afis: + - afi: 'ipv4' + enabled: true + verify_mac: true + vlans: ['1', '2', '3', '5'] + trusted: + - intf_name: 'Ethernet8' + state: merged + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: false + dhcpv4-verify-mac-address: false + dhcpv6-verify-mac-address: false + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: [] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: [] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: [] + expected_config_requests: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv4-admin-enable' + method: 'patch' + data: + openconfig-dhcp-snooping:dhcpv4-admin-enable: true + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv4-verify-mac-address' + method: 'patch' + data: + openconfig-dhcp-snooping:dhcpv4-verify-mac-address: true + - path: 'data/openconfig-interfaces:interfaces/interface=Ethernet8/dhcpv4-snooping-trust/config/dhcpv4-snooping-trust' + method: 'patch' + data: + openconfig-interfaces:dhcpv4-snooping-trust: 'ENABLE' + - path: 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST=Vlan1/dhcpv4_snooping_enable' + method: 'patch' + data: + sonic-vlan:dhcpv4_snooping_enable: 'enable' + - path: 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST=Vlan2/dhcpv4_snooping_enable' + method: 'patch' + data: + sonic-vlan:dhcpv4_snooping_enable: 'enable' + - path: 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST=Vlan3/dhcpv4_snooping_enable' + method: 'patch' + data: + sonic-vlan:dhcpv4_snooping_enable: 'enable' + - path: 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST=Vlan5/dhcpv4_snooping_enable' + method: 'patch' + data: + sonic-vlan:dhcpv4_snooping_enable: 'enable' + +merged_02: + module_args: + config: + afis: + - afi: 'ipv6' + enabled: true + vlans: + - '4' + trusted: + - intf_name: 'Ethernet2' + - intf_name: PortChannel1 + state: merged + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: false + dhcpv4-verify-mac-address: false + dhcpv6-verify-mac-address: false + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: [] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: [] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: [] + expected_config_requests: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv6-admin-enable' + method: 'patch' + data: + openconfig-dhcp-snooping:dhcpv6-admin-enable: true + - path: 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST=Vlan4/dhcpv6_snooping_enable' + method: 'patch' + data: + sonic-vlan:dhcpv6_snooping_enable: 'enable' + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel1/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: 'patch' + data: + openconfig-interfaces:dhcpv6-snooping-trust: 'ENABLE' + - path: 'data/openconfig-interfaces:interfaces/interface=Ethernet2/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: 'patch' + data: + openconfig-interfaces:dhcpv6-snooping-trust: 'ENABLE' + +merged_03: + module_args: + config: + afis: + - afi: 'ipv4' + source_bindings: + - mac_addr: '00:b0:d0:63:c2:26' + ip_addr: '192.0.2.146' + intf_name: 'Ethernet4' + vlan_id: '1' + - mac_addr: 'aa:f7:67:fc:f4:9a' + ip_addr: '156.33.90.167' + intf_name: 'PortChannel1' + vlan_id: '2' + state: merged + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: false + dhcpv4-verify-mac-address: false + dhcpv6-verify-mac-address: false + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: [] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: [] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: [] + expected_config_requests: + - path: data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry + method: patch + data: + openconfig-dhcp-snooping:entry: + - mac: '00:b0:d0:63:c2:26' + iptype: ipv4 + config: + mac: '00:b0:d0:63:c2:26' + iptype: ipv4 + vlan: "Vlan1" + interface: Ethernet4 + ip: '192.0.2.146' + - mac: 'aa:f7:67:fc:f4:9a' + iptype: ipv4 + config: + mac: 'aa:f7:67:fc:f4:9a' + iptype: ipv4 + vlan: "Vlan2" + interface: PortChannel1 + ip: '156.33.90.167' + +merged_04_blank: + module_args: + config: + afis: + - afi: ipv4 + - afi: ipv6 + state: merged + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: false + dhcpv4-verify-mac-address: false + dhcpv6-verify-mac-address: false + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: [] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: [] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: [] + expected_config_requests: [] + +deleted_01: + module_args: + config: + afis: + - afi: 'ipv4' + vlans: + - '3' + - '5' + state: deleted + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: true + dhcpv6-admin-enable: false + dhcpv4-verify-mac-address: true + dhcpv6-verify-mac-address: false + state: + dhcpv4-snooping-vlan: ["1", "2", "3", "5"] + dhcpv6-snooping-vlan: [] + dhcpv4-trusted-intf: ["Ethernet8"] + dhcpv6-trusted-intf: [] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: [] + expected_config_requests: + - path: 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST=Vlan3/dhcpv4_snooping_enable' + method: 'delete' + - path: 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST=Vlan5/dhcpv4_snooping_enable' + method: 'delete' + +deleted_02_clear_vlans: + module_args: + config: + afis: + - afi: 'ipv6' + vlans: [] + state: deleted + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: true + dhcpv4-verify-mac-address: false + dhcpv6-verify-mac-address: true + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: ["4"] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: ["PortChannel1", "PortChannel2", "PortChannel3", "PortChannel4"] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: [] + expected_config_requests: + - path: 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST=Vlan4/dhcpv6_snooping_enable' + method: 'delete' + +deleted_02_2_select_vlans: + module_args: + config: + afis: + - afi: 'ipv6' + vlans: + - "2" + - "6" + state: deleted + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: true + dhcpv4-verify-mac-address: false + dhcpv6-verify-mac-address: true + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: ["4", "2"] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: ["PortChannel1", "PortChannel2", "PortChannel3", "PortChannel4"] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: [] + expected_config_requests: + - path: 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST=Vlan2/dhcpv6_snooping_enable' + method: 'delete' + +deleted_03: + module_args: + config: + afis: + - afi: 'ipv6' + state: deleted + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: true + dhcpv4-verify-mac-address: false + dhcpv6-verify-mac-address: false + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: ["4"] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: ["PortChannel1", "PortChannel2", "PortChannel3", "PortChannel4"] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: [] + expected_config_requests: + - path: data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv6-admin-enable + method: patch + data: + openconfig-dhcp-snooping:dhcpv6-admin-enable: false + - path: 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST=Vlan4/dhcpv6_snooping_enable' + method: 'delete' + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel1/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: delete + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel2/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: delete + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel3/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: 'delete' + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel4/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: 'delete' + - path: "data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv6-verify-mac-address" + method: "patch" + data: + openconfig-dhcp-snooping:dhcpv6-verify-mac-address: true + +deleted_04_clear_bindings: + module_args: + config: + afis: + - afi: 'ipv4' + source_bindings: [] + state: deleted + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: true + dhcpv4-verify-mac-address: false + dhcpv6-verify-mac-address: true + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: [] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: [] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping-binding: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: + - mac: '00:b0:d0:63:c2:26' + iptype: ipv4 + state: + mac: '00:b0:d0:63:c2:26' + iptype: ipv4 + vlan: "1" + intf: Ethernet4 + ipaddress: "192.0.2.146" + - mac: 'aa:f7:67:fc:f4:9a' + iptype: ipv4 + state: + mac: 'aa:f7:67:fc:f4:9a' + iptype: ipv4 + vlan: "2" + intf: PortChannel1 + ipaddress: '156.33.90.167' + expected_config_requests: + - path: data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=00:b0:d0:63:c2:26,ipv4 + method: delete + - path: data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=aa:f7:67:fc:f4:9a,ipv4 + method: delete + data: Null + +deleted_05_select_bindings: + module_args: + config: + afis: + - afi: 'ipv4' + source_bindings: + - mac_addr: '00:b0:d0:63:c2:26' + ip_addr: '192.0.2.146' + intf_name: 'Ethernet4' + vlan_id: '1' + - mac_addr: 'a6:83:f5:92:c2:69' + ip_addr: '46.9.247.7' + intf_name: 'Ethernet6' + vlan_id: '7' + state: deleted + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: true + dhcpv4-verify-mac-address: false + dhcpv6-verify-mac-address: true + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: [] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: [] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping-binding: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: + - mac: '00:b0:d0:63:c2:26' + iptype: ipv4 + state: + mac: '00:b0:d0:63:c2:26' + iptype: ipv4 + vlan: "1" + intf: Ethernet4 + ipaddress: "192.0.2.146" + - mac: 'aa:f7:67:fc:f4:9a' + iptype: ipv4 + state: + mac: 'aa:f7:67:fc:f4:9a' + iptype: ipv4 + vlan: "2" + intf: PortChannel1 + ipaddress: '156.33.90.167' + expected_config_requests: + - path: data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=00:b0:d0:63:c2:26,ipv4 + method: delete + +deleted_06_clear_trusted: + module_args: + config: + afis: + - afi: 'ipv6' + trusted: [] + state: deleted + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: true + dhcpv4-verify-mac-address: false + dhcpv6-verify-mac-address: true + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: ["4"] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: ["PortChannel1", "PortChannel2", "PortChannel3", "PortChannel4"] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: [] + expected_config_requests: + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel1/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: delete + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel2/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: delete + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel3/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: 'delete' + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel4/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: 'delete' + +deleted_07_select_trusted: + module_args: + config: + afis: + - afi: 'ipv6' + trusted: + - intf_name: PortChannel1 + - intf_name: PortChannel6 + state: deleted + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: true + dhcpv4-verify-mac-address: false + dhcpv6-verify-mac-address: true + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: ["4"] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: ["PortChannel1", "PortChannel2", "PortChannel3", "PortChannel4"] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: [] + expected_config_requests: + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel1/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: delete + +deleted_08_booleans: + module_args: + config: + afis: + - afi: 'ipv6' + enabled: true + verify_mac: false + state: deleted + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: true + dhcpv4-verify-mac-address: false + dhcpv6-verify-mac-address: false + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: ["4"] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: ["PortChannel1", "PortChannel2", "PortChannel3", "PortChannel4"] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: [] + expected_config_requests: + - path: data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv6-admin-enable + method: patch + data: + openconfig-dhcp-snooping:dhcpv6-admin-enable: false + - path: "data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv6-verify-mac-address" + method: "patch" + data: + openconfig-dhcp-snooping:dhcpv6-verify-mac-address: true + +deleted_09_empty: + module_args: + config: + afis: [] + state: deleted + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: true + dhcpv4-verify-mac-address: false + dhcpv6-verify-mac-address: false + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: ["4"] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: ["PortChannel1", "PortChannel2", "PortChannel3", "PortChannel4"] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: [] + expected_config_requests: + - path: data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv6-admin-enable + method: patch + data: + openconfig-dhcp-snooping:dhcpv6-admin-enable: false + - path: "data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv6-verify-mac-address" + method: "patch" + data: + openconfig-dhcp-snooping:dhcpv6-verify-mac-address: true + - path: "data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv4-verify-mac-address" + method: "patch" + data: + openconfig-dhcp-snooping:dhcpv4-verify-mac-address: true + - path: 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST=Vlan4/dhcpv6_snooping_enable' + method: 'delete' + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel1/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: delete + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel2/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: delete + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel3/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: 'delete' + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel4/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: 'delete' + +overridden_01: + module_args: + config: + afis: + - afi: 'ipv4' + enabled: false + verify_mac: false + source_bindings: + - mac_addr: '00:b0:d0:63:c2:26' + ip_addr: '192.0.2.146' + intf_name: 'Ethernet4' + vlan_id: '3' + - afi: 'ipv6' + enabled: false + source_bindings: + - mac_addr: '00:b0:d0:63:c2:27' + ip_addr: '2002::2' + intf_name: 'Ethernet1' + vlan_id: '3' + state: overridden + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: true + dhcpv6-admin-enable: false + dhcpv4-verify-mac-address: true + dhcpv6-verify-mac-address: true + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: [] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: [] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping-binding: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: + - mac: '00:b0:d0:63:c2:26' + iptype: ipv4 + state: + mac: '00:b0:d0:63:c2:26' + iptype: ipv4 + vlan: "1" + intf: Ethernet4 + ipaddress: "192.0.2.146" + - mac: '28:21:28:15:c1:1b' + iptype: ipv4 + state: + mac: '28:21:28:15:c1:1b' + iptype: ipv4 + vlan: "1" + intf: Ethernet2 + ipaddress: "141.202.222.118" + - mac: 'aa:f7:67:fc:f4:9a' + iptype: ipv4 + state: + mac: 'aa:f7:67:fc:f4:9a' + iptype: ipv4 + vlan: "2" + intf: PortChannel1 + ipaddress: '156.33.90.167' + - mac: '00:b0:d0:63:c2:27' + iptype: ipv6 + state: + mac: '00:b0:d0:63:c2:27' + iptype: ipv6 + vlan: "1" + intf: Ethernet3 + ipaddress: "2002::2" + expected_config_requests: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=00:b0:d0:63:c2:26,ipv4' + method: 'delete' + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=28:21:28:15:c1:1b,ipv4' + method: 'delete' + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=aa:f7:67:fc:f4:9a,ipv4' + method: "delete" + - path: data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry + method: patch + data: + openconfig-dhcp-snooping:entry: + - mac: 00:b0:d0:63:c2:26 + iptype: ipv4 + config: + mac: 00:b0:d0:63:c2:26 + iptype: ipv4 + vlan: "Vlan3" + interface: Ethernet4 + ip: "192.0.2.146" + - path: data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv4-admin-enable + method: patch + data: + openconfig-dhcp-snooping:dhcpv4-admin-enable: false + - path: "data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv4-verify-mac-address" + method: "patch" + data: + openconfig-dhcp-snooping:dhcpv4-verify-mac-address: false + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=00:b0:d0:63:c2:27,ipv6' + method: "delete" + - path: data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry + method: patch + data: + openconfig-dhcp-snooping:entry: + - mac: '00:b0:d0:63:c2:27' + iptype: ipv6 + config: + mac: '00:b0:d0:63:c2:27' + iptype: ipv6 + vlan: "Vlan3" + interface: Ethernet1 + ip: "2002::2" + +replaced_01: + module_args: + config: + afis: + - afi: 'ipv4' + source_bindings: + - mac_addr: '00:b0:d0:63:c2:26' + ip_addr: '192.0.2.146' + intf_name: 'Ethernet4' + vlan_id: '3' + enabled: true + verify_mac: true + - afi: 'ipv6' + source_bindings: + - mac_addr: '00:b0:d0:63:c2:23' + ip_addr: '1640:0:0::83' + intf_name: 'Ethernet2' + vlan_id: '1' + state: replaced + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: true + dhcpv4-verify-mac-address: true + dhcpv6-verify-mac-address: true + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: [] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: [] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping-binding: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: + - mac: '00:b0:d0:63:c2:26' + iptype: ipv4 + state: + mac: '00:b0:d0:63:c2:26' + iptype: ipv4 + vlan: "1" + intf: Ethernet4 + ipaddress: "192.0.2.146" + - mac: '28:21:28:15:c1:1b' + iptype: ipv4 + state: + mac: '28:21:28:15:c1:1b' + iptype: ipv4 + vlan: "1" + intf: Ethernet2 + ipaddress: "141.202.222.118" + - mac: 'aa:f7:67:fc:f4:9a' + iptype: ipv4 + state: + mac: 'aa:f7:67:fc:f4:9a' + iptype: ipv4 + vlan: "2" + intf: PortChannel1 + ipaddress: '156.33.90.167' + expected_config_requests: + - path: data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv4-admin-enable + method: patch + data: + openconfig-dhcp-snooping:dhcpv4-admin-enable: true + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=00:b0:d0:63:c2:26,ipv4' + method: 'delete' + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=28:21:28:15:c1:1b,ipv4' + method: 'delete' + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=aa:f7:67:fc:f4:9a,ipv4' + method: "delete" + - path: "data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry" + method: "patch" + data: + openconfig-dhcp-snooping:entry: + - mac: "00:b0:d0:63:c2:26" + iptype: "ipv4" + config: + mac: "00:b0:d0:63:c2:26" + iptype: "ipv4" + vlan: "Vlan3" + interface: "Ethernet4" + ip: "192.0.2.146" + - path: "data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry" + method: "patch" + data: + openconfig-dhcp-snooping:entry: + - mac: "00:b0:d0:63:c2:23" + iptype: "ipv6" + config: + mac: "00:b0:d0:63:c2:23" + iptype: "ipv6" + vlan: "Vlan1" + interface: "Ethernet2" + ip: "1640:0:0::83" + +replaced_02: + module_args: + config: + afis: + - afi: 'ipv4' + source_bindings: + - mac_addr: '00:b0:d0:63:c2:26' + ip_addr: '192.0.2.146' + intf_name: 'Ethernet4' + vlan_id: '3' + state: replaced + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: false + dhcpv4-verify-mac-address: true + dhcpv6-verify-mac-address: true + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: [] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: [] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping-binding: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: + - mac: '00:b0:d0:63:c2:26' + iptype: ipv4 + state: + mac: '00:b0:d0:63:c2:26' + iptype: ipv4 + vlan: "1" + intf: Ethernet4 + ipaddress: "192.0.2.146" + - mac: '28:21:28:15:c1:1b' + iptype: ipv4 + state: + mac: '28:21:28:15:c1:1b' + iptype: ipv4 + vlan: "1" + intf: Ethernet2 + ipaddress: "141.202.222.118" + - mac: 'aa:f7:67:fc:f4:9a' + iptype: ipv4 + state: + mac: 'aa:f7:67:fc:f4:9a' + iptype: ipv4 + vlan: "2" + intf: PortChannel1 + ipaddress: '156.33.90.167' + expected_config_requests: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=00:b0:d0:63:c2:26,ipv4' + method: 'delete' + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=28:21:28:15:c1:1b,ipv4' + method: 'delete' + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=aa:f7:67:fc:f4:9a,ipv4' + method: 'delete' + - path: "data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry" + method: "patch" + data: + openconfig-dhcp-snooping:entry: + - mac: "00:b0:d0:63:c2:26" + iptype: "ipv4" + config: + mac: "00:b0:d0:63:c2:26" + iptype: "ipv4" + vlan: "Vlan3" + interface: "Ethernet4" + ip: "192.0.2.146" + +replaced_03_vlan_replace: + module_args: + config: + afis: + - afi: 'ipv6' + vlans: + - "3" + - "2" + state: replaced + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: false + dhcpv4-verify-mac-address: true + dhcpv6-verify-mac-address: true + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: ["3", "1"] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: [] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping-binding: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: + - mac: '00:b0:d0:63:c2:26' + iptype: ipv6 + state: + mac: '00:b0:d0:63:c2:26' + iptype: ipv6 + vlan: "1" + intf: Ethernet4 + ipaddress: "192.0.2.146" + expected_config_requests: + - path: 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST=Vlan1/dhcpv6_snooping_enable' + method: 'delete' + - path: 'data/sonic-vlan:sonic-vlan/VLAN/VLAN_LIST=Vlan2/dhcpv6_snooping_enable' + method: 'patch' + data: + sonic-vlan:dhcpv6_snooping_enable: 'enable' + +replaced_04_trusted: + module_args: + config: + afis: + - afi: 'ipv6' + trusted: + - intf_name: Ethernet1 + - intf_name: PortChannel1 + verify_mac: true + state: replaced + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: false + dhcpv4-verify-mac-address: true + dhcpv6-verify-mac-address: false + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: ["3"] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: ["Ethernet54"] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping-binding: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: + - mac: '00:b0:d0:63:c2:26' + iptype: ipv6 + state: + mac: '00:b0:d0:63:c2:26' + iptype: ipv6 + vlan: "1" + intf: Ethernet4 + ipaddress: "192.0.2.146" + expected_config_requests: + - path: "data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv6-verify-mac-address" + method: "patch" + data: + openconfig-dhcp-snooping:dhcpv6-verify-mac-address: true + - path: 'data/openconfig-interfaces:interfaces/interface=Ethernet54/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: delete + - path: 'data/openconfig-interfaces:interfaces/interface=Ethernet1/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: 'patch' + data: + openconfig-interfaces:dhcpv6-snooping-trust: 'ENABLE' + - path: 'data/openconfig-interfaces:interfaces/interface=PortChannel1/dhcpv6-snooping-trust/config/dhcpv6-snooping-trust' + method: 'patch' + data: + openconfig-interfaces:dhcpv6-snooping-trust: 'ENABLE' + +replaced_05_verify: + module_args: + config: + afis: + - afi: 'ipv6' + verify_mac: true + state: replaced + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: false + dhcpv4-verify-mac-address: true + dhcpv6-verify-mac-address: false + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: ["3"] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: ["Ethernet54"] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping-binding: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: [] + expected_config_requests: + - path: "data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv6-verify-mac-address" + method: "patch" + data: + openconfig-dhcp-snooping:dhcpv6-verify-mac-address: true + +replaced_06_empty_bindings: + module_args: + config: + afis: + - afi: 'ipv6' + source_bindings: [] + verify_mac: false + state: replaced + existing_config: + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping' + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping: + config: + dhcpv4-admin-enable: false + dhcpv6-admin-enable: false + dhcpv4-verify-mac-address: true + dhcpv6-verify-mac-address: true + state: + dhcpv4-snooping-vlan: [] + dhcpv6-snooping-vlan: ["3"] + dhcpv4-trusted-intf: [] + dhcpv6-trusted-intf: ["Ethernet54"] + - path: data/openconfig-dhcp-snooping:dhcp-snooping-binding + response: + code: 200 + value: + openconfig-dhcp-snooping:dhcp-snooping-binding: + dhcp-snooping-binding-entry-list: + dhcp-snooping-binding-list: + - mac: '00:b0:d0:63:c2:26' + iptype: ipv6 + state: + mac: '00:b0:d0:63:c2:26' + iptype: ipv6 + vlan: "1" + intf: Ethernet4 + ipaddress: "192.0.2.146" + - mac: '28:21:28:15:c1:1b' + iptype: ipv6 + state: + mac: '28:21:28:15:c1:1b' + iptype: ipv6 + vlan: "1" + intf: Ethernet2 + ipaddress: "141.202.222.118" + expected_config_requests: + - path: "data/openconfig-dhcp-snooping:dhcp-snooping/config/dhcpv6-verify-mac-address" + method: "patch" + data: + openconfig-dhcp-snooping:dhcpv6-verify-mac-address: false + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=00:b0:d0:63:c2:26,ipv6' + method: 'delete' + - path: 'data/openconfig-dhcp-snooping:dhcp-snooping-static-binding/entry=28:21:28:15:c1:1b,ipv6' + method: 'delete' diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_facts.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_facts.yaml new file mode 100644 index 000000000..87dd809ec --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_facts.yaml @@ -0,0 +1,5 @@ +--- +merged_01: + module_args: + gather_network_resources: + - "vlans" diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_interfaces.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_interfaces.yaml new file mode 100644 index 000000000..da213b099 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_interfaces.yaml @@ -0,0 +1,339 @@ +--- +merged_01: + module_args: + config: + - name: Eth1/1 + description: "Test Desc for eth1/1" + enabled: false + mtu: 1600 + - name: Loopback1 + description: "Test Desc for Loopback1" + enabled: false + existing_interfaces_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - name: 'Eth1/1' + config: + mtu: 2000 + description: '' + enabled: true + openconfig-if-ethernet:ethernet: + config: + openconfig-if-ethernet-ext2:advertised-speed: '' + auto-negotiate: false + openconfig-if-ethernet-ext2:port-fec: "openconfig-platform-types:FEC_DISABLED" + port-speed: "openconfig-if-ethernet:SPEED_25GB" + - path: "data/openconfig-port-group:port-groups" + response: + code: 200 + value: + openconfig-port-group:port-groups: + port-group: + - state: + member-if-end: "Eth1/4" + member-if-start: "Eth1/1" + + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces" + method: "patch" + data: + openconfig-interfaces:interfaces: + interface: + - name: Loopback1 + config: + name: Loopback1 + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/config" + method: "patch" + data: + openconfig-interfaces:config: + description: 'Test Desc for eth1/1' + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/config" + method: "patch" + data: + openconfig-interfaces:config: + mtu: 1600 + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/config" + method: "patch" + data: + openconfig-interfaces:config: + enabled: false +deleted_01: + module_args: + state: deleted + existing_interfaces_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - name: 'Eth1/1' + config: + enabled: false + description: 'Test Desc for eth1/1' + mtu: 8888 + openconfig-if-ethernet:ethernet: + config: + openconfig-if-ethernet-ext2:advertised-speed: '' + auto-negotiate: true + openconfig-if-ethernet-ext2:port-fec: "openconfig-platform-types:FEC_DISABLED" + port-speed: "openconfig-if-ethernet:SPEED_25GB" + - name: 'Loopback123' + config: + enabled: false + description: 'Test Desc for Loopback123' + - path: "data/openconfig-port-group:port-groups" + response: + code: 200 + value: + openconfig-port-group:port-groups: + port-group: + - state: + member-if-end: "Eth1/4" + member-if-start: "Eth1/2" + - path: "data/sonic-port:sonic-port/PORT/PORT_LIST=Eth1%2F1/valid_speeds" + response: + code: 200 + value: + sonic-port:valid_speeds: "100000, 40000" + + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/config/description" + method: "delete" + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/config/mtu" + method: "delete" + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/openconfig-if-ethernet:ethernet/config" + method: "patch" + data: + openconfig-if-ethernet:config: + auto-negotiate: false + - path: "data/openconfig-interfaces:interfaces/interface=Loopback123" + method: "delete" + +deleted_02: + module_args: + state: deleted + config: + - name: 'Eth1/1' + fec: FEC_DISABLED + auto_negotiate: true + speed: SPEED_100GB + advertised_speed: + - 100000 + - name: 'Loopback123' + existing_interfaces_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - name: 'Eth1/1' + config: + mtu: 6767 + openconfig-if-ethernet:ethernet: + config: + auto-negotiate: true + port-speed: openconfig-if-ethernet:SPEED_40GB + openconfig-if-ethernet-ext2:port-fec: FEC_FC + openconfig-if-ethernet-ext2:advertised-speed: '100000,40000' + - name: 'Loopback123' + config: + enabled: false + description: 'Test Desc for Loopback123' + - path: "data/openconfig-port-group:port-groups" + response: + code: 200 + value: + openconfig-port-group:port-groups: + port-group: + - state: + member-if-end: "Eth1/4" + member-if-start: "Eth1/2" + - path: "data/sonic-port:sonic-port/PORT/PORT_LIST=Eth1%2F1/valid_speeds" + response: + code: 200 + value: + sonic-port:valid_speeds: "100000, 40000" + + + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/openconfig-if-ethernet:ethernet/config" + method: "patch" + data: + openconfig-if-ethernet:config: + auto-negotiate: false + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/openconfig-if-ethernet:ethernet/config" + method: "patch" + data: + openconfig-if-ethernet:config: + openconfig-if-ethernet-ext2:port-fec: "FEC_DISABLED" + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/openconfig-if-ethernet:ethernet/config" + method: "patch" + data: + openconfig-if-ethernet:config: + openconfig-if-ethernet-ext2:advertised-speed: '40000' + - path: "data/openconfig-interfaces:interfaces/interface=Loopback123" + method: "delete" +replaced_01: + module_args: + state: replaced + config: + - name: Eth1/1 + mtu: 1600 + speed: "SPEED_40GB" + existing_interfaces_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - name: 'Eth1/1' + config: + description: 'test descr for eth1/1' + mtu: 2000 + enabled: false + openconfig-if-ethernet:ethernet: + config: + openconfig-if-ethernet-ext2:advertised-speed: '' + auto-negotiate: true + openconfig-if-ethernet-ext2:port-fec: "openconfig-platform-types:FEC_DISABLED" + port-speed: "openconfig-if-ethernet:SPEED_25GB" + - name: 'Eth1/2' + config: + mtu: 6767 + openconfig-if-ethernet:ethernet: + config: + auto-negotiate: true + port-speed: openconfig-if-ethernet:SPEED_40GB + openconfig-if-ethernet-ext2:port-fec: FEC_FC + openconfig-if-ethernet-ext2:advertised-speed: '100000,40000' + - name: 'Loopback123' + config: + enabled: false + description: 'Test Desc for Loopback123' + - path: "data/openconfig-port-group:port-groups" + response: + code: 200 + value: + openconfig-port-group:port-groups: + port-group: + - state: + member-if-end: "Eth1/4" + member-if-start: "Eth1/3" + - path: "data/sonic-port:sonic-port/PORT/PORT_LIST=Eth1%2F1/valid_speeds" + response: + code: 200 + value: + sonic-port:valid_speeds: "100000, 40000" + - path: "data/sonic-port:sonic-port/PORT/PORT_LIST=Eth1%2F2/valid_speeds" + response: + code: 200 + value: + sonic-port:valid_speeds: "100000, 40000" + + + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/config" + method: "patch" + data: + openconfig-interfaces:config: + mtu: 1600 + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/config/description" + method: "delete" + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/openconfig-if-ethernet:ethernet/config" + method: "patch" + data: + openconfig-if-ethernet:config: + auto-negotiate: false + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/openconfig-if-ethernet:ethernet/config" + method: "patch" + data: + openconfig-if-ethernet:config: + port-speed: "openconfig-if-ethernet:SPEED_40GB" + +overridden_01: + module_args: + state: overridden + config: + - name: Eth1/1 + mtu: 1600 + speed: "SPEED_40GB" + existing_interfaces_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - name: 'Eth1/1' + config: + mtu: 2000 + openconfig-if-ethernet:ethernet: + config: + openconfig-if-ethernet-ext2:advertised-speed: '' + auto-negotiate: true + openconfig-if-ethernet-ext2:port-fec: "openconfig-platform-types:FEC_DISABLED" + port-speed: "openconfig-if-ethernet:SPEED_25GB" + - name: 'Eth1/2' + config: + mtu: 2000 + openconfig-if-ethernet:ethernet: + config: + openconfig-if-ethernet-ext2:advertised-speed: '' + auto-negotiate: true + openconfig-if-ethernet-ext2:port-fec: "openconfig-platform-types:FEC_DISABLED" + port-speed: "openconfig-if-ethernet:SPEED_25GB" + - path: "data/openconfig-port-group:port-groups" + response: + code: 200 + value: + openconfig-port-group:port-groups: + port-group: + - state: + member-if-end: "Eth1/4" + member-if-start: "Eth1/3" + - path: "data/sonic-port:sonic-port/PORT/PORT_LIST=Eth1%2F1/valid_speeds" + response: + code: 200 + value: + sonic-port:valid_speeds: "100000, 40000" + - path: "data/sonic-port:sonic-port/PORT/PORT_LIST=Eth1%2F2/valid_speeds" + response: + code: 200 + value: + sonic-port:valid_speeds: "100000, 40000" + + + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/config" + method: "patch" + data: + openconfig-interfaces:config: + mtu: 1600 + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/config/enabled" + method: "delete" + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/openconfig-if-ethernet:ethernet/config" + method: "patch" + data: + openconfig-if-ethernet:config: + auto-negotiate: false + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/openconfig-if-ethernet:ethernet/config" + method: "patch" + data: + openconfig-if-ethernet:config: + port-speed: "openconfig-if-ethernet:SPEED_40GB" + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F2/config/enabled" + method: "delete" + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F2/config/mtu" + method: "delete" + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F2/openconfig-if-ethernet:ethernet/config" + method: "patch" + data: + openconfig-if-ethernet:config: + auto-negotiate: false diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_ip_neighbor.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_ip_neighbor.yaml new file mode 100644 index 000000000..5c0ba555a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_ip_neighbor.yaml @@ -0,0 +1,146 @@ +--- +merged_01: + module_args: + config: + ipv4_arp_timeout: 1200 + ipv4_drop_neighbor_aging_time: 600 + ipv6_drop_neighbor_aging_time: 600 + ipv6_nd_cache_expiry: 1200 + num_local_neigh: 1000 + existing_ip_neighbor_config: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config" + response: + code: 200 + value: + openconfig-neighbor:config: + ipv4-arp-timeout: 1800 + ipv6-nd-cache-expiry: 1800 + name: Values + expected_config_requests: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config" + method: "patch" + data: + config: + ipv4-arp-timeout: 1200 + ipv4-drop-neighbor-aging-time: 600 + ipv6-drop-neighbor-aging-time: 600 + ipv6-nd-cache-expiry: 1200 + num-local-neigh: 1000 + +deleted_01: + module_args: + state: deleted + existing_ip_neighbor_config: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config" + response: + code: 200 + value: + openconfig-neighbor:config: + ipv4-arp-timeout: 1200 + ipv6-nd-cache-expiry: 1200 + ipv4-drop-neighbor-aging-time: 600 + ipv6-drop-neighbor-aging-time: 600 + num-local-neigh: 1000 + name: Values + expected_config_requests: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + ipv4_arp_timeout: 1200 + ipv4_drop_neighbor_aging_time: 600 + ipv6_drop_neighbor_aging_time: 600 + ipv6_nd_cache_expiry: 1200 + num_local_neigh: 1000 + existing_ip_neighbor_config: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config" + response: + code: 200 + value: + openconfig-neighbor:config: + ipv4-arp-timeout: 1200 + ipv6-nd-cache-expiry: 1200 + ipv4-drop-neighbor-aging-time: 600 + ipv6-drop-neighbor-aging-time: 600 + num-local-neigh: 1000 + name: Values + expected_config_requests: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config/ipv4-arp-timeout" + method: "delete" + data: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config/ipv4-drop-neighbor-aging-time" + method: "delete" + data: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config/ipv6-drop-neighbor-aging-time" + method: "delete" + data: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config/ipv6-nd-cache-expiry" + method: "delete" + data: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config/num-local-neigh" + method: "delete" + data: + +replaced_01: + module_args: + state: replaced + config: + ipv4_arp_timeout: 1201 + ipv4_drop_neighbor_aging_time: 601 + ipv6_nd_cache_expiry: 1201 + num_local_neigh: 1001 + existing_ip_neighbor_config: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config" + response: + code: 200 + value: + openconfig-neighbor:config: + ipv4-arp-timeout: 1200 + ipv6-nd-cache-expiry: 1200 + ipv4-drop-neighbor-aging-time: 600 + ipv6-drop-neighbor-aging-time: 600 + num-local-neigh: 1000 + name: Values + expected_config_requests: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config" + method: "patch" + data: + config: + ipv4-arp-timeout: 1201 + ipv4-drop-neighbor-aging-time: 601 + ipv6-nd-cache-expiry: 1201 + num-local-neigh: 1001 + +overridden_01: + module_args: + state: overridden + config: + ipv4_drop_neighbor_aging_time: 602 + ipv6_drop_neighbor_aging_time: 602 + num_local_neigh: 1002 + existing_ip_neighbor_config: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config" + response: + code: 200 + value: + openconfig-neighbor:config: + ipv4-arp-timeout: 1200 + ipv6-nd-cache-expiry: 1200 + ipv4-drop-neighbor-aging-time: 600 + ipv6-drop-neighbor-aging-time: 600 + num-local-neigh: 1000 + name: Values + expected_config_requests: + - path: "data/openconfig-neighbor:neighbor-globals/neighbor-global=Values/config" + method: "patch" + data: + config: + ipv4-arp-timeout: 180 + ipv4-drop-neighbor-aging-time: 602 + ipv6-drop-neighbor-aging-time: 602 + ipv6-nd-cache-expiry: 180 + num-local-neigh: 1002 diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_l2_acls.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_l2_acls.yaml new file mode 100644 index 000000000..a02aabcf0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_l2_acls.yaml @@ -0,0 +1,962 @@ +--- +merged_01: + module_args: + state: 'merged' + config: + - name: 'acl1' + remark: 'L2 ACL 1' + rules: + - sequence_num: 1 + action: 'permit' + source: + host: '11:11:11:11:11:11' + destination: + host: '11:11:11:22:22:22' + remark: 'Rule1' + - sequence_num: 2 + action: 'transit' + source: + address: '00:00:10:00:00:00' + address_mask: '00:00:ff:ff:00:00' + destination: + any: true + vlan_id: 100 + - sequence_num: 3 + action: 'transit' + source: + any: true + destination: + address: '00:00:00:00:10:00' + address_mask: '00:00:00:00:ff:ff' + ethertype: + value: '0x800' + facts_get_requests: + - path: 'data/openconfig-acl:acl/acl-sets' + response: + code: 200 + value: {} + config_requests: + - path: 'data/openconfig-acl:acl/acl-sets/acl-set' + method: 'patch' + data: + acl-set: + - name: 'acl1' + type: 'ACL_L2' + config: + name: 'acl1' + type: 'ACL_L2' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl1,ACL_L2/config/description' + method: 'patch' + data: + description: 'L2 ACL 1' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl1,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + description: 'Rule1' + actions: + config: + forwarding-action: 'ACCEPT' + l2: + config: + source-mac: '11:11:11:11:11:11' + destination-mac: '11:11:11:22:22:22' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl1,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'TRANSIT' + l2: + config: + source-mac: '00:00:10:00:00:00' + source-mac-mask: '00:00:ff:ff:00:00' + vlanid: 100 + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl1,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 3 + config: + sequence-id: 3 + actions: + config: + forwarding-action: 'TRANSIT' + l2: + config: + destination-mac: '00:00:00:00:10:00' + destination-mac-mask: '00:00:00:00:ff:ff' + ethertype: 'ETHERTYPE_IPV4' + +merged_02: + module_args: + state: 'merged' + config: + - name: 'acl1' + remark: 'Remark_ACL1' + rules: + - sequence_num: 4 + action: 'discard' + source: + any: true + destination: + any: true + ethertype: + arp: true + - sequence_num: 5 + action: 'discard' + source: + any: true + destination: + any: true + vlan_tag_format: + multi_tagged: true + remark: 'VLAN_multi_tagged' + - name: 'acl2' + remark: 'Remark_ACL2' + rules: + - sequence_num: 1 + action: 'permit' + source: + any: true + destination: + any: true + ethertype: + ipv6: true + vlan_id: 200 + - sequence_num: 2 + action: 'permit' + source: + any: true + destination: + any: true + dei: 1 + - sequence_num: 3 + action: 'permit' + source: + any: true + destination: + any: true + pcp: + value: 4 + mask: 6 + - sequence_num: 4 + action: 'do-not-nat' + source: + any: true + destination: + any: true + ethertype: + ipv4: true + facts_get_requests: + - path: 'data/openconfig-acl:acl/acl-sets' + response: + code: 200 + value: + openconfig-acl:acl-sets: + acl-set: + - config: + description: 'L2 ACL 1' + name: 'acl1' + type: 'openconfig-acl:ACL_L2' + name: 'acl1' + type: 'openconfig-acl:ACL_L2' + acl-entries: + acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + config: + description: 'Rule1' + sequence-id: 1 + l2: + config: + destination-mac: '11:11:11:22:22:22' + source-mac: '11:11:11:11:11:11' + - sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + config: + sequence-id: 2 + l2: + config: + openconfig-acl-ext:vlanid: 100 + source-mac: '00:00:10:00:00:00' + source-mac-mask: '00:00:ff:ff:00:00' + - sequence-id: 3 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + config: + sequence-id: 3 + l2: + config: + destination-mac: '00:00:00:00:10:00' + destination-mac-mask: '00:00:00:00:ff:ff' + ethertype: 2114 + config_requests: + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl1,ACL_L2/config/description' + method: 'patch' + data: + description: 'Remark_ACL1' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl1,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 4 + actions: + config: + forwarding-action: 'DISCARD' + config: + sequence-id: 4 + l2: + config: + ethertype: 'ETHERTYPE_ARP' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl1,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 5 + actions: + config: + forwarding-action: 'DISCARD' + config: + description: 'VLAN_multi_tagged' + sequence-id: 5 + l2: + config: + vlan-tag-format: 'openconfig-acl-ext:MULTI_TAGGED' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set' + method: 'patch' + data: + acl-set: + - name: 'acl2' + type: 'ACL_L2' + config: + name: 'acl2' + type: 'ACL_L2' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl2,ACL_L2/config/description' + method: 'patch' + data: + description: 'Remark_ACL2' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl2,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + config: + sequence-id: 1 + l2: + config: + ethertype: 'ETHERTYPE_IPV6' + vlanid: 200 + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl2,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 2 + actions: + config: + forwarding-action: 'ACCEPT' + config: + sequence-id: 2 + l2: + config: + dei: 1 + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl2,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 3 + actions: + config: + forwarding-action: 'ACCEPT' + config: + sequence-id: 3 + l2: + config: + pcp: 4 + pcp-mask: 6 + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl2,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 4 + actions: + config: + forwarding-action: 'DO_NOT_NAT' + config: + sequence-id: 4 + l2: + config: + ethertype: 'ETHERTYPE_IPV4' + +replaced_01: + module_args: + state: 'replaced' + config: + - name: 'acl2' + rules: + - sequence_num: 1 + action: 'permit' + source: + any: true + destination: + host: '11:11:11:22:22:22' + - sequence_num: 2 + action: 'deny' + source: + any: true + destination: + any: true + ethertype: + ipv4: true + vlan_id: 100 + - name: 'acl3' + rules: + - sequence_num: 1 + action: 'permit' + source: + host: '22:22:22:22:22:22' + destination: + any: true + pcp: + traffic_type: 'ca' + - sequence_num: 2 + action: 'deny' + source: + any: true + destination: + any: true + remark: 'Deny_All' + facts_get_requests: + - path: 'data/openconfig-acl:acl/acl-sets' + response: + code: 200 + value: + openconfig-acl:acl-sets: + acl-set: + - name: 'acl1' + type: 'openconfig-acl:ACL_L2' + config: + description: 'Remark_ACL1' + name: 'acl1' + type: 'openconfig-acl:ACL_L2' + acl-entries: + acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + config: + description: 'Rule1' + sequence-id: 1 + l2: + config: + destination-mac: '11:11:11:22:22:22' + source-mac: '11:11:11:11:11:11' + - sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + config: + sequence-id: 2 + l2: + config: + openconfig-acl-ext:vlanid: 100 + source-mac: '00:00:10:00:00:00' + source-mac-mask: '00:00:ff:ff:00:00' + - sequence-id: 3 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + config: + sequence-id: 3 + l2: + config: + destination-mac: '00:00:00:00:10:00' + destination-mac-mask: '00:00:00:00:ff:ff' + ethertype: 2114 + - name: 'acl2' + type: 'openconfig-acl:ACL_L2' + config: + description: 'Remark_ACL2' + name: 'acl2' + type: 'openconfig-acl:ACL_L2' + acl-entries: + acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + config: + sequence-id: 1 + l2: + config: + ethertype: 'openconfig-packet-match-types:ETHERTYPE_IPV6' + openconfig-acl-ext:vlanid: 200 + - sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + config: + sequence-id: 2 + l2: + config: + openconfig-acl-ext:dei: 1 + - sequence-id: 3 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + config: + sequence-id: 3 + l2: + config: + openconfig-acl-ext:pcp: 4 + openconfig-acl-ext:pcp-mask: 6 + config_requests: + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl2,ACL_L2/config/description' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl2,ACL_L2/acl-entries/acl-entry=1' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl2,ACL_L2/acl-entries/acl-entry=2' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl2,ACL_L2/acl-entries/acl-entry=3' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl2,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + l2: + config: + destination-mac: '11:11:11:22:22:22' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl2,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'DROP' + l2: + config: + ethertype: 'ETHERTYPE_IPV4' + vlanid: 100 + - path: 'data/openconfig-acl:acl/acl-sets/acl-set' + method: 'patch' + data: + acl-set: + - name: 'acl3' + type: 'ACL_L2' + config: + name: 'acl3' + type: 'ACL_L2' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl3,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + l2: + config: + pcp: 3 + source-mac: '22:22:22:22:22:22' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl3,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 2 + config: + description: 'Deny_All' + sequence-id: 2 + actions: + config: + forwarding-action: 'DROP' + l2: + config: {} + +overridden_01: + module_args: + state: 'overridden' + config: + - name: 'acl1' + rules: + - sequence_num: 1 + action: 'permit' + source: + host: '33:33:33:33:33:33' + destination: + host: '44:44:44:44:44:44' + - name: 'test-acl' + remark: 'test_mac_acl' + rules: + - sequence_num: 1 + action: 'permit' + source: + host: '22:22:22:22:22:22' + destination: + any: true + vlan_id: 20 + - sequence_num: 2 + action: 'permit' + source: + any: true + destination: + any: true + ethertype: + value: '0x88cc' + remark: 'LLDP' + - sequence_num: 3 + action: 'permit' + source: + any: true + destination: + address: '00:00:10:00:00:00' + address_mask: '00:00:ff:ff:00:00' + pcp: + value: 4 + mask: 6 + - name: 'test-acl-1' + remark: 'test_mac_acl_1' + rules: + - sequence_num: 1 + action: 'permit' + source: + any: true + destination: + any: true + ethertype: + arp: true + vlan_id: 200 + - sequence_num: 2 + action: 'discard' + source: + any: true + destination: + any: true + ethertype: + value: 0x8035 + pcp: + value: 5 + facts_get_requests: + - path: 'data/openconfig-acl:acl/acl-sets' + response: + code: 200 + value: + openconfig-acl:acl-sets: + acl-set: + - name: 'acl1' + type: 'openconfig-acl:ACL_L2' + config: + description: 'Remark_ACL1' + name: 'acl1' + type: 'openconfig-acl:ACL_L2' + acl-entries: + acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + config: + description: 'Rule1' + sequence-id: 1 + l2: + config: + destination-mac: '11:11:11:22:22:22' + source-mac: '11:11:11:11:11:11' + - sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + config: + sequence-id: 2 + l2: + config: + openconfig-acl-ext:vlanid: 100 + source-mac: '00:00:10:00:00:00' + source-mac-mask: '00:00:ff:ff:00:00' + - sequence-id: 3 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + config: + sequence-id: 3 + l2: + config: + destination-mac: '00:00:00:00:10:00' + destination-mac-mask: '00:00:00:00:ff:ff' + ethertype: 2114 + - name: 'acl2' + type: 'openconfig-acl:ACL_L2' + config: + name: 'acl2' + type: 'openconfig-acl:ACL_L2' + acl-entries: + acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + config: + sequence-id: 1 + l2: + config: + destination-mac: '11:11:11:22:22:22' + - sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl:DROP' + config: + sequence-id: 2 + l2: + config: + ethertype: 'openconfig-packet-match-types:ETHERTYPE_IPV4' + openconfig-acl-ext:vlanid: 100 + - name: 'acl3' + type: 'openconfig-acl:ACL_L2' + config: + name: 'acl3' + type: 'openconfig-acl:ACL_L2' + acl-entries: + acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + config: + sequence-id: 1 + l2: + config: + openconfig-acl-ext:pcp: 3 + source-mac: '22:22:22:22:22:22' + - sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl:DROP' + config: + description: 'Deny_All' + sequence-id: 2 + config_requests: + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl2,ACL_L2' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl3,ACL_L2' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl1,ACL_L2/config/description' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl1,ACL_L2/acl-entries/acl-entry=2' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl1,ACL_L2/acl-entries/acl-entry=3' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl1,ACL_L2/acl-entries/acl-entry=1' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=test-acl-1,ACL_L2/config/description' + method: 'patch' + data: + description: 'test_mac_acl_1' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl1,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + l2: + config: + destination-mac: '44:44:44:44:44:44' + source-mac: '33:33:33:33:33:33' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set' + method: 'patch' + data: + acl-set: + - name: 'test-acl' + type: 'ACL_L2' + config: + name: 'test-acl' + type: 'ACL_L2' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=test-acl,ACL_L2/config/description' + method: 'patch' + data: + description: 'test_mac_acl' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=test-acl,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + l2: + config: + source-mac: '22:22:22:22:22:22' + vlanid: 20 + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=test-acl,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 2 + config: + description: 'LLDP' + sequence-id: 2 + actions: + config: + forwarding-action: 'ACCEPT' + l2: + config: + ethertype: 'ETHERTYPE_LLDP' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=test-acl,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 3 + config: + sequence-id: 3 + actions: + config: + forwarding-action: 'ACCEPT' + l2: + config: + destination-mac: '00:00:10:00:00:00' + destination-mac-mask: '00:00:ff:ff:00:00' + pcp: 4 + pcp-mask: 6 + - path: 'data/openconfig-acl:acl/acl-sets/acl-set' + method: 'patch' + data: + acl-set: + - name: 'test-acl-1' + type: 'ACL_L2' + config: + name: 'test-acl-1' + type: 'ACL_L2' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=test-acl-1,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + l2: + config: + ethertype: 'ETHERTYPE_ARP' + vlanid: 200 + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=test-acl-1,ACL_L2/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'DISCARD' + l2: + config: + ethertype: 32821 + pcp: 5 + +deleted_01: + module_args: + state: 'deleted' + config: + - name: 'acl1' + - name: 'test-acl' + rules: + - sequence_num: 3 + - name: 'test-acl-1' + remark: 'test_mac_acl_1' + rules: + - sequence_num: 2 + facts_get_requests: + - path: 'data/openconfig-acl:acl/acl-sets' + response: + code: 200 + value: + openconfig-acl:acl-sets: + acl-set: + - name: 'acl1' + type: 'openconfig-acl:ACL_L2' + config: + name: 'acl1' + type: 'openconfig-acl:ACL_L2' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + l2: + config: + destination-mac: '44:44:44:44:44:44' + source-mac: '33:33:33:33:33:33' + - name: 'test-acl' + type: 'openconfig-acl:ACL_L2' + config: + description: 'test_mac_acl' + name: 'test-acl' + type: 'openconfig-acl:ACL_L2' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + l2: + config: + openconfig-acl-ext:vlanid: 20 + source-mac: '22:22:22:22:22:22' + - sequence-id: 2 + config: + description: 'LLDP' + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + l2: + config: + ethertype: 'openconfig-packet-match-types:ETHERTYPE_LLDP' + - sequence-id: 3 + config: + sequence-id: 3 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + l2: + config: + destination-mac: '00:00:10:00:00:00' + destination-mac-mask: '00:00:ff:ff:00:00' + openconfig-acl-ext:pcp: 4 + openconfig-acl-ext:pcp-mask: 6 + - name: 'test-acl-1' + type: 'openconfig-acl:ACL_L2' + config: + description: 'test_mac_acl_1' + name: 'test-acl-1' + type: 'openconfig-acl:ACL_L2' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + l2: + config: + ethertype: 'openconfig-packet-match-types:ETHERTYPE_ARP' + openconfig-acl-ext:vlanid: 200 + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl-ext:DISCARD' + config_requests: + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=acl1,ACL_L2' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=test-acl,ACL_L2/acl-entries/acl-entry=3' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=test-acl-1,ACL_L2/config/description' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=test-acl-1,ACL_L2/acl-entries/acl-entry=2' + method: 'delete' + + +deleted_02: + module_args: + state: 'deleted' + config: [] + facts_get_requests: + - path: 'data/openconfig-acl:acl/acl-sets' + response: + code: 200 + value: + openconfig-acl:acl-sets: + acl-set: + - name: 'test-acl' + type: 'openconfig-acl:ACL_L2' + config: + description: 'test_mac_acl' + name: 'test-acl' + type: 'openconfig-acl:ACL_L2' + acl-entries: + acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + config: + sequence-id: 1 + l2: + config: + openconfig-acl-ext:vlanid: 20 + destination-mac: '11:11:11:11:11:11' + destination-mac-mask: 'ff:ff:ff:ff:ff:ff' + source-mac: '22:22:22:22:22:22' + source-mac-mask: 'ff:ff:ff:ff:ff:ff' + - sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + config: + description: 'LLDP' + sequence-id: 2 + l2: + config: + ethertype: 'openconfig-packet-match-types:ETHERTYPE_LLDP' + - name: 'test-acl-1' + type: 'openconfig-acl:ACL_L2' + config: + name: 'test-acl-1' + type: 'openconfig-acl:ACL_L2' + acl-entries: + acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + config: + sequence-id: 1 + l2: + config: + openconfig-acl-ext:vlan-tag-format: 'openconfig-acl-ext:MULTI_TAGGED' + config_requests: + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=test-acl,ACL_L2' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=test-acl-1,ACL_L2' + method: 'delete' diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_l2_interfaces.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_l2_interfaces.yaml new file mode 100644 index 000000000..fc7d17067 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_l2_interfaces.yaml @@ -0,0 +1,187 @@ +--- +merged_01: + module_args: + config: + - name: Eth1/1 + access: + vlan: 10 + trunk: + allowed_vlans: + - vlan: '11' + - vlan: '12' + - vlan: '13' + - vlan: '14' + - vlan: '16' + - vlan: '21-30' + - name: Mgmt1/1/1 + access: + vlan: 100 + - name: PortChannel200 + access: + vlan: 200 + - name: Eth1/12 + existing_l2_interfaces_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - name: 'Eth1/1' + config: + mtu: 2000 + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" + method: "patch" + data: + openconfig-vlan:config: + access-vlan: 10 + trunk-vlans: + - 11 + - 12 + - 13 + - 14 + - 16 + - '21..30' + - path: "data/openconfig-interfaces:interfaces/interface=PortChannel200/openconfig-if-aggregate:aggregation/openconfig-vlan:switched-vlan/config" + method: "patch" + data: + openconfig-vlan:config: + access-vlan: 200 +merged_02: + module_args: + config: + - name: Eth1/1 + access: + vlan: 10 + trunk: + allowed_vlans: + - vlan: 14-20 + - name: Eth1/2 + access: + vlan: 30 + trunk: + allowed_vlans: + - vlan: 21 + existing_l2_interfaces_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - name: Eth1/1 + openconfig-if-ethernet:ethernet: + openconfig-vlan:switched-vlan: + config: + access-vlan: 10 + interface-mode: TRUNK + trunk-vlans: + - "11..15" + - name: Eth1/2 + openconfig-if-ethernet:ethernet: + openconfig-vlan:switched-vlan: + config: + access-vlan: 20 + interface-mode: TRUNK + trunk-vlans: + - "21..25" + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" + method: "patch" + data: + openconfig-vlan:config: + trunk-vlans: + - '16..20' + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f2/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" + method: "patch" + data: + openconfig-vlan:config: + access-vlan: 30 + +deleted_01: + module_args: + state: deleted + existing_l2_interfaces_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - name: Eth1/1 + openconfig-if-ethernet:ethernet: + openconfig-vlan:switched-vlan: + config: + access-vlan: 10 + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + - name: Eth1/1 + access: + vlan: 10 + trunk: + allowed_vlans: + - vlan: "11-13" + - vlan: 16 + - name: Eth1/2 + access: + vlan: + trunk: + allowed_vlans: + - name: Eth1/3 + existing_l2_interfaces_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - name: Eth1/1 + openconfig-if-ethernet:ethernet: + openconfig-vlan:switched-vlan: + config: + access-vlan: 10 + interface-mode: TRUNK + trunk-vlans: + - "11..20" + - name: Eth1/2 + openconfig-if-ethernet:ethernet: + openconfig-vlan:switched-vlan: + config: + access-vlan: 12 + interface-mode: TRUNK + trunk-vlans: + - "21..30" + - 51 + - name: Eth1/3 + openconfig-if-ethernet:ethernet: + openconfig-vlan:switched-vlan: + config: + access-vlan: 13 + interface-mode: TRUNK + trunk-vlans: + - "31..40" + - 61 + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config/access-vlan" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config/trunk-vlans=11..13%2C16" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f2/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config/access-vlan" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f2/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config/trunk-vlans=21..30%2C51" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f3/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_l3_acls.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_l3_acls.yaml new file mode 100644 index 000000000..314c803c1 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_l3_acls.yaml @@ -0,0 +1,1290 @@ +--- +merged_01: + module_args: + state: 'merged' + config: + - address_family: 'ipv4' + acls: + - name: 'ip-acl1' + remark: 'IPv4 ACL 1' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'ip' + source: + host: '192.168.1.2' + destination: + host: '192.168.2.2' + remark: 'Host-Rule' + - sequence_num: 2 + action: 'transit' + protocol: + name: 'icmp' + source: + prefix: '192.168.0.0/16' + destination: + any: true + protocol_options: + icmp: + type: 8 + vlan_id: 100 + - address_family: 'ipv6' + acls: + - name: 'ipv6-acl1' + remark: 'IPv6 ACL 1' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'ipv6' + source: + prefix: '192::/64' + destination: + any: true + - sequence_num: 2 + action: 'permit' + protocol: + name: 'tcp' + source: + any: true + port_number: + gt: 1024 + destination: + host: '192::2' + port_number: + eq: 80 + facts_get_requests: + - path: 'data/openconfig-acl:acl/acl-sets' + response: + code: 200 + value: {} + config_requests: + - path: 'data/openconfig-acl:acl/acl-sets/acl-set' + method: 'patch' + data: + acl-set: + - name: 'ip-acl1' + type: 'ACL_IPV4' + config: + name: 'ip-acl1' + type: 'ACL_IPV4' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl1,ACL_IPV4/config/description' + method: 'patch' + data: + description: 'IPv4 ACL 1' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl1,ACL_IPV4/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + config: + description: 'Host-Rule' + sequence-id: 1 + ipv4: + config: + destination-address: '192.168.2.2/32' + source-address: '192.168.1.2/32' + transport: + config: {} + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl1,ACL_IPV4/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 2 + actions: + config: + forwarding-action: 'TRANSIT' + config: + sequence-id: 2 + ipv4: + config: + protocol: 'IP_ICMP' + source-address: '192.168.0.0/16' + l2: + config: + vlanid: 100 + transport: + config: + icmp-type: 8 + - path: 'data/openconfig-acl:acl/acl-sets/acl-set' + method: 'patch' + data: + acl-set: + - name: 'ipv6-acl1' + type: 'ACL_IPV6' + config: + name: 'ipv6-acl1' + type: 'ACL_IPV6' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/config/description' + method: 'patch' + data: + description: 'IPv6 ACL 1' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + config: + sequence-id: 1 + ipv6: + config: + source-address: '192::/64' + transport: + config: {} + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 2 + actions: + config: + forwarding-action: 'ACCEPT' + config: + sequence-id: 2 + ipv6: + config: + destination-address: '192::2/128' + protocol: 'IP_TCP' + transport: + config: + destination-port: 80 + source-port: '1024..65535' + +merged_02: + module_args: + state: 'merged' + config: + - address_family: 'ipv4' + acls: + - name: 'ip-acl1' + remark: 'IPv4 ACL 1' + rules: + - sequence_num: 3 + action: 'deny' + protocol: + number: 17 + source: + host: '192.168.1.2' + destination: + prefix: '192.168.1.0/24' + port_number: + lt: 1024 + remark: "Drop UDP" + - name: 'ip-acl2' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'tcp' + source: + any: true + destination: + any: true + protocol_options: + tcp: + ack: true + syn: true + fin: true + - sequence_num: 2 + action: 'permit' + protocol: + number: 2 + source: + any: true + destination: + any: true + dscp: + voice_admit: true + - sequence_num: 3 + action: 'discard' + protocol: + name: 'icmp' + source: + any: true + destination: + any: true + - address_family: 'ipv6' + acls: + - name: 'ipv6-acl1' + remark: 'Updated IPv6 ACL 1' + rules: + - sequence_num: 3 + action: 'deny' + protocol: + name: 'udp' + source: + any: true + destination: + host: '100::1' + port_number: + range: + begin: 1024 + end: 2048 + - name: 'ipv6-acl2' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'icmpv6' + source: + any: true + destination: + any: true + protocol_options: + icmpv6: + type: 128 + code: 0 + vlan_id: 200 + - sequence_num: 2 + action: 'deny' + protocol: + name: 'ipv6' + source: + host: '100::1' + destination: + any: true + vlan_id: 200 + facts_get_requests: + - path: 'data/openconfig-acl:acl/acl-sets' + response: + code: 200 + value: + openconfig-acl:acl-sets: + acl-set: + - name: 'ip-acl1' + type: 'openconfig-acl:ACL_IPV4' + config: + description: 'IPv4 ACL 1' + name: 'ip-acl1' + type: 'openconfig-acl:ACL_IPV4' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + description: 'Host-Rule' + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv4: + config: + destination-address: '192.168.2.2/32' + source-address: '192.168.1.2/32' + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + ipv4: + config: + protocol: 'openconfig-packet-match-types:IP_ICMP' + source-address: '192.168.0.0/16' + l2: + config: + openconfig-acl-ext:vlanid: 100 + transport: + config: + openconfig-acl-ext:icmp-type: 8 + - name: 'ipv6-acl1' + type: 'openconfig-acl:ACL_IPV6' + config: + description: 'IPv6 ACL 1' + name: 'ipv6-acl1' + type: 'openconfig-acl:ACL_IPV6' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + source-address: '192::/64' + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + destination-address: '192::2/128' + protocol: 'openconfig-packet-match-types:IP_TCP' + transport: + config: + destination-port: 80 + source-port: '1024..65535' + config_requests: + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl1,ACL_IPV4/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 3 + actions: + config: + forwarding-action: 'DROP' + config: + description: 'Drop UDP' + sequence-id: 3 + ipv4: + config: + destination-address: '192.168.1.0/24' + protocol: 'IP_UDP' + source-address: '192.168.1.2/32' + transport: + config: + destination-port: '0..1024' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set' + method: 'patch' + data: + acl-set: + - name: 'ip-acl2' + type: 'ACL_IPV4' + config: + name: 'ip-acl2' + type: 'ACL_IPV4' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl2,ACL_IPV4/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + config: + sequence-id: 1 + ipv4: + config: + protocol: 'IP_TCP' + transport: + config: + tcp-flags: + - 'TCP_ACK' + - 'TCP_SYN' + - 'TCP_FIN' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl2,ACL_IPV4/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 2 + actions: + config: + forwarding-action: 'ACCEPT' + config: + sequence-id: 2 + ipv4: + config: + dscp: 44 + protocol: 'IP_IGMP' + transport: + config: {} + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl2,ACL_IPV4/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 3 + actions: + config: + forwarding-action: 'DISCARD' + config: + sequence-id: 3 + ipv4: + config: + protocol: 'IP_ICMP' + transport: + config: {} + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/config/description' + method: 'patch' + data: + description: 'Updated IPv6 ACL 1' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 3 + actions: + config: + forwarding-action: 'DROP' + config: + sequence-id: 3 + ipv6: + config: + destination-address: '100::1/128' + protocol: 'IP_UDP' + transport: + config: + destination-port: '1024..2048' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set' + method: 'patch' + data: + acl-set: + - name: 'ipv6-acl2' + type: 'ACL_IPV6' + config: + name: 'ipv6-acl2' + type: 'ACL_IPV6' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl2,ACL_IPV6/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + config: + sequence-id: 1 + ipv6: + config: + protocol: 58 + l2: + config: + vlanid: 200 + transport: + config: + icmp-code: 0 + icmp-type: 128 + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl2,ACL_IPV6/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 2 + actions: + config: + forwarding-action: 'DROP' + config: + sequence-id: 2 + ipv6: + config: + source-address: '100::1/128' + l2: + config: + vlanid: 200 + transport: + config: {} + +replaced_01: + module_args: + state: 'replaced' + config: + - address_family: 'ipv4' + acls: + - name: 'ip-acl2' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'tcp' + source: + any: true + destination: + any: true + protocol_options: + tcp: + established: true + remark: 'TCP established' + - sequence_num: 2 + action: 'deny' + protocol: + name: 'icmp' + source: + any: true + destination: + any: true + protocol_options: + icmp: + code: 128 + - name: 'ip-acl3' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'ip' + source: + any: true + destination: + host: '192.168.2.2' + dscp: + value: 4 + - address_family: 'ipv6' + acls: + - name: 'ipv6-acl1' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'tcp' + source: + host: '100::2' + port_number: + gt: 0 + destination: + any: true + - sequence_num: 2 + action: 'deny' + protocol: + name: 'udp' + source: + any: true + port_number: + range: + begin: 0 + end: 2048 + destination: + any: true + port_number: + range: + begin: 8000 + end: 65535 + facts_get_requests: + - path: 'data/openconfig-acl:acl/acl-sets' + response: + code: 200 + value: + openconfig-acl:acl-sets: + acl-set: + - name: 'ip-acl1' + type: 'openconfig-acl:ACL_IPV4' + config: + description: 'IPv4 ACL 1' + name: 'ip-acl1' + type: 'openconfig-acl:ACL_IPV4' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + description: 'Host-Rule' + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv4: + config: + destination-address: '192.168.2.2/32' + source-address: '192.168.1.2/32' + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + ipv4: + config: + protocol: 'openconfig-packet-match-types:IP_ICMP' + source-address: '192.168.0.0/16' + l2: + config: + openconfig-acl-ext:vlanid: 100 + transport: + config: + openconfig-acl-ext:icmp-type: 8 + - name: 'ip-acl2' + type: 'openconfig-acl:ACL_IPV4' + config: + name: 'ip-acl2' + type: 'openconfig-acl:ACL_IPV4' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv4: + config: + protocol: 'openconfig-packet-match-types:IP_TCP' + transport: + config: + tcp-flags: + - 'openconfig-packet-match-types:TCP_FIN' + - 'openconfig-packet-match-types:TCP_SYN' + - 'openconfig-packet-match-types:TCP_ACK' + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + ipv4: + config: + dscp: 44 + protocol: 'openconfig-packet-match-types:IP_IGMP' + - name: 'ipv6-acl1' + type: 'openconfig-acl:ACL_IPV6' + config: + description: 'IPv6 ACL 1' + name: 'ipv6-acl1' + type: 'openconfig-acl:ACL_IPV6' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + source-address: '192::/64' + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + destination-address: '192::2/128' + protocol: 'openconfig-packet-match-types:IP_TCP' + transport: + config: + destination-port: 80 + source-port: '1024..65535' + - name: 'ipv6-acl2' + type: 'openconfig-acl:ACL_IPV6' + config: + name: 'ipv6-acl2' + type: 'openconfig-acl:ACL_IPV6' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + protocol: 58 + config_requests: + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl2,ACL_IPV4/acl-entries/acl-entry=1' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl2,ACL_IPV4/acl-entries/acl-entry=2' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/config/description' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/acl-entries/acl-entry=1' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/acl-entries/acl-entry=2' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl2,ACL_IPV4/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + config: + description: 'TCP established' + sequence-id: 1 + ipv4: + config: + protocol: 'IP_TCP' + transport: + config: + tcp-session-established: true + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl2,ACL_IPV4/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 2 + actions: + config: + forwarding-action: 'DROP' + config: + sequence-id: 2 + ipv4: + config: + protocol: 'IP_ICMP' + transport: + config: + icmp-code: 128 + - path: 'data/openconfig-acl:acl/acl-sets/acl-set' + method: 'patch' + data: + acl-set: + - name: 'ip-acl3' + type: 'ACL_IPV4' + config: + name: 'ip-acl3' + type: 'ACL_IPV4' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl3,ACL_IPV4/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + config: + sequence-id: 1 + ipv4: + config: + destination-address: '192.168.2.2/32' + dscp: 4 + transport: + config: {} + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + config: + sequence-id: 1 + ipv6: + config: + protocol: 'IP_TCP' + source-address: '100::2/128' + transport: + config: + source-port: '0..65535' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 2 + actions: + config: + forwarding-action: 'DROP' + config: + sequence-id: 2 + ipv6: + config: + protocol: 'IP_UDP' + transport: + config: + destination-port: '8000..65535' + source-port: '0..2048' + +overridden_01: + module_args: + state: 'overridden' + config: + - address_family: 'ipv4' + acls: + - name: 'ip-acl2' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'tcp' + source: + any: true + destination: + any: true + protocol_options: + tcp: + established: true + remark: 'TCP established' + - name: 'ip-acl3' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'ip' + source: + any: true + destination: + host: '192.168.2.2' + dscp: + value: 10 + - address_family: 'ipv6' + acls: + - name: 'ipv6-acl1' + rules: + - sequence_num: 1 + action: 'permit' + protocol: + name: 'tcp' + source: + host: '100::2' + port_number: + gt: 0 + destination: + any: true + facts_get_requests: + - path: 'data/openconfig-acl:acl/acl-sets' + response: + code: 200 + value: + openconfig-acl:acl-sets: + acl-set: + - name: 'ip-acl1' + type: 'openconfig-acl:ACL_IPV4' + config: + description: 'IPv4 ACL 1' + name: 'ip-acl1' + type: 'openconfig-acl:ACL_IPV4' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + description: 'Host-Rule' + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv4: + config: + destination-address: '192.168.2.2/32' + source-address: '192.168.1.2/32' + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + ipv4: + config: + protocol: 'openconfig-packet-match-types:IP_ICMP' + source-address: '192.168.0.0/16' + l2: + config: + openconfig-acl-ext:vlanid: 100 + transport: + config: + openconfig-acl-ext:icmp-type: 8 + - name: 'ip-acl2' + type: 'openconfig-acl:ACL_IPV4' + config: + name: 'ip-acl2' + type: 'openconfig-acl:ACL_IPV4' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + description: 'TCP established' + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv4: + config: + protocol: 'openconfig-packet-match-types:IP_TCP' + transport: + config: + openconfig-acl-ext:tcp-session-established: true + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + ipv4: + config: + dscp: 44 + protocol: 'openconfig-packet-match-types:IP_IGMP' + - name: 'ipv6-acl1' + type: 'openconfig-acl:ACL_IPV6' + config: + description: 'IPv6 ACL 1' + name: 'ipv6-acl1' + type: 'openconfig-acl:ACL_IPV6' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + source-address: '192::/64' + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + destination-address: '192::2/128' + protocol: 'openconfig-packet-match-types:IP_TCP' + transport: + config: + destination-port: 80 + source-port: '1024..65535' + - name: 'ipv6-acl2' + type: 'openconfig-acl:ACL_IPV6' + config: + name: 'ipv6-acl2' + type: 'openconfig-acl:ACL_IPV6' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + protocol: 58 + config_requests: + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl1,ACL_IPV4' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl2,ACL_IPV6' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl2,ACL_IPV4/acl-entries/acl-entry=2' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/config/description' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/acl-entries/acl-entry=1' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/acl-entries/acl-entry=2' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set' + method: 'patch' + data: + acl-set: + - name: 'ip-acl3' + type: 'ACL_IPV4' + config: + name: 'ip-acl3' + type: 'ACL_IPV4' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl3,ACL_IPV4/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + config: + sequence-id: 1 + ipv4: + config: + destination-address: '192.168.2.2/32' + dscp: 10 + transport: + config: {} + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6/acl-entries' + method: 'post' + data: + openconfig-acl:acl-entry: + - sequence-id: 1 + actions: + config: + forwarding-action: 'ACCEPT' + config: + sequence-id: 1 + ipv6: + config: + protocol: 'IP_TCP' + source-address: '100::2/128' + transport: + config: + source-port: '0..65535' + +deleted_01: + module_args: + state: 'deleted' + config: + - address_family: 'ipv4' + acls: + - name: 'ip-acl1' + remark: 'IPv4 ACL 1' + - name: 'ip-acl2' + - name: 'ip-acl3' + rules: + - sequence_num: 1 + - address_family: 'ipv6' + facts_get_requests: + - path: 'data/openconfig-acl:acl/acl-sets' + response: + code: 200 + value: + openconfig-acl:acl-sets: + acl-set: + - name: 'ip-acl1' + type: 'openconfig-acl:ACL_IPV4' + config: + description: 'IPv4 ACL 1' + name: 'ip-acl1' + type: 'openconfig-acl:ACL_IPV4' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + description: 'Host-Rule' + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv4: + config: + destination-address: '192.168.2.2/32' + source-address: '192.168.1.2/32' + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + ipv4: + config: + protocol: 'openconfig-packet-match-types:IP_ICMP' + source-address: '192.168.0.0/16' + l2: + config: + openconfig-acl-ext:vlanid: 100 + transport: + config: + openconfig-acl-ext:icmp-type: 8 + - name: 'ip-acl2' + type: 'openconfig-acl:ACL_IPV4' + config: + name: 'ip-acl2' + type: 'openconfig-acl:ACL_IPV4' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + description: 'TCP established' + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv4: + config: + protocol: 'openconfig-packet-match-types:IP_TCP' + transport: + config: + openconfig-acl-ext:tcp-session-established: true + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + ipv4: + config: + dscp: 44 + protocol: 'openconfig-packet-match-types:IP_IGMP' + - acl-entries: + acl-entry: + - actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + config: + sequence-id: 1 + ipv4: + config: + destination-address: '192.168.2.2/32' + dscp: 4 + sequence-id: 1 + - actions: + config: + forwarding-action: 'openconfig-acl-ext:DISCARD' + config: + sequence-id: 2 + ipv4: + config: + protocol: 'openconfig-packet-match-types:IP_TCP' + sequence-id: 2 + transport: + config: + tcp-flags: + - 'openconfig-packet-match-types:TCP_PSH' + - 'openconfig-packet-match-types:TCP_URG' + config: + name: 'ip-acl3' + type: 'openconfig-acl:ACL_IPV4' + name: 'ip-acl3' + type: 'openconfig-acl:ACL_IPV4' + - name: 'ipv6-acl1' + type: 'openconfig-acl:ACL_IPV6' + config: + description: 'IPv6 ACL 1' + name: 'ipv6-acl1' + type: 'openconfig-acl:ACL_IPV6' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + source-address: '192::/64' + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + destination-address: '192::2/128' + protocol: 'openconfig-packet-match-types:IP_TCP' + transport: + config: + destination-port: 80 + source-port: '1024..65535' + - name: 'ipv6-acl2' + type: 'openconfig-acl:ACL_IPV6' + config: + name: 'ipv6-acl2' + type: 'openconfig-acl:ACL_IPV6' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + protocol: 58 + config_requests: + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl2,ACL_IPV4' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl2,ACL_IPV6' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl1,ACL_IPV4/config/description' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl3,ACL_IPV4/acl-entries/acl-entry=1' + method: 'delete' + +deleted_02: + module_args: + state: 'deleted' + config: [] + facts_get_requests: + - path: 'data/openconfig-acl:acl/acl-sets' + response: + code: 200 + value: + openconfig-acl:acl-sets: + acl-set: + - name: 'ip-acl1' + type: 'openconfig-acl:ACL_IPV4' + config: + description: 'IPv4 ACL 1' + name: 'ip-acl1' + type: 'openconfig-acl:ACL_IPV4' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + description: 'Host-Rule' + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv4: + config: + destination-address: '192.168.2.2/32' + source-address: '192.168.1.2/32' + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl-ext:TRANSIT' + ipv4: + config: + protocol: 'openconfig-packet-match-types:IP_ICMP' + source-address: '192.168.0.0/16' + l2: + config: + openconfig-acl-ext:vlanid: 100 + transport: + config: + openconfig-acl-ext:icmp-type: 8 + - name: 'ip-acl2' + type: 'openconfig-acl:ACL_IPV4' + config: + name: 'ip-acl2' + type: 'openconfig-acl:ACL_IPV4' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + description: 'TCP established' + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv4: + config: + protocol: 'openconfig-packet-match-types:IP_TCP' + transport: + config: + openconfig-acl-ext:tcp-session-established: true + - name: 'ipv6-acl1' + type: 'openconfig-acl:ACL_IPV6' + config: + description: 'IPv6 ACL 1' + name: 'ipv6-acl1' + type: 'openconfig-acl:ACL_IPV6' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + source-address: '192::/64' + - sequence-id: 2 + config: + sequence-id: 2 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + destination-address: '192::2/128' + protocol: 'openconfig-packet-match-types:IP_TCP' + transport: + config: + destination-port: 80 + source-port: '1024..65535' + - name: 'ipv6-acl2' + type: 'openconfig-acl:ACL_IPV6' + config: + name: 'ipv6-acl2' + type: 'openconfig-acl:ACL_IPV6' + acl-entries: + acl-entry: + - sequence-id: 1 + config: + sequence-id: 1 + actions: + config: + forwarding-action: 'openconfig-acl:ACCEPT' + ipv6: + config: + protocol: 58 + config_requests: + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl1,ACL_IPV4' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ip-acl2,ACL_IPV4' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl1,ACL_IPV6' + method: 'delete' + - path: 'data/openconfig-acl:acl/acl-sets/acl-set=ipv6-acl2,ACL_IPV6' + method: 'delete' diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_l3_interfaces.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_l3_interfaces.yaml new file mode 100644 index 000000000..fea3cea89 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_l3_interfaces.yaml @@ -0,0 +1,586 @@ +--- +merged_01: + module_args: + config: + - name: Eth1/1 + ipv4: + addresses: + - address: 83.1.1.1/16 + - address: 84.1.1.1/16 + secondary: True + ipv6: + enabled: true + addresses: + - address: 83::1/16 + - address: 84::1/16 + - name: Vlan11 + ipv4: + addresses: + - address: 73.1.1.1/16 + - address: 74.1.1.1/16 + secondary: True + ipv6: + enabled: true + addresses: + - address: 73::1/16 + - name: Vlan12 + ipv4: + anycast_addresses: + - 11.12.13.14/12 + existing_l3_interfaces_config: + - path: "data/openconfig-interfaces:interfaces/interface" + response: + code: 200 + value: + openconfig-interfaces:interface: + - name: Eth1/1 + config: + mtu: 2000 + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses" + method: "patch" + data: + openconfig-if-ip:addresses: + address: + - ip: 83.1.1.1 + openconfig-if-ip:config: + ip: 83.1.1.1 + prefix-length: 16.0 + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses" + method: "patch" + data: + openconfig-if-ip:addresses: + address: + - ip: 84.1.1.1 + openconfig-if-ip:config: + ip: 84.1.1.1 + prefix-length: 16.0 + secondary: True + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv6/addresses" + method: "patch" + data: + openconfig-if-ip:addresses: + address: + - ip: 83::1 + openconfig-if-ip:config: + ip: 83::1 + prefix-length: 16.0 + - ip: 84::1 + openconfig-if-ip:config: + ip: 84::1 + prefix-length: 16.0 + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv6/config" + method: "patch" + data: + config: + enabled: True + - path: "data/openconfig-interfaces:interfaces/interface=Vlan11/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv4/addresses" + method: "patch" + data: + openconfig-if-ip:addresses: + address: + - ip: 73.1.1.1 + openconfig-if-ip:config: + ip: 73.1.1.1 + prefix-length: 16.0 + - path: "data/openconfig-interfaces:interfaces/interface=Vlan11/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv4/addresses" + method: "patch" + data: + openconfig-if-ip:addresses: + address: + - ip: 74.1.1.1 + openconfig-if-ip:config: + ip: 74.1.1.1 + prefix-length: 16.0 + secondary: True + - path: "data/openconfig-interfaces:interfaces/interface=Vlan11/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv6/addresses" + method: "patch" + data: + openconfig-if-ip:addresses: + address: + - ip: 73::1 + openconfig-if-ip:config: + ip: 73::1 + prefix-length: 16.0 + - path: "data/openconfig-interfaces:interfaces/interface=Vlan11/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv6/config" + method: "patch" + data: + config: + enabled: True + - path: "data/openconfig-interfaces:interfaces/interface=Vlan12/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv4/openconfig-interfaces-ext:sag-ipv4/config/static-anycast-gateway" + method: "patch" + data: + openconfig-interfaces-ext:static-anycast-gateway: + - 11.12.13.14/12 +deleted_01: + module_args: + state: deleted + existing_l3_interfaces_config: + - path: "data/openconfig-interfaces:interfaces/interface" + response: + code: 200 + value: + openconfig-interfaces:interface: + - name: Eth1/1 + subinterfaces: + subinterface: + - index: 0 + config: + index: 0 + openconfig-if-ip:ipv4: + addresses: + address: + - config: + ip: 73.1.1.1 + prefix-length: 8 + secondary: False + openconfig-if-ip:ipv6: + addresses: + address: + - config: + ip: 73::1 + prefix-length: 64 + enabled: True + - name: Vlan99 + openconfig-vlan:routed-vlan: + openconfig-if-ip:ipv4: + addresses: + address: + - config: + ip: 99.99.99.99 + prefix-length: 8 + secondary: False + - name: Vlan88 + openconfig-vlan:routed-vlan: + openconfig-if-ip:ipv4: + openconfig-interfaces-ext:sag-ipv4: + config: + static-anycast-gateway: 11.12.13.14/12 + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv6/addresses" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Vlan88/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv4/openconfig-interfaces-ext:sag-ipv4/config/static-anycast-gateway=11.12.13.14%2f12" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Vlan99/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv4/addresses" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + - name: Eth1/1 + ipv4: + addresses: + - address: 84.1.1.1/8 + secondary: True + ipv6: + addresses: + - address: 84::1/64 + - name: Eth1/2 + - name: Vlan99 + ipv4: + addresses: + - address: 74.1.1.1/8 + secondary: True + ipv6: + addresses: + - address: 73::1/64 + - name: Vlan88 + existing_l3_interfaces_config: + - path: "data/openconfig-interfaces:interfaces/interface" + response: + code: 200 + value: + openconfig-interfaces:interface: + - name: Eth1/1 + subinterfaces: + subinterface: + - index: 0 + config: + index: 0 + openconfig-if-ip:ipv4: + addresses: + address: + - config: + ip: 83.1.1.1 + prefix-length: 8 + secondary: False + - config: + ip: 84.1.1.1 + prefix-length: 8 + secondary: True + openconfig-if-ip:ipv6: + addresses: + address: + - config: + ip: 83::1 + prefix-length: 64 + - config: + ip: 84::1 + prefix-length: 64 + config: + enabled: True + - name: Eth1/2 + subinterfaces: + subinterface: + - index: 0 + config: + index: 0 + openconfig-if-ip:ipv4: + addresses: + address: + - config: + ip: 93.1.1.2 + prefix-length: 8 + secondary: False + - config: + ip: 94.1.1.2 + prefix-length: 8 + secondary: True + openconfig-if-ip:ipv6: + addresses: + address: + - config: + ip: 93::2 + prefix-length: 64 + - config: + ip: 94::2 + prefix-length: 64 + config: + enabled: True + - name: Vlan99 + openconfig-vlan:routed-vlan: + openconfig-if-ip:ipv4: + addresses: + address: + - config: + ip: 73.1.1.1 + prefix-length: 8 + secondary: False + - config: + ip: 74.1.1.1 + prefix-length: 8 + secondary: True + openconfig-if-ip:ipv6: + addresses: + address: + - config: + ip: 73::1 + prefix-length: 64 + config: + enabled: True + - name: Vlan88 + openconfig-vlan:routed-vlan: + openconfig-if-ip:ipv4: + openconfig-interfaces-ext:sag-ipv4: + config: + static-anycast-gateway: 11.12.13.14/12 + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses/address=84.1.1.1/config/secondary" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv6/addresses/address=84::1" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f2/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f2/subinterfaces/subinterface=0/openconfig-if-ip:ipv6/addresses" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f2/subinterfaces/subinterface=0/openconfig-if-ip:ipv6/config/enabled" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Vlan88/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv4/openconfig-interfaces-ext:sag-ipv4/config/static-anycast-gateway=11.12.13.14%2f12" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Vlan99/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv4/addresses/address=74.1.1.1/config/secondary" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Vlan99/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv6/addresses/address=73::1" + method: "delete" + data: + +replaced_01: + module_args: + state: replaced + config: + - name: Vlan13 + ipv4: + anycast_addresses: + - 11.12.13.14/12 + ipv6: + enabled: true + - name: Eth1/1 + ipv4: + addresses: + - address: 31.31.31.1/24 + - address: 32.32.32.1/24 + secondary: True + ipv6: + addresses: + - address: 31::1/64 + - address: 32::1/64 + existing_l3_interfaces_config: + - path: "data/openconfig-interfaces:interfaces/interface" + response: + code: 200 + value: + openconfig-interfaces:interface: + - name: Eth1/1 + subinterfaces: + subinterface: + - index: 0 + config: + index: 0 + openconfig-if-ip:ipv4: + addresses: + address: + - config: + ip: 73.1.1.1 + prefix-length: 8 + secondary: False + openconfig-if-ip:ipv6: + addresses: + address: + - config: + ip: 73::1 + prefix-length: 64 + enabled: True + - name: Eth1/2 + subinterfaces: + subinterface: + - index: 0 + config: + index: 0 + openconfig-if-ip:ipv4: + addresses: + address: + - config: + ip: 93.1.1.2 + prefix-length: 8 + secondary: False + - config: + ip: 94.1.1.2 + prefix-length: 8 + secondary: True + openconfig-if-ip:ipv6: + addresses: + address: + - config: + ip: 93::2 + prefix-length: 64 + - config: + ip: 94::2 + prefix-length: 64 + config: + enabled: True + - name: Vlan99 + openconfig-vlan:routed-vlan: + openconfig-if-ip:ipv4: + addresses: + address: + - config: + ip: 99.99.99.99 + prefix-length: 8 + secondary: False + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv6/addresses" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses" + method: "patch" + data: + openconfig-if-ip:addresses: + address: + - ip: 31.31.31.1 + openconfig-if-ip:config: + ip: 31.31.31.1 + prefix-length: 24.0 + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses" + method: "patch" + data: + openconfig-if-ip:addresses: + address: + - ip: 32.32.32.1 + openconfig-if-ip:config: + ip: 32.32.32.1 + prefix-length: 24.0 + secondary: True + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv6/addresses" + method: "patch" + data: + openconfig-if-ip:addresses: + address: + - ip: 31::1 + openconfig-if-ip:config: + ip: 31::1 + prefix-length: 64.0 + - ip: 32::1 + openconfig-if-ip:config: + ip: 32::1 + prefix-length: 64.0 + - path: "data/openconfig-interfaces:interfaces/interface=Vlan13/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv4/openconfig-interfaces-ext:sag-ipv4/config/static-anycast-gateway" + method: "patch" + data: + openconfig-interfaces-ext:static-anycast-gateway: + - 11.12.13.14/12 + - path: "data/openconfig-interfaces:interfaces/interface=Vlan13/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv6/config" + method: "patch" + data: + config: + enabled: True + +overridden_01: + module_args: + state: overridden + config: + - name: Vlan13 + ipv4: + anycast_addresses: + - 11.12.13.14/12 + ipv6: + enabled: true + - name: Eth1/1 + ipv4: + addresses: + - address: 31.31.31.1/24 + - address: 32.32.32.1/24 + secondary: True + ipv6: + addresses: + - address: 31::1/64 + - address: 32::1/64 + existing_l3_interfaces_config: + - path: "data/openconfig-interfaces:interfaces/interface" + response: + code: 200 + value: + openconfig-interfaces:interface: + - name: Eth1/1 + subinterfaces: + subinterface: + - index: 0 + config: + index: 0 + openconfig-if-ip:ipv4: + addresses: + address: + - config: + ip: 73.1.1.1 + prefix-length: 8 + secondary: False + openconfig-if-ip:ipv6: + addresses: + address: + - config: + ip: 73::1 + prefix-length: 64 + enabled: True + - name: Eth1/2 + subinterfaces: + subinterface: + - index: 0 + config: + index: 0 + openconfig-if-ip:ipv4: + addresses: + address: + - config: + ip: 93.1.1.2 + prefix-length: 8 + secondary: False + - config: + ip: 94.1.1.2 + prefix-length: 8 + secondary: True + openconfig-if-ip:ipv6: + addresses: + address: + - config: + ip: 93::2 + prefix-length: 64 + - config: + ip: 94::2 + prefix-length: 64 + config: + enabled: True + - name: Vlan99 + openconfig-vlan:routed-vlan: + openconfig-if-ip:ipv4: + addresses: + address: + - config: + ip: 99.99.99.99 + prefix-length: 8 + secondary: False + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv6/addresses" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f2/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f2/subinterfaces/subinterface=0/openconfig-if-ip:ipv6/addresses" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f2/subinterfaces/subinterface=0/openconfig-if-ip:ipv6/config/enabled" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Vlan99/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv4/addresses" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses" + method: "patch" + data: + openconfig-if-ip:addresses: + address: + - ip: 31.31.31.1 + openconfig-if-ip:config: + ip: 31.31.31.1 + prefix-length: 24.0 + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses" + method: "patch" + data: + openconfig-if-ip:addresses: + address: + - ip: 32.32.32.1 + openconfig-if-ip:config: + ip: 32.32.32.1 + prefix-length: 24.0 + secondary: True + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f1/subinterfaces/subinterface=0/openconfig-if-ip:ipv6/addresses" + method: "patch" + data: + openconfig-if-ip:addresses: + address: + - ip: 31::1 + openconfig-if-ip:config: + ip: 31::1 + prefix-length: 64.0 + - ip: 32::1 + openconfig-if-ip:config: + ip: 32::1 + prefix-length: 64.0 + - path: "data/openconfig-interfaces:interfaces/interface=Vlan13/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv4/openconfig-interfaces-ext:sag-ipv4/config/static-anycast-gateway" + method: "patch" + data: + openconfig-interfaces-ext:static-anycast-gateway: + - 11.12.13.14/12 + - path: "data/openconfig-interfaces:interfaces/interface=Vlan13/openconfig-vlan:routed-vlan/openconfig-if-ip:ipv6/config" + method: "patch" + data: + config: + enabled: True diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_lag_interfaces.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_lag_interfaces.yaml new file mode 100644 index 000000000..8ebc1623a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_lag_interfaces.yaml @@ -0,0 +1,204 @@ +--- +merged_01: + module_args: + config: + - name: PortChannel10 + members: + interfaces: + - member: Eth1/11 + - member: Eth1/12 + - name: PortChannel20 + members: + interfaces: + - member: Eth1/21 + - member: Eth1/22 + - name: PortChannel30 + existing_lag_interfaces_config: + - path: "data/openconfig-interfaces:interfaces/interface" + response: + code: 200 + value: + openconfig-interfaces:interface: + - name: 'Eth1/1' + config: + mtu: 2000 + - path: "data/sonic-portchannel:sonic-portchannel" + response: + code: 200 + - path: "data/sonic-portchannel:sonic-portchannel" + response: + code: 200 + value: + sonic-portchannel:sonic-portchannel: + PORTCHANNEL: + PORTCHANNEL_LIST: + - name: PortChannel10 + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces" + method: "patch" + data: + openconfig-interfaces:interfaces: + interface: + - name: PortChannel20 + config: + name: PortChannel20 + - path: "data/openconfig-interfaces:interfaces" + method: "patch" + data: + openconfig-interfaces:interfaces: + interface: + - name: PortChannel30 + config: + name: PortChannel30 + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F11/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id" + method: "patch" + data: + openconfig-if-aggregate:aggregate-id: PortChannel10 + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F12/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id" + method: "patch" + data: + openconfig-if-aggregate:aggregate-id: PortChannel10 + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F21/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id" + method: "patch" + data: + openconfig-if-aggregate:aggregate-id: PortChannel20 + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F22/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id" + method: "patch" + data: + openconfig-if-aggregate:aggregate-id: PortChannel20 +deleted_01: + module_args: + state: deleted + existing_lag_interfaces_config: + - path: "data/sonic-portchannel:sonic-portchannel" + response: + code: 200 + value: + sonic-portchannel:sonic-portchannel: + PORTCHANNEL: + PORTCHANNEL_LIST: + - name: PortChannel10 + PORTCHANNEL_MEMBER: + PORTCHANNEL_MEMBER_LIST: + - ifname: Eth1/11 + name: PortChannel10 + - ifname: Eth1/12 + name: PortChannel10 + expected_config_requests: + - path: "data/sonic-portchannel:sonic-portchannel/PORTCHANNEL/PORTCHANNEL_LIST" + method: "delete" + data: + - path: "data/sonic-portchannel:sonic-portchannel/PORTCHANNEL_MEMBER/PORTCHANNEL_MEMBER_LIST" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + - name: PortChannel10 + members: + interfaces: + - member: Eth1/12 + - member: Eth1/14 + - name: PortChannel20 + members: + interfaces: + - member: Eth1/23 + - name: PortChannel30 + members: + interfaces: + - member: Eth1/31 + - name: PortChannel40 + existing_lag_interfaces_config: + - path: "data/sonic-portchannel:sonic-portchannel" + response: + code: 200 + value: + sonic-portchannel:sonic-portchannel: + PORTCHANNEL: + PORTCHANNEL_LIST: + - name: PortChannel10 + - name: PortChannel20 + - name: PortChannel30 + - name: PortChannel40 + PORTCHANNEL_MEMBER: + PORTCHANNEL_MEMBER_LIST: + - ifname: Eth1/11 + name: PortChannel10 + - ifname: Eth1/12 + name: PortChannel10 + - ifname: Eth1/13 + name: PortChannel10 + - ifname: Eth1/14 + name: PortChannel10 + - ifname: Eth1/21 + name: PortChannel20 + - ifname: Eth1/22 + name: PortChannel20 + - ifname: Eth1/23 + name: PortChannel20 + - ifname: Eth1/24 + name: PortChannel20 + - ifname: Eth1/31 + name: PortChannel30 + - ifname: Eth1/41 + name: PortChannel40 + - ifname: Eth1/42 + name: PortChannel40 + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f12/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f14/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f23/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2f31/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id" + method: "delete" + data: + - path: "data/openconfig-interfaces:interfaces/interface=PortChannel40" + method: "delete" + data: + +deleted_03: + module_args: + state: deleted + config: + existing_lag_interfaces_config: + - path: "data/sonic-portchannel:sonic-portchannel" + response: + code: 200 + value: + sonic-portchannel:sonic-portchannel: + PORTCHANNEL: + PORTCHANNEL_LIST: + - name: PortChannel10 + - name: PortChannel20 + PORTCHANNEL_MEMBER: + PORTCHANNEL_MEMBER_LIST: + - ifname: Eth1/11 + name: PortChannel10 + - ifname: Eth1/12 + name: PortChannel10 + - ifname: Eth1/13 + name: PortChannel10 + - ifname: Eth1/14 + name: PortChannel10 + - ifname: Eth1/21 + name: PortChannel20 + - ifname: Eth1/22 + name: PortChannel20 + - ifname: Eth1/23 + name: PortChannel20 + - ifname: Eth1/24 + name: PortChannel20 + expected_config_requests: + - path: "data/sonic-portchannel:sonic-portchannel/PORTCHANNEL/PORTCHANNEL_LIST" + method: "delete" + data: + - path: "data/sonic-portchannel:sonic-portchannel/PORTCHANNEL_MEMBER/PORTCHANNEL_MEMBER_LIST" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_logging.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_logging.yaml new file mode 100644 index 000000000..61e996878 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_logging.yaml @@ -0,0 +1,180 @@ +--- +merged_01: + module_args: + config: + remote_servers: + - host: 10.11.0.2 + remote_port: 5 + source_interface: Eth1/24 + message_type: event + - host: log1.dell.com + remote_port: 6 + source_interface: Eth1/28 + existing_logging_config: + - path: "data/openconfig-system:system/logging" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-system:system/logging/remote-servers" + method: "patch" + data: + openconfig-system:remote-servers: + remote-server: + - host: 10.11.0.2 + config: + host: 10.11.0.2 + source-interface: Eth1/24 + message-type: event + remote-port: 5 + - host: log1.dell.com + config: + host: log1.dell.com + source-interface: Eth1/28 + remote-port: 6 + +deleted_01: + module_args: + state: deleted + existing_logging_config: + - path: "data/openconfig-system:system/logging" + response: + code: 200 + value: + openconfig-system:logging: + remote-servers: + remote-server: + - host: 10.11.0.2 + config: + host: 10.11.0.2 + source-interface: Eth1/24 + message-type: event + remote-port: 5 + - host: log1.dell.com + config: + host: log1.dell.com + source-interface: Eth1/28 + remote-port: 6 + expected_config_requests: + - path: "data/openconfig-system:system/logging/remote-servers" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + remote_servers: + - host: 10.11.0.2 + existing_logging_config: + - path: "data/openconfig-system:system/logging" + response: + code: 200 + value: + openconfig-system:logging: + remote-servers: + remote-server: + - host: 10.11.0.2 + config: + host: 10.11.0.2 + source-interface: Eth1/24 + message-type: event + remote-port: 5 + - host: log1.dell.com + config: + host: log1.dell.com + source-interface: Eth1/28 + remote-port: 6 + expected_config_requests: + - path: "data/openconfig-system:system/logging/remote-servers/remote-server=10.11.0.2" + method: "delete" + data: + +replaced_01: + module_args: + state: replaced + config: + remote_servers: + - host: 10.11.0.2 + remote_port: 9 + source_interface: Eth1/25 + message_type: log + existing_logging_config: + - path: "data/openconfig-system:system/logging" + response: + code: 200 + value: + openconfig-system:logging: + remote-servers: + remote-server: + - host: 10.11.0.2 + config: + host: 10.11.0.2 + source-interface: Eth1/24 + message-type: event + remote-port: 5 + - host: log1.dell.com + config: + host: log1.dell.com + source-interface: Eth1/28 + remote-port: 6 + expected_config_requests: + - path: "data/openconfig-system:system/logging/remote-servers" + method: "patch" + data: + openconfig-system:remote-servers: + remote-server: + - host: 10.11.0.2 + config: + host: 10.11.0.2 + source-interface: Eth1/25 + message-type: log + remote-port: 9 + vrf-name: + - path: "data/openconfig-system:system/logging/remote-servers/remote-server=10.11.0.2" + method: "delete" + data: + +overridden_01: + module_args: + state: overridden + config: + remote_servers: + - host: 10.11.0.10 + remote_port: 10 + source_interface: Eth1/26 + message_type: log + existing_logging_config: + - path: "data/openconfig-system:system/logging" + response: + code: 200 + value: + openconfig-system:logging: + remote-servers: + remote-server: + - host: 10.11.0.2 + config: + host: 10.11.0.2 + source-interface: Eth1/24 + message-type: event + remote-port: 5 + - host: log1.dell.com + config: + host: log1.dell.com + source-interface: Eth1/28 + remote-port: 6 + expected_config_requests: + - path: "data/openconfig-system:system/logging/remote-servers" + method: "delete" + data: + - path: "data/openconfig-system:system/logging/remote-servers" + method: "patch" + data: + openconfig-system:remote-servers: + remote-server: + - host: 10.11.0.10 + config: + host: 10.11.0.10 + source-interface: Eth1/26 + message-type: log + remote-port: 10 + vrf-name: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_mac.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_mac.yaml new file mode 100644 index 000000000..cd869a1a5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_mac.yaml @@ -0,0 +1,373 @@ +--- +merged_01: + module_args: + config: + - vrf_name: 'default' + mac: + aging_time: 50 + dampening_interval: 20 + dampening_threshold: 30 + mac_table_entries: + - mac_address: '00:00:5e:00:53:af' + vlan_id: 1 + interface: 'Ethernet20' + - mac_address: '00:33:33:33:33:33' + vlan_id: 2 + interface: 'Ethernet24' + - mac_address: '00:00:4e:00:24:af' + vlan_id: 3 + interface: 'Ethernet28' + existing_mac_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb/config/mac-aging-time" + response: + code: 200 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/openconfig-mac-dampening:mac-dampening/config" + response: + code: 200 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb/mac-table/entries" + response: + code: 200 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb" + method: "patch" + data: + openconfig-network-instance:fdb: + config: + mac-aging-time: 50 + mac-table: + entries: + entry: + - mac-address: '00:00:5e:00:53:af' + vlan: 1 + config: + mac-address: '00:00:5e:00:53:af' + vlan: 1 + interface: + interface-ref: + config: + interface: 'Ethernet20' + subinterface: 0 + - mac-address: '00:33:33:33:33:33' + vlan: 2 + config: + mac-address: '00:33:33:33:33:33' + vlan: 2 + interface: + interface-ref: + config: + interface: 'Ethernet24' + subinterface: 0 + - mac-address: '00:00:4e:00:24:af' + vlan: 3 + config: + mac-address: '00:00:4e:00:24:af' + vlan: 3 + interface: + interface-ref: + config: + interface: 'Ethernet28' + subinterface: 0 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/openconfig-mac-dampening:mac-dampening" + method: "patch" + data: + openconfig-mac-dampening:mac-dampening: + config: + interval: 20 + threshold: 30 + +replaced_01: + module_args: + config: + - vrf_name: 'default' + mac: + aging_time: 45 + dampening_interval: 30 + dampening_threshold: 60 + mac_table_entries: + - mac_address: '00:00:5e:00:53:af' + vlan_id: 1 + interface: 'Ethernet20' + - mac_address: '00:44:44:44:44:44' + vlan_id: 2 + interface: 'Ethernet28' + existing_mac_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb/config/mac-aging-time" + response: + code: 200 + value: + openconfig-network-instance:mac-aging-time: 50 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/openconfig-mac-dampening:mac-dampening/config" + response: + code: 200 + value: + openconfig-mac-dampening:config: + threshold: 30 + interval: 20 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb/mac-table/entries" + response: + code: 200 + value: + openconfig-network-instance:entries: + entry: + - mac-address: 00:00:4e:00:24:af + vlan: 3 + config: + mac-address: 00:00:4e:00:24:af + vlan: 3 + interface: + interface-ref: + config: + interface: Ethernet28 + - mac-address: 00:00:5e:00:53:af + vlan: 1 + config: + mac-address: 00:00:5e:00:53:af + vlan: 1 + interface: + interface-ref: + config: + interface: Ethernet24 + - mac-address: '00:33:33:33:33:33' + vlan: 2 + config: + mac-address: '00:33:33:33:33:33' + vlan: 2 + interface: + interface-ref: + config: + interface: Ethernet20 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb" + method: "patch" + data: + openconfig-network-instance:fdb: + config: + mac-aging-time: 45 + mac-table: + entries: + entry: + - mac-address: '00:00:5e:00:53:af' + vlan: 1 + config: + mac-address: '00:00:5e:00:53:af' + vlan: 1 + interface: + interface-ref: + config: + interface: 'Ethernet20' + subinterface: 0 + - mac-address: '00:44:44:44:44:44' + vlan: 2 + config: + mac-address: '00:44:44:44:44:44' + vlan: 2 + interface: + interface-ref: + config: + interface: 'Ethernet28' + subinterface: 0 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/openconfig-mac-dampening:mac-dampening" + method: "patch" + data: + openconfig-mac-dampening:mac-dampening: + config: + interval: 30 + threshold: 60 + +overridden_01: + module_args: + config: + - vrf_name: 'default' + mac: + aging_time: 10 + dampening_interval: 20 + dampening_threshold: 30 + mac_table_entries: + - mac_address: '00:11:11:11:11:11' + vlan_id: 1 + interface: 'Ethernet20' + - mac_address: '00:22:22:22:22:22' + vlan_id: 2 + interface: 'Ethernet24' + - mac_address: '00:00:33:33:33:33' + vlan_id: 3 + interface: 'Ethernet28' + state: overridden + existing_mac_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb/config/mac-aging-time" + response: + code: 200 + value: + openconfig-network-instance:mac-aging-time: 50 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/openconfig-mac-dampening:mac-dampening/config" + response: + code: 200 + value: + openconfig-mac-dampening:config: + threshold: 30 + interval: 20 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb/mac-table/entries" + response: + code: 200 + value: + openconfig-network-instance:entries: + entry: + - mac-address: '00:00:5e:00:53:af' + vlan: 1 + config: + mac-address: '00:00:5e:00:53:af' + vlan: 1 + interface: + interface-ref: + config: + interface: 'Ethernet20' + subinterface: 0 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb" + method: "patch" + data: + openconfig-network-instance:fdb: + config: + mac-aging-time: 10 + mac-table: + entries: + entry: + - mac-address: '00:11:11:11:11:11' + vlan: 1 + config: + mac-address: '00:11:11:11:11:11' + vlan: 1 + interface: + interface-ref: + config: + interface: 'Ethernet20' + subinterface: 0 + - mac-address: '00:22:22:22:22:22' + vlan: 2 + config: + mac-address: '00:22:22:22:22:22' + vlan: 2 + interface: + interface-ref: + config: + interface: 'Ethernet24' + subinterface: 0 + - mac-address: '00:00:33:33:33:33' + vlan: 3 + config: + mac-address: '00:00:33:33:33:33' + vlan: 3 + interface: + interface-ref: + config: + interface: 'Ethernet28' + subinterface: 0 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/openconfig-mac-dampening:mac-dampening" + method: "patch" + data: + openconfig-mac-dampening:mac-dampening: + config: + interval: 20 + threshold: 30 + +deleted_01: + module_args: + config: + - vrf_name: 'default' + mac: + aging_time: 10 + dampening_interval: 20 + dampening_threshold: 30 + mac_table_entries: + - mac_address: '00:11:11:11:11:11' + vlan_id: 1 + interface: 'Ethernet20' + - mac_address: '00:00:33:33:33:33' + vlan_id: 3 + state: deleted + existing_mac_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb/config/mac-aging-time" + response: + code: 200 + value: + openconfig-network-instance:mac-aging-time: 10 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/openconfig-mac-dampening:mac-dampening/config" + response: + code: 200 + value: + openconfig-mac-dampening:config: + threshold: 30 + interval: 20 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb/mac-table/entries" + response: + code: 200 + value: + openconfig-network-instance:entries: + entry: + - mac-address: '00:00:33:33:33:33' + vlan: 3 + config: + mac-address: '00:00:33:33:33:33' + vlan: 3 + interface: + interface-ref: + config: + interface: Ethernet28 + - mac-address: '00:11:11:11:11:11' + vlan: 1 + config: + mac-address: '00:11:11:11:11:11' + vlan: 1 + interface: + interface-ref: + config: + interface: Ethernet20 + - mac-address: '00:22:22:22:22:22' + vlan: 2 + config: + mac-address: '00:22:22:22:22:22' + vlan: 2 + interface: + interface-ref: + config: + interface: Ethernet24 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb/config/mac-aging-time" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/openconfig-mac-dampening:mac-dampening/config/interval" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/openconfig-mac-dampening:mac-dampening/config/threshold" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb/mac-table/entries/entry=00:11:11:11:11:11,1/interface" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/fdb/mac-table/entries/entry=00:00:33:33:33:33,3" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_mclag.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_mclag.yaml new file mode 100644 index 000000000..b12fa0704 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_mclag.yaml @@ -0,0 +1,987 @@ +--- +merged_01: + module_args: + config: + domain_id: 1 + keepalive: 1 + peer_address: 1.1.1.1 + peer_link: 'Portchannel1' + source_address: 2.2.2.2 + session_timeout: 3 + system_mac: '00:00:00:11:11:11' + unique_ip: + vlans: + - vlan: Vlan4 + - vlan: Vlan201-205 + peer_gateway: + vlans: + - vlan: Vlan4 + - vlan: Vlan201-205 + members: + portchannels: + - lag: PortChannel10 + existing_mclag_config: + - path: "data/openconfig-mclag:mclag" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-mclag:mclag/interfaces/interface" + method: "patch" + data: + openconfig-mclag:interface: + - name: PortChannel10 + config: + name: PortChannel10 + mclag-domain-id: 1 + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain" + method: "patch" + data: + openconfig-mclag:mclag-domain: + - domain-id: 1 + config: + session-timeout: 3 + keepalive-interval: 1 + source-address: 2.2.2.2 + peer-address: 1.1.1.1 + peer-link: PortChannel1 + openconfig-mclag:mclag-system-mac: 00:00:00:11:11:11 + - path: "data/openconfig-mclag:mclag/vlan-ifs/vlan-if" + method: "patch" + data: + openconfig-mclag:vlan-if: + - name: Vlan4 + config: + name: Vlan4 + peer-gateway-enable: ENABLE + - name: Vlan201 + config: + name: Vlan201 + peer-gateway-enable: ENABLE + - name: Vlan202 + config: + name: Vlan202 + peer-gateway-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + peer-gateway-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + peer-gateway-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + peer-gateway-enable: ENABLE + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface" + method: "patch" + data: + openconfig-mclag:vlan-interface: + - name: Vlan4 + config: + name: Vlan4 + unique-ip-enable: ENABLE + - name: Vlan201 + config: + name: Vlan201 + unique-ip-enable: ENABLE + - name: Vlan202 + config: + name: Vlan202 + unique-ip-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + unique-ip-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + unique-ip-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + unique-ip-enable: ENABLE + +merged_02: + module_args: + config: + domain_id: 1 + unique_ip: + vlans: + - vlan: Vlan204-208 + peer_gateway: + vlans: + - vlan: Vlan204-208 + state: merged + existing_mclag_config: + - path: "data/openconfig-mclag:mclag" + response: + code: 200 + value: + openconfig-mclag:mclag: + mclag-domains: + mclag-domain: + - domain-id: 1 + config: + session-timeout: 3 + keepalive-interval: 1 + source-address: 2.2.2.2 + peer-address: 1.1.1.1 + peer-link: PortChannel1 + mclag-system-mac: 00:00:00:11:11:11 + vlan-ifs: + vlan-if: + - name: Vlan4 + config: + name: Vlan4 + peer-gateway-enable: ENABLE + - name: Vlan201 + config: + name: Vlan201 + peer-gateway-enable: ENABLE + - name: Vlan202 + config: + name: Vlan202 + peer-gateway-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + peer-gateway-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + peer-gateway-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + peer-gateway-enable: ENABLE + vlan-interfaces: + vlan-interface: + - name: Vlan4 + config: + name: Vlan4 + unique-ip-enable: ENABLE + - name: Vlan201 + config: + name: Vlan201 + unique-ip-enable: ENABLE + - name: Vlan202 + config: + name: Vlan202 + unique-ip-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + unique-ip-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + unique-ip-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + unique-ip-enable: ENABLE + expected_config_requests: + - path: "data/openconfig-mclag:mclag/vlan-ifs/vlan-if" + method: "patch" + data: + openconfig-mclag:vlan-if: + - name: Vlan206 + config: + name: Vlan206 + peer-gateway-enable: ENABLE + - name: Vlan207 + config: + name: Vlan207 + peer-gateway-enable: ENABLE + - name: Vlan208 + config: + name: Vlan208 + peer-gateway-enable: ENABLE + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface" + method: "patch" + data: + openconfig-mclag:vlan-interface: + - name: Vlan206 + config: + name: Vlan206 + unique-ip-enable: ENABLE + - name: Vlan207 + config: + name: Vlan207 + unique-ip-enable: ENABLE + - name: Vlan208 + config: + name: Vlan208 + unique-ip-enable: ENABLE + +deleted_01: + module_args: + state: deleted + existing_mclag_config: + - path: "data/openconfig-mclag:mclag" + response: + code: 200 + value: + openconfig-mclag:mclag: + mclag-domains: + mclag-domain: + - domain-id: 1 + config: + session-timeout: 3 + keepalive-interval: 1 + source-address: 2.2.2.2 + peer-address: 1.1.1.1 + peer-link: PortChannel1 + mclag-system-mac: 00:00:00:11:11:11 + - path: "data/openconfig-mclag:mclag/interfaces/interface" + response: + code: 200 + value: + openconfig-mclag:interface: + - name: PortChannel10 + config: + name: PortChannel10 + mclag-domain-id: 1 + expected_config_requests: + - path: "data/openconfig-mclag:mclag/mclag-domains" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + domain_id: 8 + keepalive: 9 + peer_address: 1.1.1.1 + peer_link: 'Portchannel1' + source_address: 2.2.2.2 + session_timeout: 33 + system_mac: '00:00:00:11:11:11' + unique_ip: + vlans: + - vlan: Vlan4 + - vlan: Vlan201 + - vlan: Vlan203-204 + peer_gateway: + vlans: + - vlan: Vlan4 + - vlan: Vlan201 + - vlan: Vlan203-204 + members: + portchannels: + - lag: PortChannel99 + existing_mclag_config: + - path: "data/openconfig-mclag:mclag" + response: + code: 200 + value: + openconfig-mclag:mclag: + mclag-domains: + mclag-domain: + - domain-id: 8 + config: + session-timeout: 33 + keepalive-interval: 9 + source-address: 2.2.2.2 + peer-address: 1.1.1.1 + peer-link: PortChannel1 + mclag-system-mac: 00:00:00:11:11:11 + interfaces: + interface: + - config: + name: PortChannel88 + mclag-domain-id: 8 + mclag-id: 88 + name: PortChannel88 + - config: + name: PortChannel99 + mclag-domain-id: 8 + mclag-id: 99 + name: PortChannel99 + vlan-ifs: + vlan-if: + - name: Vlan4 + config: + name: Vlan4 + peer-gateway-enable: ENABLE + - name: Vlan201 + config: + name: Vlan201 + peer-gateway-enable: ENABLE + - name: Vlan202 + config: + name: Vlan202 + peer-gateway-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + peer-gateway-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + peer-gateway-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + peer-gateway-enable: ENABLE + vlan-interfaces: + vlan-interface: + - name: Vlan4 + config: + name: Vlan4 + unique-ip-enable: ENABLE + - name: Vlan201 + config: + name: Vlan201 + unique-ip-enable: ENABLE + - name: Vlan202 + config: + name: Vlan202 + unique-ip-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + unique-ip-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + unique-ip-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + unique-ip-enable: ENABLE + expected_config_requests: + - path: "data/openconfig-mclag:mclag/interfaces/interface=PortChannel99" + method: "delete" + data: + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain=8/config/keepalive-interval" + method: "delete" + data: + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain=8/config/mclag-system-mac" + method: "delete" + data: + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain=8/config/peer-address" + method: "delete" + data: + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain=8/config/peer-link" + method: "delete" + data: + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain=8/config/session-timeout" + method: "delete" + data: + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain=8/config/source-address" + method: "delete" + data: + - path: "data/openconfig-mclag:mclag/vlan-ifs/vlan-if=Vlan4" + method: "delete" + data: + - path: "data/openconfig-mclag:mclag/vlan-ifs/vlan-if=Vlan201" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-ifs/vlan-if=Vlan203" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-ifs/vlan-if=Vlan204" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=Vlan4" + method: "delete" + data: + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=Vlan201" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=Vlan203" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=Vlan204" + method: "delete" + +replaced_01: + module_args: + config: + domain_id: 1 + unique_ip: + vlans: + - vlan: Vlan204-208 + peer_gateway: + vlans: + members: + portchannels: + - lag: portchannel99 + - lag: portchannel100 + state: replaced + existing_mclag_config: + - path: "data/openconfig-mclag:mclag" + response: + code: 200 + value: + openconfig-mclag:mclag: + mclag-domains: + mclag-domain: + - domain-id: 1 + config: + session-timeout: 3 + keepalive-interval: 1 + delay-restore: 300 + source-address: 2.2.2.2 + peer-address: 1.1.1.1 + peer-link: PortChannel1 + mclag-system-mac: 00:00:00:11:11:11 + mclag-gateway-macs: + mclag-gateway-mac: + - gateway-mac: 00:00:00:14:14:14 + config: + gateway-mac: 00:00:00:14:14:14 + interfaces: + interface: + - name: PortChannel88 + config: + name: PortChannel88 + mclag-domain-id: 1 + mclag-id: 88 + - name: PortChannel99 + config: + name: PortChannel99 + mclag-domain-id: 1 + mclag-id: 99 + vlan-ifs: + vlan-if: + - name: Vlan4 + config: + name: Vlan4 + peer-gateway-enable: ENABLE + - name: Vlan201 + config: + name: Vlan201 + peer-gateway-enable: ENABLE + - name: Vlan202 + config: + name: Vlan202 + peer-gateway-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + peer-gateway-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + peer-gateway-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + peer-gateway-enable: ENABLE + vlan-interfaces: + vlan-interface: + - name: Vlan4 + config: + name: Vlan4 + unique-ip-enable: ENABLE + - name: Vlan201 + config: + name: Vlan201 + unique-ip-enable: ENABLE + - name: Vlan202 + config: + name: Vlan202 + unique-ip-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + unique-ip-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + unique-ip-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + unique-ip-enable: ENABLE + expected_config_requests: + - path: "data/openconfig-mclag:mclag/interfaces/interface=PortChannel88" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-ifs/vlan-if" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=Vlan4" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=Vlan201" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=Vlan202" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=Vlan203" + method: "delete" + - path: "data/openconfig-mclag:mclag/interfaces/interface" + method: "patch" + data: + openconfig-mclag:interface: + - name: PortChannel100 + config: + name: PortChannel100 + mclag-domain-id: 1 + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface" + method: "patch" + data: + openconfig-mclag:vlan-interface: + - name: Vlan206 + config: + name: Vlan206 + unique-ip-enable: ENABLE + - name: Vlan207 + config: + name: Vlan207 + unique-ip-enable: ENABLE + - name: Vlan208 + config: + name: Vlan208 + unique-ip-enable: ENABLE + +replaced_02: + module_args: + config: + domain_id: 10 + session_timeout: 60 + system_mac: 00:00:00:02:02:02 + unique_ip: + vlans: + - vlan: Vlan204-208 + peer_gateway: + vlans: + - vlan: Vlan204-208 + members: + portchannels: + - lag: portchannel99 + - lag: portchannel100 + state: replaced + existing_mclag_config: + - path: "data/openconfig-mclag:mclag" + response: + code: 200 + value: + openconfig-mclag:mclag: + mclag-domains: + mclag-domain: + - domain-id: 1 + config: + session-timeout: 3 + keepalive-interval: 1 + delay-restore: 300 + source-address: 2.2.2.2 + peer-address: 1.1.1.1 + peer-link: PortChannel1 + mclag-system-mac: 00:00:00:11:11:11 + mclag-gateway-macs: + mclag-gateway-mac: + - gateway-mac: 00:00:00:14:14:14 + config: + gateway-mac: 00:00:00:14:14:14 + interfaces: + interface: + - name: PortChannel88 + config: + name: PortChannel88 + mclag-domain-id: 1 + mclag-id: 88 + - name: PortChannel99 + config: + name: PortChannel99 + mclag-domain-id: 1 + mclag-id: 99 + vlan-ifs: + vlan-if: + - name: Vlan4 + config: + name: Vlan4 + peer-gateway-enable: ENABLE + - name: Vlan201 + config: + name: Vlan201 + peer-gateway-enable: ENABLE + - name: Vlan202 + config: + name: Vlan202 + peer-gateway-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + peer-gateway-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + peer-gateway-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + peer-gateway-enable: ENABLE + vlan-interfaces: + vlan-interface: + - name: Vlan4 + config: + name: Vlan4 + unique-ip-enable: ENABLE + - name: Vlan201 + config: + name: Vlan201 + unique-ip-enable: ENABLE + - name: Vlan202 + config: + name: Vlan202 + unique-ip-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + unique-ip-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + unique-ip-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + unique-ip-enable: ENABLE + expected_config_requests: + - path: "data/openconfig-mclag:mclag/mclag-gateway-macs/mclag-gateway-mac" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-ifs/vlan-if" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface" + method: "delete" + - path: "data/openconfig-mclag:mclag/mclag-domains" + method: "delete" + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain" + method: "patch" + data: + openconfig-mclag:mclag-domain: + - domain-id: 10 + config: + session-timeout: 60 + openconfig-mclag:mclag-system-mac: 00:00:00:02:02:02 + - path: "data/openconfig-mclag:mclag/interfaces/interface" + method: "patch" + data: + openconfig-mclag:interface: + - name: PortChannel99 + config: + name: PortChannel99 + mclag-domain-id: 10 + - name: PortChannel100 + config: + name: PortChannel100 + mclag-domain-id: 10 + - path: "data/openconfig-mclag:mclag/vlan-ifs/vlan-if" + method: "patch" + data: + openconfig-mclag:vlan-if: + - name: Vlan204 + config: + name: Vlan204 + peer-gateway-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + peer-gateway-enable: ENABLE + - name: Vlan206 + config: + name: Vlan206 + peer-gateway-enable: ENABLE + - name: Vlan207 + config: + name: Vlan207 + peer-gateway-enable: ENABLE + - name: Vlan208 + config: + name: Vlan208 + peer-gateway-enable: ENABLE + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface" + method: "patch" + data: + openconfig-mclag:vlan-interface: + - name: Vlan204 + config: + name: Vlan204 + unique-ip-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + unique-ip-enable: ENABLE + - name: Vlan206 + config: + name: Vlan206 + unique-ip-enable: ENABLE + - name: Vlan207 + config: + name: Vlan207 + unique-ip-enable: ENABLE + - name: Vlan208 + config: + name: Vlan208 + unique-ip-enable: ENABLE + +replaced_03: + module_args: + config: + domain_id: 1 + gateway_mac: 00:00:00:12:12:12 + unique_ip: + vlans: + - vlan: Vlan204-208 + state: replaced + existing_mclag_config: + - path: "data/openconfig-mclag:mclag" + response: + code: 200 + value: + openconfig-mclag:mclag: + mclag-domains: + mclag-domain: + - domain-id: 1 + config: + session-timeout: 3 + keepalive-interval: 1 + delay-restore: 300 + source-address: 2.2.2.2 + peer-address: 1.1.1.1 + peer-link: PortChannel1 + mclag-system-mac: 00:00:00:11:11:11 + mclag-gateway-macs: + mclag-gateway-mac: + - gateway-mac: 00:00:00:14:14:14 + config: + gateway-mac: 00:00:00:14:14:14 + interfaces: + interface: + - name: PortChannel88 + config: + name: PortChannel88 + mclag-domain-id: 1 + mclag-id: 88 + - name: PortChannel99 + config: + name: PortChannel99 + mclag-domain-id: 1 + mclag-id: 99 + vlan-ifs: + vlan-if: + - name: Vlan4 + config: + name: Vlan4 + peer-gateway-enable: ENABLE + - name: Vlan201 + config: + name: Vlan201 + peer-gateway-enable: ENABLE + - name: Vlan202 + config: + name: Vlan202 + peer-gateway-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + peer-gateway-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + peer-gateway-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + peer-gateway-enable: ENABLE + vlan-interfaces: + vlan-interface: + - name: Vlan4 + config: + name: Vlan4 + unique-ip-enable: ENABLE + - name: Vlan201 + config: + name: Vlan201 + unique-ip-enable: ENABLE + - name: Vlan202 + config: + name: Vlan202 + unique-ip-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + unique-ip-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + unique-ip-enable: ENABLE + - name: Vlan205 + config: + name: Vlan205 + unique-ip-enable: ENABLE + expected_config_requests: + - path: "data/openconfig-mclag:mclag/mclag-gateway-macs/mclag-gateway-mac" + method: "delete" + - path: "data/openconfig-mclag:mclag/interfaces/interface=PortChannel88" + method: "delete" + - path: "data/openconfig-mclag:mclag/interfaces/interface=PortChannel99" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-ifs/vlan-if" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=Vlan4" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=Vlan201" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=Vlan202" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=Vlan203" + method: "delete" + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain=1/config/mclag-system-mac" + method: "delete" + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain=1/config/peer-address" + method: "delete" + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain=1/config/peer-link" + method: "delete" + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain=1/config/session-timeout" + method: "delete" + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain=1/config/source-address" + method: "delete" + - path: "data/openconfig-mclag:mclag/mclag-gateway-macs/mclag-gateway-mac" + method: "patch" + data: + openconfig-mclag:mclag-gateway-mac: + - gateway-mac: 00:00:00:12:12:12 + config: + gateway-mac: 00:00:00:12:12:12 + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface" + method: "patch" + data: + openconfig-mclag:vlan-interface: + - name: Vlan206 + config: + name: Vlan206 + unique-ip-enable: ENABLE + - name: Vlan207 + config: + name: Vlan207 + unique-ip-enable: ENABLE + - name: Vlan208 + config: + name: Vlan208 + unique-ip-enable: ENABLE + +overridden_01: + module_args: + config: + domain_id: 10 + peer_address: 1.1.1.1 + peer_link: 'Portchannel1' + source_address: 2.2.2.2 + keepalive: 1 + session_timeout: 60 + system_mac: 00:00:00:02:02:02 + unique_ip: + vlans: + - vlan: Vlan205-208 + members: + portchannels: + - lag: portchannel99 + - lag: portchannel100 + state: overridden + existing_mclag_config: + - path: "data/openconfig-mclag:mclag" + response: + code: 200 + value: + openconfig-mclag:mclag: + mclag-domains: + mclag-domain: + - domain-id: 10 + config: + session-timeout: 3 + keepalive-interval: 1 + delay-restore: 360 + source-address: 2.2.2.2 + peer-address: 1.1.1.1 + peer-link: PortChannel1 + mclag-system-mac: 00:00:00:11:11:11 + mclag-gateway-macs: + mclag-gateway-mac: + - gateway-mac: 00:00:00:14:14:14 + config: + gateway-mac: 00:00:00:14:14:14 + interfaces: + interface: + - name: PortChannel88 + config: + name: PortChannel88 + mclag-domain-id: 10 + mclag-id: 88 + - name: PortChannel99 + config: + name: PortChannel99 + mclag-domain-id: 10 + mclag-id: 99 + vlan-ifs: + vlan-if: + - name: Vlan4 + config: + name: Vlan4 + peer-gateway-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + peer-gateway-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + peer-gateway-enable: ENABLE + vlan-interfaces: + vlan-interface: + - name: Vlan4 + config: + name: Vlan4 + unique-ip-enable: ENABLE + - name: Vlan203 + config: + name: Vlan203 + unique-ip-enable: ENABLE + - name: Vlan204 + config: + name: Vlan204 + unique-ip-enable: ENABLE + expected_config_requests: + - path: "data/openconfig-mclag:mclag/mclag-gateway-macs/mclag-gateway-mac" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-ifs/vlan-if" + method: "delete" + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface" + method: "delete" + - path: "data/openconfig-mclag:mclag/interfaces/interface=PortChannel88" + method: "delete" + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain=10/config/delay-restore" + method: "delete" + - path: "data/openconfig-mclag:mclag/mclag-domains/mclag-domain" + method: "patch" + data: + openconfig-mclag:mclag-domain: + - domain-id: 10 + config: + session-timeout: 60 + openconfig-mclag:mclag-system-mac: 00:00:00:02:02:02 + - path: "data/openconfig-mclag:mclag/interfaces/interface" + method: "patch" + data: + openconfig-mclag:interface: + - name: PortChannel100 + config: + name: PortChannel100 + mclag-domain-id: 10 + - path: "data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface" + method: "patch" + data: + openconfig-mclag:vlan-interface: + - name: Vlan205 + config: + name: Vlan205 + unique-ip-enable: ENABLE + - name: Vlan206 + config: + name: Vlan206 + unique-ip-enable: ENABLE + - name: Vlan207 + config: + name: Vlan207 + unique-ip-enable: ENABLE + - name: Vlan208 + config: + name: Vlan208 + unique-ip-enable: ENABLE diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_ntp.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_ntp.yaml new file mode 100644 index 000000000..b1b7dc9d0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_ntp.yaml @@ -0,0 +1,486 @@ +--- +merged_01: + module_args: + config: + servers: + - address: 10.11.0.2 + minpoll: 5 + maxpoll: 12 + key_id: 19 + - address: dell.org + minpoll: 7 + maxpoll: 10 + prefer: true + source_interfaces: + - Eth1/8 + - Eth1/16 + trusted_keys: + - 88 + - 99 + ntp_keys: + - key_id: 10 + key_type: NTP_AUTH_MD5 + key_value: "abcd" + - key_id: 20 + key_type: NTP_AUTH_SHA2_256 + key_value: "efgh" + enable_ntp_auth: True + existing_ntp_config: + - path: "data/openconfig-system:system/ntp" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-system:system/ntp/config/enable-ntp-auth" + method: "patch" + data: + openconfig-system:enable-ntp-auth: True + - path: "data/openconfig-system:system/ntp/config/source-interface" + method: "patch" + data: + openconfig-system:source-interface: + - Eth1/8 + - Eth1/16 + - path: "data/openconfig-system:system/ntp/config/trusted-key" + method: "patch" + data: + openconfig-system:trusted-key: + - 88 + - 99 + - path: "data/openconfig-system:system/ntp/ntp-keys" + method: "patch" + data: + openconfig-system:ntp-keys: + ntp-key: + - key-id: 10 + config: + encrypted: + key-id: 10 + key-type: NTP_AUTH_MD5 + key-value: "abcd" + - key-id: 20 + config: + encrypted: + key-id: 20 + key-type: NTP_AUTH_SHA2_256 + key-value: "efgh" + - path: "data/openconfig-system:system/ntp/servers" + method: "patch" + data: + openconfig-system:servers: + server: + - address: 10.11.0.2 + config: + address: 10.11.0.2 + minpoll: 5 + maxpoll: 12 + key-id: 19 + - address: dell.org + config: + address: dell.org + minpoll: 7 + maxpoll: 10 + prefer: True +merged_02: + module_args: + config: + servers: + - address: 10.11.0.2 + minpoll: 5 + maxpoll: 12 + - address: dell.org + minpoll: 7 + maxpoll: 10 + prefer: true + source_interfaces: + - Eth1/8 + - Eth1/16 + ntp_keys: + - key_id: 10 + - key_id: 20 + existing_ntp_config: + - path: "data/openconfig-system:system/ntp" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-system:system/ntp/config/source-interface" + method: "patch" + data: + openconfig-system:source-interface: + - Eth1/8 + - Eth1/16 + - path: "data/openconfig-system:system/ntp/ntp-keys" + method: "patch" + data: + openconfig-system:ntp-keys: + ntp-key: + - key-id: 10 + config: + encrypted: + key-id: 10 + key-type: + key-value: + - key-id: 20 + config: + encrypted: + key-id: 20 + key-type: + key-value: + - path: "data/openconfig-system:system/ntp/servers" + method: "patch" + data: + openconfig-system:servers: + server: + - address: 10.11.0.2 + config: + address: 10.11.0.2 + minpoll: 5 + maxpoll: 12 + - address: dell.org + config: + address: dell.org + minpoll: 7 + maxpoll: 10 + prefer: True +deleted_01: + module_args: + state: deleted + existing_ntp_config: + - path: "data/openconfig-system:system/ntp" + response: + code: 200 + value: + openconfig-system:ntp: + servers: + server: + - address: 10.11.0.2 + config: + address: 10.11.0.2 + minpoll: 5 + maxpoll: 12 + - address: dell.org + config: + address: dell.org + minpoll: 7 + maxpoll: 10 + prefer: True + config: + source-interface: + - Eth1/8 + - Eth1/16 + enable-ntp-auth: True + trusted-key: + - 77 + - 88 + network-instance: mgmt + ntp-keys: + ntp-key: + - key-id: 11 + config: + key-id: 11 + key-type: NTP_AUTH_MD5 + key-value: "abcd" + - key-id: 12 + config: + key-id: 12 + key-type: NTP_AUTH_SHA2_256 + key-value: "efgh" + + expected_config_requests: + - path: "data/openconfig-system:system/ntp" + method: "delete" + data: + - path: "data/openconfig-system:system/ntp/ntp-keys" + method: "delete" + data: + - path: "data/openconfig-system:system/ntp/servers" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + servers: + - address: 10.11.0.2 + source_interfaces: + - Eth1/8 + ntp_keys: + - key_id: 11 + trusted_keys: + - 88 + vrf: mgmt + existing_ntp_config: + - path: "data/openconfig-system:system/ntp" + response: + code: 200 + value: + openconfig-system:ntp: + servers: + server: + - address: 10.11.0.2 + config: + address: 10.11.0.2 + minpoll: 5 + maxpoll: 12 + - address: dell.org + config: + address: dell.org + minpoll: 7 + maxpoll: 10 + prefer: True + config: + source-interface: + - Eth1/8 + - Eth1/16 + enable-ntp-auth: True + trusted-key: + - 77 + - 88 + network-instance: mgmt + ntp-keys: + ntp-key: + - key-id: 11 + config: + key-id: 11 + key-type: NTP_AUTH_MD5 + key-value: "abcd" + - key-id: 12 + config: + key-id: 12 + key-type: NTP_AUTH_SHA2_256 + key-value: "efgh" + + expected_config_requests: + - path: "data/openconfig-system:system/ntp/config/network-instance" + method: "delete" + data: + - path: "data/openconfig-system:system/ntp/config/source-interface=Eth1%2f8" + method: "delete" + data: + - path: "data/openconfig-system:system/ntp/config/trusted-key=88" + method: "delete" + data: + - path: "data/openconfig-system:system/ntp/ntp-keys/ntp-key=11" + method: "delete" + data: + - path: "data/openconfig-system:system/ntp/servers/server=10.11.0.2" + method: "delete" + data: + +replaced_01: + module_args: + state: replaced + config: + servers: + - address: 10.11.0.3 + minpoll: 6 + maxpoll: 13 + key_id: 20 + source_interfaces: + - Eth1/9 + enable_ntp_auth: False + trusted_keys: + ntp_keys: + - key_id: 30 + key_type: NTP_AUTH_MD5 + key_value: "ntpkey30Value" + vrf: mgmt + existing_ntp_config: + - path: "data/openconfig-system:system/ntp" + response: + code: 200 + value: + openconfig-system:ntp: + servers: + server: + - address: 10.11.0.2 + config: + address: 10.11.0.2 + minpoll: 5 + maxpoll: 12 + - address: dell.org + config: + address: dell.org + minpoll: 7 + maxpoll: 10 + prefer: True + config: + source-interface: + - Eth1/8 + - Eth1/16 + enable-ntp-auth: True + trusted-key: + - 77 + - 88 + network-instance: mgmt + ntp-keys: + ntp-key: + - key-id: 11 + config: + key-id: 11 + key-type: NTP_AUTH_MD5 + key-value: "abcd" + - key-id: 12 + config: + key-id: 12 + key-type: NTP_AUTH_SHA2_256 + key-value: "efgh" + + expected_config_requests: + - path: "data/openconfig-system:system/ntp" + method: "delete" + data: + - path: "data/openconfig-system:system/ntp/config/enable-ntp-auth" + method: "patch" + data: + openconfig-system:enable-ntp-auth: False + - path: "data/openconfig-system:system/ntp/config/network-instance" + method: "patch" + data: + openconfig-system:network-instance: mgmt + - path: "data/openconfig-system:system/ntp/config/source-interface" + method: "patch" + data: + openconfig-system:source-interface: + - Eth1/9 + - path: "data/openconfig-system:system/ntp/ntp-keys" + method: "delete" + data: + - path: "data/openconfig-system:system/ntp/ntp-keys" + method: "patch" + data: + openconfig-system:ntp-keys: + ntp-key: + - key-id: 30 + config: + encrypted: + key-id: 30 + key-type: NTP_AUTH_MD5 + key-value: "ntpkey30Value" + - path: "data/openconfig-system:system/ntp/servers" + method: "delete" + data: + - path: "data/openconfig-system:system/ntp/servers" + method: "patch" + data: + openconfig-system:servers: + server: + - address: 10.11.0.3 + config: + address: 10.11.0.3 + minpoll: 6 + maxpoll: 13 + prefer: False + key-id: 20 + +overridden_01: + module_args: + state: overridden + config: + servers: + - address: 10.11.0.3 + minpoll: 6 + maxpoll: 13 + key_id: 20 + source_interfaces: + - Eth1/9 + enable_ntp_auth: False + trusted_keys: + - 66 + ntp_keys: + - key_id: 30 + key_type: NTP_AUTH_MD5 + key_value: "ntpkey30Value" + vrf: mgmt + existing_ntp_config: + - path: "data/openconfig-system:system/ntp" + response: + code: 200 + value: + openconfig-system:ntp: + servers: + server: + - address: 10.11.0.2 + config: + address: 10.11.0.2 + minpoll: 5 + maxpoll: 12 + - address: dell.org + config: + address: dell.org + minpoll: 7 + maxpoll: 10 + prefer: True + config: + source-interface: + - Eth1/8 + - Eth1/16 + enable-ntp-auth: True + trusted-key: + - 77 + - 88 + network-instance: mgmt + ntp-keys: + ntp-key: + - key-id: 11 + config: + key-id: 11 + key-type: NTP_AUTH_MD5 + key-value: "abcd" + - key-id: 12 + config: + key-id: 12 + key-type: NTP_AUTH_SHA2_256 + key-value: "efgh" + + expected_config_requests: + - path: "data/openconfig-system:system/ntp" + method: "delete" + data: + - path: "data/openconfig-system:system/ntp/config/enable-ntp-auth" + method: "patch" + data: + openconfig-system:enable-ntp-auth: False + - path: "data/openconfig-system:system/ntp/config/network-instance" + method: "patch" + data: + openconfig-system:network-instance: mgmt + - path: "data/openconfig-system:system/ntp/config/source-interface" + method: "patch" + data: + openconfig-system:source-interface: + - Eth1/9 + - path: "data/openconfig-system:system/ntp/config/trusted-key" + method: "patch" + data: + openconfig-system:trusted-key: + - 66 + - path: "data/openconfig-system:system/ntp/ntp-keys" + method: "delete" + data: + - path: "data/openconfig-system:system/ntp/ntp-keys" + method: "patch" + data: + openconfig-system:ntp-keys: + ntp-key: + - key-id: 30 + config: + encrypted: + key-id: 30 + key-type: NTP_AUTH_MD5 + key-value: "ntpkey30Value" + - path: "data/openconfig-system:system/ntp/servers" + method: "delete" + data: + - path: "data/openconfig-system:system/ntp/servers" + method: "patch" + data: + openconfig-system:servers: + server: + - address: 10.11.0.3 + config: + address: 10.11.0.3 + minpoll: 6 + maxpoll: 13 + prefer: False + key-id: 20 diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_pki.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_pki.yaml new file mode 100644 index 000000000..8606b8b4f --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_pki.yaml @@ -0,0 +1,187 @@ +--- +merged_01: + module_args: + config: + security_profiles: + - profile_name: rest + ocsp_responder_list: + - http://example.com/ocspa + - http://example.com/ocspb + certificate_name: host + trust_store: default-ts + trust_stores: + - name: default-ts + ca_name: + - CA2 + state: merged + + existing_pki_config: + - path: "data/openconfig-pki:pki/" + response: + code: 200 + value: {} + expected_config_requests: + - path: "data/openconfig-pki:pki/security-profiles/security-profile" + method: "patch" + data: + openconfig-pki:security-profile: + - profile-name: rest + config: + ocsp-responder-list: + - http://example.com/ocspa + - http://example.com/ocspb + certificate-name: host + trust-store: default-ts + - path: "data/openconfig-pki:pki/trust-stores/trust-store" + method: "patch" + data: + openconfig-pki:trust-store: + - name: default-ts + config: + name: default-ts + ca-name: + - CA2 + +deleted_01: + module_args: + config: + security_profiles: + - profile_name: rest + trust_store: default-ts + state: deleted + existing_pki_config: + - path: "data/openconfig-pki:pki/" + response: + code: 200 + value: + openconfig-pki:pki: + security-profiles: + security-profile: + - profile-name: rest + config: + ocsp-responder-list: + - http://example.com/ocspa + - http://example.com/ocspb + certificate-name: host + trust-store: default-ts + profile-name: rest + trust-stores: + trust-store: + - name: default-ts + config: + name: default-ts + ca-name: + - CA2 + expected_config_requests: + - path: "data/openconfig-pki:pki/security-profiles/security-profile=rest/config/trust-store" + method: "delete" + data: + +replaced_01: + module_args: + config: + security_profiles: + - profile_name: rest + ocsp_responder_list: + - http://example.com/ocsp + key_usage_check: True + state: replaced + + existing_pki_config: + - path: "data/openconfig-pki:pki/" + response: + code: 200 + value: + openconfig-pki:pki: + security-profiles: + security-profile: + - profile-name: rest + config: + ocsp-responder-list: + - http://example.com/ocspa + - http://example.com/ocspb + certificate-name: host + trust-store: default-ts + profile-name: rest + trust-stores: + trust-store: + - name: default-ts + config: + name: default-ts + ca-name: + - CA2 + expected_config_requests: + - path: "data/openconfig-pki:pki/security-profiles/security-profile=rest" + method: "put" + data: + openconfig-pki:security-profile: + - profile-name: rest + config: + profile-name: rest + ocsp-responder-list: + - http://example.com/ocsp + key-usage-check: True + +overridden_01: + module_args: + config: + security_profiles: + - profile_name: telemetry + ocsp_responder_list: + - http://example.com/ocsp + certificate_name: host2 + trust_stores: + - name: telem-ts + ca_name: + - CA + state: overridden + + existing_pki_config: + - path: "data/openconfig-pki:pki/" + response: + code: 200 + value: + openconfig-pki:pki: + security-profiles: + security-profile: + - profile-name: rest + config: + ocsp-responder-list: + - http://example.com/ocspa + - http://example.com/ocspb + certificate-name: host + trust-store: default-ts + profile-name: rest + trust-stores: + trust-store: + - name: default-ts + config: + name: default-ts + ca-name: + - CA2 + expected_config_requests: + - path: "data/openconfig-pki:pki/security-profiles/security-profile=rest" + method: "delete" + data: + - path: "data/openconfig-pki:pki/trust-stores/trust-store=default-ts" + method: "delete" + data: + - path: "data/openconfig-pki:pki/security-profiles/security-profile=telemetry" + method: "put" + data: + openconfig-pki:security-profile: + - profile-name: telemetry + config: + profile-name: telemetry + ocsp-responder-list: + - http://example.com/ocsp + certificate-name: host2 + - path: "data/openconfig-pki:pki/trust-stores/trust-store=telem-ts" + method: "put" + data: + openconfig-pki:trust-store: + - name: telem-ts + config: + name: telem-ts + ca-name: + - CA diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_port_breakout.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_port_breakout.yaml new file mode 100644 index 000000000..81c86886a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_port_breakout.yaml @@ -0,0 +1,252 @@ +--- +merged_01: + module_args: + config: + - name: 1/10 + mode: 1x100G + existing_port_breakout_config: + - path: "data/sonic-port-breakout:sonic-port-breakout/BREAKOUT_CFG/BREAKOUT_CFG_LIST" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-platform:components" + method: "patch" + data: + openconfig-platform:components: + component: + - name: 1/10 + port: + openconfig-platform-port:breakout-mode: + groups: + group: + - index: 1 + config: + index: 1 + num-breakouts: 1 + breakout-speed: SPEED_100GB +deleted_01: + module_args: + state: deleted + existing_port_breakout_config: + - path: "data/sonic-port-breakout:sonic-port-breakout/BREAKOUT_CFG/BREAKOUT_CFG_LIST" + response: + code: 200 + value: + sonic-port-breakout:BREAKOUT_CFG_LIST: + - port: 1/10 + brkout_mode: 1x100G + - path: "data/openconfig-platform:components/component=1%2f10" + response: + code: 200 + value: + openconfig-platform:component: + - name: 1/10 + port: + openconfig-platform-port:breakout-mode: + groups: + group: + - index: 1 + config: + index: 1 + breakout-speed: openconfig-if-ethernet:SPEED_100GB + num-breakouts: 1 + expected_config_requests: + - path: "data/openconfig-platform:components/component=1%2f10/port/openconfig-platform-port:breakout-mode" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + - name: 1/10 + mode: 1x100G + existing_port_breakout_config: + - path: "data/sonic-port-breakout:sonic-port-breakout/BREAKOUT_CFG/BREAKOUT_CFG_LIST" + response: + code: 200 + value: + sonic-port-breakout:BREAKOUT_CFG_LIST: + - port: 1/10 + brkout_mode: 1x100G + - path: "data/openconfig-platform:components/component=1%2f10" + response: + code: 200 + value: + openconfig-platform:component: + - name: 1/10 + port: + openconfig-platform-port:breakout-mode: + groups: + group: + - index: 1 + config: + index: 1 + breakout-speed: openconfig-if-ethernet:SPEED_100GB + num-breakouts: 1 + expected_config_requests: + - path: "data/openconfig-platform:components/component=1%2f10/port/openconfig-platform-port:breakout-mode" + method: "delete" + data: + +replaced_01: + module_args: + config: + - name: 1/10 + mode: 1x100G + - name: 1/12 + mode: 4x25G + state: replaced + existing_port_breakout_config: + - path: "data/sonic-port-breakout:sonic-port-breakout/BREAKOUT_CFG/BREAKOUT_CFG_LIST" + response: + code: 200 + value: + sonic-port-breakout:BREAKOUT_CFG_LIST: + - port: 1/10 + brkout_mode: 4x10G + - port: 1/11 + brkout_mode: 1x100G + - path: "data/openconfig-platform:components/component=1%2f10" + response: + code: 200 + value: + openconfig-platform:component: + - name: 1/10 + port: + openconfig-platform-port:breakout-mode: + groups: + group: + - index: 1 + config: + index: 1 + breakout-speed: openconfig-if-ethernet:SPEED_10GB + num-breakouts: 4 + - path: "data/openconfig-platform:components/component=1%2f11" + response: + code: 200 + value: + openconfig-platform:component: + - name: 1/11 + port: + openconfig-platform-port:breakout-mode: + groups: + group: + - index: 1 + config: + index: 1 + breakout-speed: openconfig-if-ethernet:SPEED_100GB + num-breakouts: 1 + expected_config_requests: + - path: "data/openconfig-platform:components" + method: "patch" + data: + openconfig-platform:components: + component: + - name: 1/10 + port: + openconfig-platform-port:breakout-mode: + groups: + group: + - index: 1 + config: + index: 1 + num-breakouts: 1 + breakout-speed: SPEED_100GB + - path: "data/openconfig-platform:components" + method: "patch" + data: + openconfig-platform:components: + component: + - name: 1/12 + port: + openconfig-platform-port:breakout-mode: + groups: + group: + - index: 1 + config: + index: 1 + num-breakouts: 4 + breakout-speed: SPEED_25GB + +overridden_01: + module_args: + config: + - name: 1/10 + mode: 1x100G + - name: 1/12 + mode: 4x25G + state: overridden + existing_port_breakout_config: + - path: "data/sonic-port-breakout:sonic-port-breakout/BREAKOUT_CFG/BREAKOUT_CFG_LIST" + response: + code: 200 + value: + sonic-port-breakout:BREAKOUT_CFG_LIST: + - port: 1/10 + brkout_mode: 4x10G + - port: 1/11 + brkout_mode: 1x100G + - path: "data/openconfig-platform:components/component=1%2f10" + response: + code: 200 + value: + openconfig-platform:component: + - name: 1/10 + port: + openconfig-platform-port:breakout-mode: + groups: + group: + - index: 1 + config: + index: 1 + breakout-speed: openconfig-if-ethernet:SPEED_10GB + num-breakouts: 4 + - path: "data/openconfig-platform:components/component=1%2f11" + response: + code: 200 + value: + openconfig-platform:component: + - name: 1/11 + port: + openconfig-platform-port:breakout-mode: + groups: + group: + - index: 1 + config: + index: 1 + breakout-speed: openconfig-if-ethernet:SPEED_100GB + num-breakouts: 1 + expected_config_requests: + - path: "data/openconfig-platform:components/component=1%2f11/port/openconfig-platform-port:breakout-mode" + method: "delete" + - path: "data/openconfig-platform:components" + method: "patch" + data: + openconfig-platform:components: + component: + - name: 1/10 + port: + openconfig-platform-port:breakout-mode: + groups: + group: + - index: 1 + config: + index: 1 + num-breakouts: 1 + breakout-speed: SPEED_100GB + - path: "data/openconfig-platform:components" + method: "patch" + data: + openconfig-platform:components: + component: + - name: 1/12 + port: + openconfig-platform-port:breakout-mode: + groups: + group: + - index: 1 + config: + index: 1 + num-breakouts: 4 + breakout-speed: SPEED_25GB diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_port_group.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_port_group.yaml new file mode 100644 index 000000000..e86a7ec4b --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_port_group.yaml @@ -0,0 +1,115 @@ +--- +merged_01: + module_args: + config: + - id: 1 + speed: SPEED_10GB + - id: 9 + speed: SPEED_10GB + existing_port_group_config: + - path: "data/openconfig-port-group:port-groups/port-group" + response: + code: 200 + value: + openconfig-port-group:port-group: + - id: 1 + config: + speed: SPEED_100GB + - id: 9 + config: + speed: SPEED_100GB + expected_config_requests: + - path: "data/openconfig-port-group:port-groups/port-group" + method: "patch" + data: + openconfig-port-group:port-group: + - id: '1' + config: + id: '1' + speed: openconfig-if-ethernet:SPEED_10GB + - id: '9' + config: + id: '9' + speed: openconfig-if-ethernet:SPEED_10GB + +deleted_01: + module_args: + state: deleted + existing_port_group_config: + - path: "data/openconfig-port-group:port-groups/port-group" + response: + code: 200 + value: + openconfig-port-group:port-group: + - id: 1 + config: + speed: SPEED_100GB + - id: 9 + config: + speed: SPEED_100GB + expected_config_requests: + - path: "data/openconfig-port-group:port-groups/port-group=1/config/speed" + method: "delete" + data: + - path: "data/openconfig-port-group:port-groups/port-group=9/config/speed" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + - id: 1 + speed: SPEED_100GB + existing_port_group_config: + - path: "data/openconfig-port-group:port-groups/port-group" + response: + code: 200 + value: + openconfig-port-group:port-group: + - id: 1 + config: + speed: SPEED_100GB + - id: 9 + config: + speed: SPEED_100GB + expected_config_requests: + - path: "data/openconfig-port-group:port-groups/port-group=1/config/speed" + method: "delete" + data: + +replaced_01: + module_args: + state: replaced + config: + - id: 1 + speed: SPEED_10GB + - id: 5 + speed: SPEED_40GB + - id: 9 + speed: SPEED_100GB + existing_port_group_config: + - path: "data/openconfig-port-group:port-groups/port-group" + response: + code: 200 + value: + openconfig-port-group:port-group: + - id: 1 + config: + speed: SPEED_100GB + - id: 9 + config: + speed: SPEED_100GB + expected_config_requests: + - path: "data/openconfig-port-group:port-groups/port-group" + method: "patch" + data: + openconfig-port-group:port-group: + - id: '1' + config: + id: '1' + speed: openconfig-if-ethernet:SPEED_10GB + - id: '5' + config: + id: '5' + speed: openconfig-if-ethernet:SPEED_40GB diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_prefix_lists.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_prefix_lists.yaml new file mode 100644 index 000000000..beb7428d5 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_prefix_lists.yaml @@ -0,0 +1,153 @@ +--- +merged_01: + module_args: + config: + - name: pfx1 + afi: "ipv4" + prefixes: + - sequence: 10 + prefix: "1.2.3.4/24" + action: "permit" + ge: 26 + le: 30 + - name: pfx6 + afi: "ipv6" + prefixes: + - sequence: 25 + action: "deny" + prefix: "40::300/124" + existing_prefix_lists_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - name: 'Eth1/1' + config: + mtu: 2000 + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/prefix-sets" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/prefix-sets/prefix-set" + method: "patch" + data: + openconfig-routing-policy:prefix-set: + - name: pfx1 + config: + name: pfx1 + mode: IPV4 + openconfig-routing-policy-ext:extended-prefixes: + extended-prefix: + - ip-prefix: 1.2.3.4/24 + sequence-number: 10 + masklength-range: 26..30 + config: + sequence-number: 10 + ip-prefix: 1.2.3.4/24 + masklength-range: 26..30 + openconfig-routing-policy-ext:action: PERMIT + - name: pfx6 + config: + name: pfx6 + mode: IPV6 + openconfig-routing-policy-ext:extended-prefixes: + extended-prefix: + - ip-prefix: 40::300/124 + sequence-number: 25 + masklength-range: exact + config: + sequence-number: 25 + ip-prefix: 40::300/124 + masklength-range: exact + openconfig-routing-policy-ext:action: DENY +deleted_01: + module_args: + state: deleted + existing_prefix_lists_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/prefix-sets" + response: + code: 200 + value: + openconfig-routing-policy:prefix-sets: + prefix-set: + - config: + mode: IPV4 + name: pfx1 + openconfig-routing-policy-ext:extended-prefixes: + extended-prefix: + - config: + action: PERMIT + ip-prefix: 1.2.3.4/8 + masklength-range: 8..16 + sequence-number: 10 + ip-prefix: 1.2.3.4/8 + masklength-range: 8..16 + sequence-number: 10 + name: pfx1 + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/prefix-sets" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + - name: pfx1 + afi: "ipv4" + prefixes: + - sequence: 10 + prefix: "1.2.3.4/8" + action: "permit" + ge: 9 + le: 16 + - name: pfx2 + afi: "ipv6" + prefixes: + - sequence: 11 + action: "permit" + prefix: "11::22/124" + existing_prefix_lists_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/prefix-sets" + response: + code: 200 + value: + openconfig-routing-policy:prefix-sets: + prefix-set: + - config: + mode: IPV4 + name: pfx1 + openconfig-routing-policy-ext:extended-prefixes: + extended-prefix: + - config: + action: PERMIT + ip-prefix: 1.2.3.4/8 + masklength-range: 9..16 + sequence-number: 10 + ip-prefix: 1.2.3.4/8 + masklength-range: 9..16 + sequence-number: 10 + name: pfx1 + - config: + mode: IPV6 + name: pfx2 + openconfig-routing-policy-ext:extended-prefixes: + extended-prefix: + - config: + action: PERMIT + ip-prefix: 11::22/124 + masklength-range: exact + sequence-number: 11 + ip-prefix: 11::22/124 + masklength-range: exact + sequence-number: 11 + name: pfx2 + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/prefix-sets/prefix-set=pfx1/openconfig-routing-policy-ext:extended-prefixes/extended-prefix=10,1.2.3.4%2F8,9..16" + method: "delete" + data: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/prefix-sets/prefix-set=pfx2/openconfig-routing-policy-ext:extended-prefixes/extended-prefix=11,11::22%2F124,exact" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_radius_server.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_radius_server.yaml new file mode 100644 index 000000000..7e5a6e606 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_radius_server.yaml @@ -0,0 +1,207 @@ +--- +merged_01: + module_args: + config: + auth_type: chap + key: chap + nas_ip: 1.2.3.4 + statistics: true + timeout: 10 + retransmit: 3 + servers: + host: + - name: localhost + auth_type: mschapv2 + key: local + priority: 2 + port: 52 + retransmit: 2 + timeout: 20 + source_interface: Eth1/2 + vrf: mgmt + existing_radius_server_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - name: 'Eth1/1' + config: + mtu: 2000 + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/config" + response: + code: 200 + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/openconfig-aaa-radius-ext:radius/config" + response: + code: 200 + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/servers" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/config" + method: "patch" + data: + openconfig-system:config: + auth-type: 'chap' + secret-key: 'chap' + timeout: 10 + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/openconfig-aaa-radius-ext:radius/config" + method: "patch" + data: + openconfig-aaa-radius-ext:config: + nas-ip-address: 1.2.3.4 + retransmit-attempts: 3 + statistics: True + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/servers" + method: "patch" + data: + openconfig-system:servers: + server: + - address: localhost + config: + address: localhost + auth-type: mschapv2 + priority: 2 + vrf: mgmt + timeout: 20 + radius: + config: + auth-port: 52 + secret-key: local + retransmit-attempts: 2 + openconfig-aaa-radius-ext:source-interface: Eth1/2 + +deleted_01: + module_args: + state: deleted + existing_radius_server_config: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/config" + response: + code: 200 + value: + openconfig-system:config: + auth-type: 'chap' + secret-key: 'chap' + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/openconfig-aaa-radius-ext:radius/config" + response: + code: 200 + value: + openconfig-aaa-radius-ext:config: + nas-ip-address: 1.2.3.4 + retransmit-attempts: 3 + statistics: True + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/servers" + response: + code: 200 + value: + openconfig-system:servers: + server: + - address: localhost + config: + address: localhost + auth-type: mschapv2 + priority: 2 + vrf: mgmt + timeout: 20 + radius: + config: + auth-port: 52 + secret-key: local + retransmit-attempts: 2 + openconfig-aaa-radius-ext:source-interface: Eth1/2 + expected_config_requests: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/config/auth-type" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/config/secret-key" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/openconfig-aaa-radius-ext:radius/config/nas-ip-address" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/openconfig-aaa-radius-ext:radius/config/retransmit-attempts" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/openconfig-aaa-radius-ext:radius/config/statistics" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/servers/server=localhost" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + auth_type: chap + key: chap + nas_ip: 1.2.3.4 + statistics: true + timeout: 10 + retransmit: 3 + servers: + host: + - name: localhost + auth_type: mschapv2 + key: local + priority: 2 + port: 52 + retransmit: 2 + timeout: 20 + source_interface: Eth1/2 + vrf: mgmt + existing_radius_server_config: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/config" + response: + code: 200 + value: + openconfig-system:config: + auth-type: 'chap' + secret-key: 'chap' + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/openconfig-aaa-radius-ext:radius/config" + response: + code: 200 + value: + openconfig-aaa-radius-ext:config: + nas-ip-address: 1.2.3.4 + retransmit-attempts: 3 + statistics: True + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/servers" + response: + code: 200 + value: + openconfig-system:servers: + server: + - address: localhost + config: + address: localhost + auth-type: mschapv2 + priority: 2 + vrf: mgmt + timeout: 20 + radius: + config: + auth-port: 52 + secret-key: local + retransmit-attempts: 2 + openconfig-aaa-radius-ext:source-interface: Eth1/2 + expected_config_requests: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/config/auth-type" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/config/secret-key" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/openconfig-aaa-radius-ext:radius/config/nas-ip-address" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/openconfig-aaa-radius-ext:radius/config/retransmit-attempts" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/openconfig-aaa-radius-ext:radius/config/statistics" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/servers/server=localhost" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_static_routes.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_static_routes.yaml new file mode 100644 index 000000000..c83b622fa --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_static_routes.yaml @@ -0,0 +1,340 @@ +--- +merged_01: + module_args: + config: + - vrf_name: 'default' + static_list: + - prefix: '2.0.0.0/8' + next_hops: + - index: + interface: 'Ethernet4' + metric: 1 + tag: 2 + track: 3 + - index: + next_hop: '3.0.0.0' + metric: 2 + tag: 4 + track: 8 + existing_static_routes_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + response: + code: 200 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=STATIC,static/static-routes" + response: + code: 200 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + method: "patch" + data: + openconfig-network-instance:static-routes: + static: + - prefix: 2.0.0.0/8 + config: + prefix: 2.0.0.0/8 + next-hops: + next-hop: + - index: Ethernet4 + config: + index: Ethernet4 + next-hop: + metric: 1 + track: 3 + tag: 2 + interface-ref: + config: + interface: Ethernet4 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + method: "patch" + data: + openconfig-network-instance:static-routes: + static: + - prefix: 2.0.0.0/8 + config: + prefix: 2.0.0.0/8 + next-hops: + next-hop: + - index: 3.0.0.0 + config: + index: 3.0.0.0 + next-hop: 3.0.0.0 + metric: 2 + track: 8 + tag: 4 + +deleted_01: + module_args: + state: deleted + existing_static_routes_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + response: + code: 200 + value: + openconfig-network-instance:static-routes: + static: + - prefix: 2.0.0.0/8 + config: + prefix: 2.0.0.0/8 + next-hops: + next-hop: + - index: 3.0.0.0 + config: + index: 3.0.0.0 + next-hop: 3.0.0.0 + metric: 2 + track: 8 + tag: 4 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=STATIC,static/static-routes" + response: + code: 200 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + - vrf_name: 'default' + static_list: + - prefix: '2.0.0.0/8' + next_hops: + - index: + next_hop: '3.0.0.0' + metric: 2 + tag: 4 + track: 8 + existing_static_routes_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + response: + code: 200 + value: + openconfig-network-instance:static-routes: + static: + - prefix: 2.0.0.0/8 + config: + prefix: 2.0.0.0/8 + next-hops: + next-hop: + - index: 3.0.0.0 + config: + index: 3.0.0.0 + next-hop: 3.0.0.0 + metric: 2 + track: 8 + tag: 4 + - path: "/data/openconfig-network-instance:network-instances/network-instance=VrfReg1/protocols/protocol=STATIC,static/static-routes" + response: + code: 200 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes/static=2.0.0.0%2F8/next-hops/next-hop=3.0.0.0/config/metric" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes/static=2.0.0.0%2F8/next-hops/next-hop=3.0.0.0/config/tag" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes/static=2.0.0.0%2F8/next-hops/next-hop=3.0.0.0/config/track" + method: "delete" + data: + +replaced_01: + module_args: + state: replaced + config: + - vrf_name: 'default' + static_list: + - prefix: '5.0.0.0/8' + next_hops: + - index: + next_hop: '6.0.0.0' + metric: 4 + tag: 5 + track: 6 + existing_static_routes_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + response: + code: 200 + value: + openconfig-network-instance:static-routes: + static: + - prefix: 2.0.0.0/8 + config: + prefix: 2.0.0.0/8 + next-hops: + next-hop: + - index: 3.0.0.0 + config: + index: 3.0.0.0 + next-hop: 3.0.0.0 + metric: 8 + track: 10 + tag: 12 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + method: "patch" + data: + openconfig-network-instance:static-routes: + static: + - prefix: 5.0.0.0/8 + config: + prefix: 5.0.0.0/8 + next-hops: + next-hop: + - index: 6.0.0.0 + config: + index: 6.0.0.0 + next-hop: 6.0.0.0 + blackhole: false + metric: 4 + track: 6 + tag: 5 + +replaced_02: + module_args: + state: replaced + config: + - vrf_name: 'default' + static_list: + - prefix: '2.0.0.0/8' + next_hops: + - index: + next_hop: '3.0.0.0' + metric: 4 + tag: 5 + track: 6 + existing_static_routes_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + response: + code: 200 + value: + openconfig-network-instance:static-routes: + static: + - prefix: 2.0.0.0/8 + config: + prefix: 2.0.0.0/8 + next-hops: + next-hop: + - index: 3.0.0.0 + config: + index: 3.0.0.0 + next-hop: 3.0.0.0 + metric: 8 + track: 10 + tag: 12 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + method: "patch" + data: + openconfig-network-instance:static-routes: + static: + - prefix: 2.0.0.0/8 + config: + prefix: 2.0.0.0/8 + next-hops: + next-hop: + - index: 3.0.0.0 + config: + index: 3.0.0.0 + next-hop: 3.0.0.0 + blackhole: false + metric: 4 + track: 6 + tag: 5 + +overridden_01: + module_args: + state: overridden + config: + - vrf_name: 'default' + static_list: + - prefix: '5.0.0.0/8' + next_hops: + - index: + next_hop: '6.0.0.0' + metric: 4 + tag: 5 + track: 6 + existing_static_routes_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + response: + code: 200 + value: + openconfig-network-instance:static-routes: + static: + - prefix: 2.0.0.0/8 + config: + prefix: 2.0.0.0/8 + next-hops: + next-hop: + - index: 3.0.0.0 + config: + index: 3.0.0.0 + next-hop: 3.0.0.0 + metric: 8 + track: 10 + tag: 12 + expected_config_requests: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + method: "delete" + data: + - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" + method: "patch" + data: + openconfig-network-instance:static-routes: + static: + - prefix: 5.0.0.0/8 + config: + prefix: 5.0.0.0/8 + next-hops: + next-hop: + - index: 6.0.0.0 + config: + index: 6.0.0.0 + next-hop: 6.0.0.0 + blackhole: false + metric: 4 + track: 6 + tag: 5 diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_stp.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_stp.yaml new file mode 100644 index 000000000..4aa7c6974 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_stp.yaml @@ -0,0 +1,991 @@ +--- +merged_01: + module_args: + config: + global: + enabled_protocol: mst + loop_guard: true + bpdu_filter: true + disabled_vlans: + - 4-6 + hello_time: 5 + max_age: 10 + fwd_delay: 15 + bridge_priority: 4096 + interfaces: + - intf_name: Ethernet20 + edge_port: true + link_type: shared + guard: loop + bpdu_guard: true + bpdu_filter: true + portfast: true + uplink_fast: true + shutdown: true + cost: 20 + port_priority: 30 + stp_enable: true + mstp: + mst_name: mst1 + revision: 1 + max_hop: 3 + hello_time: 6 + max_age: 9 + fwd_delay: 12 + mst_instances: + - mst_id: 1 + bridge_priority: 2048 + vlans: + - 1 + interfaces: + - intf_name: Ethernet20 + cost: 60 + port_priority: 65 + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp/global" + method: "patch" + data: + openconfig-spanning-tree:global: + config: + enabled-protocol: + - 'MSTP' + loop-guard: True + bpdu-filter: True + openconfig-spanning-tree-ext:disabled-vlans: + - '4..6' + openconfig-spanning-tree-ext:hello-time: 5 + openconfig-spanning-tree-ext:max-age: 10 + openconfig-spanning-tree-ext:bridge-priority: 4096 + - path: "data/openconfig-spanning-tree:stp/interfaces" + method: "patch" + data: + openconfig-spanning-tree:interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + edge-port: EDGE_ENABLE + link-type: SHARED + guard: LOOP + bpdu-guard: True + bpdu-filter: True + openconfig-spanning-tree-ext:portfast: True + openconfig-spanning-tree-ext:uplink-fast: True + openconfig-spanning-tree-ext:bpdu-guard-port-shutdown: True + openconfig-spanning-tree-ext:cost: 20 + openconfig-spanning-tree-ext:port-priority: 30 + openconfig-spanning-tree-ext:spanning-tree-enable: True + - path: "data/openconfig-spanning-tree:stp/mstp" + method: "patch" + data: + openconfig-spanning-tree:mstp: + config: + name: mst1 + revision: 1 + max-hop: 3 + hello-time: 6 + max-age: 9 + forwarding-delay: 12 + mst-instances: + mst-instance: + - mst-id: 1 + config: + mst-id: 1 + bridge-priority: 2048 + vlan: + - 1 + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + cost: 60 + port-priority: 65 +merged_02: + module_args: + config: + global: + enabled_protocol: pvst + bpdu_filter: true + root_guard_timeout: 25 + portfast: true + hello_time: 5 + max_age: 10 + fwd_delay: 20 + bridge_priority: 4096 + pvst: + - vlan_id: 1 + hello_time: 4 + max_age: 6 + fwd_delay: 8 + bridge_priority: 4096 + interfaces: + - intf_name: Ethernet20 + cost: 10 + port_priority: 50 + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp/global" + method: "patch" + data: + openconfig-spanning-tree:global: + config: + enabled-protocol: ['PVST'] + bpdu-filter: True + openconfig-spanning-tree-ext:rootguard-timeout: 25 + openconfig-spanning-tree-ext:portfast: True + openconfig-spanning-tree-ext:hello-time: 5 + openconfig-spanning-tree-ext:max-age: 10 + openconfig-spanning-tree-ext:forwarding-delay: 20 + openconfig-spanning-tree-ext:bridge-priority: 4096 + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst" + method: "patch" + data: + openconfig-spanning-tree-ext:pvst: + vlans: + - vlan-id: 1 + config: + vlan-id: 1 + hello-time: 4 + max-age: 6 + forwarding-delay: 8 + bridge-priority: 4096 + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + cost: 10 + port-priority: 50 +merged_03: + module_args: + config: + global: + enabled_protocol: rapid_pvst + bpdu_filter: true + root_guard_timeout: 25 + hello_time: 5 + max_age: 10 + fwd_delay: 20 + bridge_priority: 4096 + rapid_pvst: + - vlan_id: 1 + hello_time: 4 + max_age: 6 + fwd_delay: 8 + bridge_priority: 4096 + interfaces: + - intf_name: Ethernet20 + cost: 10 + port_priority: 50 + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp/global" + method: "patch" + data: + openconfig-spanning-tree:global: + config: + enabled-protocol: ['RAPID_PVST'] + bpdu-filter: True + openconfig-spanning-tree-ext:rootguard-timeout: 25 + openconfig-spanning-tree-ext:hello-time: 5 + openconfig-spanning-tree-ext:max-age: 10 + openconfig-spanning-tree-ext:forwarding-delay: 20 + openconfig-spanning-tree-ext:bridge-priority: 4096 + - path: "data/openconfig-spanning-tree:stp/rapid-pvst" + method: "patch" + data: + openconfig-spanning-tree:rapid-pvst: + vlan: + - vlan-id: 1 + config: + vlan-id: 1 + hello-time: 4 + max-age: 6 + forwarding-delay: 8 + bridge-priority: 4096 + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + cost: 10 + port-priority: 50 +replaced_01: + module_args: + config: + mstp: + mst_name: mst1 + revision: 1 + max_hop: 3 + hello_time: 6 + max_age: 9 + fwd_delay: 12 + state: replaced + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + value: + openconfig-spanning-tree:stp: + mstp: + config: + name: mst2 + revision: 2 + max-hop: 4 + hello-time: 7 + max-age: 10 + forwarding-delay: 13 + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp/mstp/config" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/mstp/mst-instances" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/mstp" + method: "patch" + data: + openconfig-spanning-tree:mstp: + config: + name: mst1 + revision: 1 + max-hop: 3 + hello-time: 6 + max-age: 9 + forwarding-delay: 12 +replaced_02: + module_args: + config: + mstp: + mst_instances: + - mst_id: 1 + interfaces: + - intf_name: Ethernet20 + cost: 60 + port_priority: 65 + state: replaced + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + value: + openconfig-spanning-tree:stp: + mstp: + mst-instances: + mst-instance: + - mst-id: 1 + config: + mst-id: 1 + vlan: + - 1 + bridge-priority: 2048 + interfaces: + interface: + - name: + config: + name: Ethernet20 + cost: 10 + port-priority: 50 + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp/mstp/mst-instances/mst-instance=1/interfaces/interface=Ethernet20" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/mstp" + method: "patch" + data: + openconfig-spanning-tree:mstp: + mst-instances: + mst-instance: + - mst-id: 1 + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + cost: 60 + port-priority: 65 +replaced_03: + module_args: + config: + mstp: + mst_instances: + - mst_id: 1 + bridge_priority: 1024 + vlans: + - 2-3 + state: replaced + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + value: + openconfig-spanning-tree:stp: + mstp: + mst-instances: + mst-instance: + - mst-id: 1 + config: + mst-id: 1 + vlan: + - 1 + bridge-priority: 2048 + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp/mstp/mst-instances/mst-instance=1" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/mstp" + method: "patch" + data: + openconfig-spanning-tree:mstp: + mst-instances: + mst-instance: + - mst-id: 1 + config: + mst-id: 1 + bridge-priority: 1024 + vlan: + - '2..3' +replaced_04: + module_args: + config: + pvst: + - vlan_id: 1 + hello_time: 7 + max_age: 8 + fwd_delay: 9 + bridge_priority: 8192 + - vlan_id: 2 + interfaces: + - intf_name: Ethernet20 + cost: 2 + port_priority: 60 + state: replaced + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + value: + openconfig-spanning-tree:stp: + openconfig-spanning-tree-ext:pvst: + vlans: + - vlan-id: 1 + config: + vlan-id: 1 + hello-time: 6 + max-age: 7 + forwarding-delay: 8 + bridge-priority: 4096 + interfaces: + interface: + - name: Ethernet24 + config: + name: Ethernet24 + cost: 40 + port-priority: 45 + - vlan-id: 2 + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + cost: 1 + port-priority: 55 + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlans=1/config/hello-time" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlans=1/config/max-age" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlans=1/config/forwarding-delay" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlans=1/config/bridge-priority" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlans=1/interfaces/interface=Ethernet24" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlans=2/interfaces/interface=Ethernet20" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst" + method: "patch" + data: + openconfig-spanning-tree-ext:pvst: + vlans: + - vlan-id: 1 + config: + vlan-id: 1 + hello-time: 7 + max-age: 8 + forwarding-delay: 9 + bridge-priority: 8192 + - vlan-id: 2 + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + cost: 2 + port-priority: 60 +replaced_05: + module_args: + config: + rapid_pvst: + - vlan_id: 1 + hello_time: 7 + max_age: 8 + fwd_delay: 9 + bridge_priority: 8192 + - vlan_id: 2 + interfaces: + - intf_name: Ethernet20 + cost: 2 + port_priority: 60 + state: replaced + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + value: + openconfig-spanning-tree:stp: + rapid-pvst: + vlan: + - vlan-id: 1 + config: + vlan-id: 1 + hello-time: 6 + max-age: 7 + forwarding-delay: 8 + bridge-priority: 4096 + interfaces: + interface: + - name: Ethernet24 + config: + name: Ethernet24 + cost: 40 + port-priority: 45 + - vlan-id: 2 + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + cost: 1 + port-priority: 55 + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst/vlan=1/config/hello-time" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst/vlan=1/config/max-age" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst/vlan=1/config/forwarding-delay" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst/vlan=1/config/bridge-priority" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst/vlan=1/interfaces/interface=Ethernet24" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst/vlan=2/interfaces/interface=Ethernet20" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst" + method: "patch" + data: + openconfig-spanning-tree:rapid-pvst: + vlan: + - vlan-id: 1 + config: + vlan-id: 1 + hello-time: 7 + max-age: 8 + forwarding-delay: 9 + bridge-priority: 8192 + - vlan-id: 2 + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + cost: 2 + port-priority: 60 +replaced_06: + module_args: + config: + global: + enabled_protocol: mst + loop_guard: true + bpdu_filter: true + disabled_vlans: + - 4-6 + hello_time: 5 + max_age: 10 + fwd_delay: 15 + bridge_priority: 4096 + state: replaced + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + value: + openconfig-spanning-tree:stp: + global: + config: + enabled-protocol: + - openconfig-spanning-tree-ext:PVST + bpdu-filter: True + openconfig-spanning-tree-ext:rootguard-timeout: 25 + openconfig-spanning-tree-ext:portfast: True + openconfig-spanning-tree-ext:hello-time: 5 + openconfig-spanning-tree-ext:max-age: 10 + openconfig-spanning-tree-ext:forwarding-delay: 20 + openconfig-spanning-tree-ext:bridge-priority: 4096 + expected_config_requests: + - path: "/data/openconfig-spanning-tree:stp" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/global" + method: "patch" + data: + openconfig-spanning-tree:global: + config: + enabled-protocol: + - 'MSTP' + loop-guard: True + bpdu-filter: True + openconfig-spanning-tree-ext:disabled-vlans: + - '4..6' + openconfig-spanning-tree-ext:hello-time: 5 + openconfig-spanning-tree-ext:max-age: 10 + openconfig-spanning-tree-ext:bridge-priority: 4096 +replaced_07: + module_args: + config: + interfaces: + - intf_name: Ethernet20 + edge_port: true + link_type: shared + guard: loop + bpdu_guard: true + bpdu_filter: true + portfast: true + uplink_fast: true + shutdown: true + cost: 20 + port_priority: 30 + state: replaced + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + value: + openconfig-spanning-tree:stp: + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + edge-port: openconfig-spanning-tree-types:EDGE_DISABLE + link-type: P2P + guard: ROOT + bpdu-guard: false + bpdu-filter: false + openconfig-spanning-tree-ext:portfast: false + openconfig-spanning-tree-ext:uplink-fast: false + openconfig-spanning-tree-ext:bpdu-guard-port-shutdown: false + openconfig-spanning-tree-ext:cost: 15 + openconfig-spanning-tree-ext:port-priority: 25 + openconfig-spanning-tree-ext:spanning-tree-enable: false + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp/interfaces/interface=Ethernet20" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/interfaces" + method: "patch" + data: + openconfig-spanning-tree:interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + edge-port: EDGE_ENABLE + link-type: SHARED + guard: LOOP + bpdu-guard: True + bpdu-filter: True + openconfig-spanning-tree-ext:portfast: True + openconfig-spanning-tree-ext:uplink-fast: True + openconfig-spanning-tree-ext:bpdu-guard-port-shutdown: True + openconfig-spanning-tree-ext:cost: 20 + openconfig-spanning-tree-ext:port-priority: 30 + openconfig-spanning-tree-ext:spanning-tree-enable: True +overridden_01: + module_args: + config: + global: + enabled_protocol: mst + loop_guard: true + bpdu_filter: true + disabled_vlans: + - 4-6 + hello_time: 5 + max_age: 10 + fwd_delay: 15 + bridge_priority: 4096 + state: overridden + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + value: + openconfig-spanning-tree:stp: + global: + config: + enabled-protocol: + - openconfig-spanning-tree-ext:PVST + bpdu-filter: True + openconfig-spanning-tree-ext:rootguard-timeout: 25 + openconfig-spanning-tree-ext:portfast: True + openconfig-spanning-tree-ext:hello-time: 5 + openconfig-spanning-tree-ext:max-age: 10 + openconfig-spanning-tree-ext:forwarding-delay: 20 + openconfig-spanning-tree-ext:bridge-priority: 4096 + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/global" + method: "patch" + data: + openconfig-spanning-tree:global: + config: + enabled-protocol: + - 'MSTP' + loop-guard: True + bpdu-filter: True + openconfig-spanning-tree-ext:disabled-vlans: + - '4..6' + openconfig-spanning-tree-ext:hello-time: 5 + openconfig-spanning-tree-ext:max-age: 10 + openconfig-spanning-tree-ext:bridge-priority: 4096 +deleted_01: + module_args: + config: + global: + enabled_protocol: pvst + loop_guard: true + bpdu_filter: true + disabled_vlans: + - 4-6 + root_guard_timeout: 25 + portfast: true + hello_time: 5 + max_age: 10 + fwd_delay: 20 + bridge_priority: 4096 + state: deleted + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + value: + openconfig-spanning-tree:stp: + global: + config: + enabled-protocol: + - openconfig-spanning-tree-ext:PVST + loop-guard: True + bpdu-filter: True + openconfig-spanning-tree-ext:disabled-vlans: + - 4-6 + openconfig-spanning-tree-ext:rootguard-timeout: 25 + openconfig-spanning-tree-ext:portfast: True + openconfig-spanning-tree-ext:hello-time: 5 + openconfig-spanning-tree-ext:max-age: 10 + openconfig-spanning-tree-ext:forwarding-delay: 20 + openconfig-spanning-tree-ext:bridge-priority: 4096 + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp/global/config/enabled-protocol" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/global/config/loop-guard" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/global/config/bpdu-filter" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/global/config/openconfig-spanning-tree-ext:disabled-vlans=4..6" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/global/config/openconfig-spanning-tree-ext:rootguard-timeout" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/global/config/openconfig-spanning-tree-ext:portfast" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/global/config/openconfig-spanning-tree-ext:hello-time" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/global/config/openconfig-spanning-tree-ext:max-age" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/global/config/openconfig-spanning-tree-ext:forwarding-delay" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/global/config/openconfig-spanning-tree-ext:bridge-priority" + method: "delete" + data: +deleted_02: + module_args: + config: + interfaces: + - intf_name: Ethernet20 + edge_port: true + link_type: point-to-point + guard: root + bpdu_guard: true + bpdu_filter: true + portfast: true + uplink_fast: true + shutdown: true + cost: 15 + port_priority: 25 + stp_enable: false + - intf_name: Ethernet24 + state: deleted + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + value: + openconfig-spanning-tree:stp: + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + edge-port: openconfig-spanning-tree-types:EDGE_ENABLE + link-type: P2P + guard: ROOT + bpdu-guard: True + bpdu-filter: True + openconfig-spanning-tree-ext:portfast: True + openconfig-spanning-tree-ext:uplink-fast: True + openconfig-spanning-tree-ext:bpdu-guard-port-shutdown: True + openconfig-spanning-tree-ext:cost: 15 + openconfig-spanning-tree-ext:port-priority: 25 + openconfig-spanning-tree-ext:spanning-tree-enable: False + - name: Ethernet24 + config: + name: Ethernet24 + edge-port: openconfig-spanning-tree-types:EDGE_ENABLE + link-type: P2P + guard: ROOT + bpdu-guard: True + bpdu-filter: True + openconfig-spanning-tree-ext:portfast: True + openconfig-spanning-tree-ext:uplink-fast: True + openconfig-spanning-tree-ext:bpdu-guard-port-shutdown: True + openconfig-spanning-tree-ext:cost: 15 + openconfig-spanning-tree-ext:port-priority: 25 + openconfig-spanning-tree-ext:spanning-tree-enable: True + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp/interfaces/interface=Ethernet20/config/edge-port" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/interfaces/interface=Ethernet20/config/link-type" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/interfaces/interface=Ethernet20/config/guard" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/interfaces/interface=Ethernet20/config/bpdu-guard" + method: "patch" + data: + openconfig-spanning-tree:bpdu-guard: False + - path: "data/openconfig-spanning-tree:stp/interfaces/interface=Ethernet20/config/bpdu-filter" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/interfaces/interface=Ethernet20/config/openconfig-spanning-tree-ext:portfast" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/interfaces/interface=Ethernet20/config/openconfig-spanning-tree-ext:uplink-fast" + method: "patch" + data: + openconfig-spanning-tree-ext:uplink-fast: False + - path: "data/openconfig-spanning-tree:stp/interfaces/interface=Ethernet20/config/openconfig-spanning-tree-ext:bpdu-guard-port-shutdown" + method: "patch" + data: + openconfig-spanning-tree-ext:bpdu-guard-port-shutdown: False + - path: "data/openconfig-spanning-tree:stp/interfaces/interface=Ethernet20/config/openconfig-spanning-tree-ext:cost" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/interfaces/interface=Ethernet20/config/openconfig-spanning-tree-ext:port-priority" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/interfaces/interface=Ethernet20/config/openconfig-spanning-tree-ext:spanning-tree-enable" + method: "patch" + data: + openconfig-spanning-tree-ext:spanning-tree-enable: True + - path: "data/openconfig-spanning-tree:stp/interfaces/interface=Ethernet24" + method: "delete" + data: +deleted_03: + module_args: + config: + pvst: + - vlan_id: 1 + hello_time: 7 + max_age: 8 + fwd_delay: 9 + bridge_priority: 8192 + interfaces: + - intf_name: Ethernet20 + - intf_name: Ethernet24 + cost: 3 + port_priority: 50 + state: deleted + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + value: + openconfig-spanning-tree:stp: + openconfig-spanning-tree-ext:pvst: + vlans: + - vlan-id: 1 + config: + vlan-id: 1 + hello-time: 7 + max-age: 8 + forwarding-delay: 9 + bridge-priority: 8192 + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + cost: 2 + port-priority: 60 + - name: Ethernet24 + config: + name: Ethernet24 + cost: 3 + port-priority: 50 + - vlan-id: 2 + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + cost: 2 + port-priority: 60 + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlans=1/config/hello-time" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlans=1/config/max-age" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlans=1/config/forwarding-delay" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlans=1/config/bridge-priority" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlans=1/interfaces/interface=Ethernet24/config/cost" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlans=1/interfaces/interface=Ethernet24/config/port-priority" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlans=1/interfaces/interface=Ethernet20" + method: "delete" + data: +deleted_04: + module_args: + config: + rapid_pvst: + - vlan_id: 1 + hello_time: 7 + max_age: 8 + fwd_delay: 9 + bridge_priority: 8192 + interfaces: + - intf_name: Ethernet20 + - intf_name: Ethernet24 + cost: 3 + port_priority: 50 + state: deleted + existing_stp_config: + - path: "/data/openconfig-spanning-tree:stp" + response: + code: 200 + value: + openconfig-spanning-tree:stp: + rapid-pvst: + vlan: + - vlan-id: 1 + config: + vlan-id: 1 + hello-time: 7 + max-age: 8 + forwarding-delay: 9 + bridge-priority: 8192 + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + cost: 2 + port-priority: 60 + - name: Ethernet24 + config: + name: Ethernet24 + cost: 3 + port-priority: 50 + - vlan-id: 2 + interfaces: + interface: + - name: Ethernet20 + config: + name: Ethernet20 + cost: 2 + port-priority: 60 + expected_config_requests: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst/vlan=1/config/hello-time" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst/vlan=1/config/max-age" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst/vlan=1/config/forwarding-delay" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst/vlan=1/config/bridge-priority" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst/vlan=1/interfaces/interface=Ethernet24/config/cost" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst/vlan=1/interfaces/interface=Ethernet24/config/port-priority" + method: "delete" + data: + - path: "data/openconfig-spanning-tree:stp/rapid-pvst/vlan=1/interfaces/interface=Ethernet20" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_system.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_system.yaml new file mode 100644 index 000000000..64b705d06 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_system.yaml @@ -0,0 +1,232 @@ +--- +merged_01: + module_args: + config: + hostname: SONIC_Test1 + interface_naming: standard + anycast_address: + ipv6: true + ipv4: true + mac_address: aa:bb:cc:dd:ee:ff + existing_system_config: + - path: "data/openconfig-system:system/config" + response: + code: 200 + value: + openconfig-system:config: + hostname: abcd_host + anycast_address: + IPv4: true + mac_address: 11:22:33:44:55:66 + - path: "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost" + response: + code: 200 + value: + sonic-device-metadata:DEVICE_METADATA_LIST: + - intf_naming_mode: native + - path: "data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST/" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-system:system/config" + method: "patch" + data: + openconfig-system:config: + hostname: SONIC_Test1 + - path: "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost/intf_naming_mode" + method: "patch" + data: + sonic-device-metadata:intf_naming_mode: standard + - path: "data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST/" + method: "patch" + data: + sonic-sag:SAG_GLOBAL_LIST: + - IPv4: enable + IPv6: enable + gwmac: aa:bb:cc:dd:ee:ff + table_distinguisher: IP + +deleted_01: + module_args: + state: deleted + existing_system_config: + - path: "data/openconfig-system:system/config" + response: + code: 200 + value: + openconfig-system:config: + hostname: SONIC_Test1 + - path: "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost" + response: + code: 200 + value: + sonic-device-metadata:DEVICE_METADATA_LIST: + - intf_naming_mode: standard + - path: "data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST/" + response: + code: 200 + value: + sonic-sag:SAG_GLOBAL_LIST: + - IPv4: enable + IPv6: enable + gwmac: aa:bb:cc:dd:ee:ff + table_distinguisher: IP + expected_config_requests: + - path: "data/openconfig-system:system/config/" + method: "patch" + data: + openconfig-system:config: + hostname: sonic + - path: "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost/intf_naming_mode" + method: "delete" + data: + - path: "data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST=IP/gwmac" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + hostname: SONIC_Test1 + interface_naming: standard + anycast_address: + ipv6: true + ipv4: true + mac_address: aa:bb:cc:dd:ee:ff + + existing_system_config: + - path: "data/openconfig-system:system/config" + response: + code: 200 + value: + openconfig-system:config: + hostname: SONIC_Test1 + - path: "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost" + response: + code: 200 + value: + sonic-device-metadata:DEVICE_METADATA_LIST: + - intf_naming_mode: standard + - path: "data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST/" + response: + code: 200 + value: + sonic-sag:SAG_GLOBAL_LIST: + - IPv4: enable + IPv6: enable + gwmac: aa:bb:cc:dd:ee:ff + table_distinguisher: IP + expected_config_requests: + - path: "data/openconfig-system:system/config/" + method: "patch" + data: + openconfig-system:config: + hostname: sonic + - path: "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost/intf_naming_mode" + method: "delete" + data: + - path: "data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST=IP/gwmac" + method: "delete" + data: + +replaced_01: + module_args: + state: replaced + config: + hostname: SONIC_Test11 + interface_naming: native + anycast_address: + ipv6: False + ipv4: true + mac_address: 11:22:33:44:55:66 + existing_system_config: + - path: "data/openconfig-system:system/config" + response: + code: 200 + value: + openconfig-system:config: + hostname: abcd_host + - path: "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost" + response: + code: 200 + value: + sonic-device-metadata:DEVICE_METADATA_LIST: + - intf_naming_mode: standard + - path: "data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST/" + response: + code: 200 + value: + sonic-sag:SAG_GLOBAL_LIST: + - IPv4: enable + IPv6: enable + gwmac: aa:bb:cc:dd:ee:ff + table_distinguisher: IP + expected_config_requests: + - path: "data/openconfig-system:system/config" + method: "patch" + data: + openconfig-system:config: + hostname: SONIC_Test11 + - path: "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost/intf_naming_mode" + method: "patch" + data: + sonic-device-metadata:intf_naming_mode: native + - path: "data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST/" + method: "patch" + data: + sonic-sag:SAG_GLOBAL_LIST: + - IPv4: enable + IPv6: disable + gwmac: 11:22:33:44:55:66 + table_distinguisher: IP +overridden_01: + module_args: + state: overridden + config: + hostname: SONIC_Test11 + interface_naming: native + anycast_address: + ipv6: False + ipv4: true + mac_address: 11:22:33:44:55:66 + existing_system_config: + - path: "data/openconfig-system:system/config" + response: + code: 200 + value: + openconfig-system:config: + hostname: abcd_host + - path: "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost" + response: + code: 200 + value: + sonic-device-metadata:DEVICE_METADATA_LIST: + - intf_naming_mode: standard + - path: "data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST/" + response: + code: 200 + value: + sonic-sag:SAG_GLOBAL_LIST: + - IPv4: enable + IPv6: enable + gwmac: aa:bb:cc:dd:ee:ff + table_distinguisher: IP + expected_config_requests: + - path: "data/openconfig-system:system/config" + method: "patch" + data: + openconfig-system:config: + hostname: SONIC_Test11 + - path: "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost/intf_naming_mode" + method: "patch" + data: + sonic-device-metadata:intf_naming_mode: native + - path: "data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST/" + method: "patch" + data: + sonic-sag:SAG_GLOBAL_LIST: + - IPv4: enable + IPv6: disable + gwmac: 11:22:33:44:55:66 + table_distinguisher: IP diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_tacacs_server.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_tacacs_server.yaml new file mode 100644 index 000000000..e789252c7 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_tacacs_server.yaml @@ -0,0 +1,172 @@ +--- +merged_01: + module_args: + config: + auth_type: pap + key: papTest + source_interface: Eth1/2 + timeout: 10 + servers: + host: + - name: 1.2.3.4 + auth_type: pap + key: 1234 + port: 99 + priority: 88 + timeout: 77 + vrf: VrfReg1 + existing_tacacs_server_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/config" + response: + code: 200 + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/servers" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/config" + method: "patch" + data: + openconfig-system:config: + auth-type: pap + secret-key: papTest + source-interface: Eth1/2 + timeout: 10 + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/servers" + method: "patch" + data: + openconfig-system:servers: + server: + - address: 1.2.3.4 + config: + address: 1.2.3.4 + auth-type: pap + priority: 88 + vrf: VrfReg1 + timeout: 77 + tacacs: + config: + port: 99 + secret-key: '1234' + +deleted_01: + module_args: + state: deleted + existing_tacacs_server_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/config" + response: + code: 200 + value: + openconfig-system:config: + auth-type: pap + secret-key: papTest + source-interface: Eth1/2 + timeout: 10 + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/servers" + response: + code: 200 + value: + openconfig-system:servers: + server: + - address: 1.2.3.4 + config: + address: 1.2.3.4 + auth-type: pap + priority: 88 + vrf: VrfReg1 + timeout: 77 + tacacs: + config: + port: 99 + secret-key: '1234' + expected_config_requests: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/config/secret-key" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/config/source-interface" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/config/timeout" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/servers/server=1.2.3.4" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + auth_type: pap + key: papTest + source_interface: Eth1/2 + timeout: 10 + servers: + host: + - name: 1.2.3.4 + auth_type: pap + key: 1234 + port: 99 + priority: 88 + timeout: 77 + vrf: VrfReg1 + existing_tacacs_server_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - vrf_name: VrfReg1 + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/config" + response: + code: 200 + value: + openconfig-system:config: + auth-type: pap + secret-key: papTest + source-interface: Eth1/2 + timeout: 10 + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/servers" + response: + code: 200 + value: + openconfig-system:servers: + server: + - address: 1.2.3.4 + config: + address: 1.2.3.4 + auth-type: pap + priority: 88 + vrf: VrfReg1 + timeout: 77 + tacacs: + config: + port: 99 + secret-key: '1234' + expected_config_requests: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/config/secret-key" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/config/source-interface" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/config/timeout" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/servers/server=1.2.3.4" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_users.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_users.yaml new file mode 100644 index 000000000..afeca94c0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_users.yaml @@ -0,0 +1,93 @@ +--- +merged_01: + module_args: + config: + - name: sysadmin + role: admin + password: admin + update_password: always + - name: sysoperator + role: operator + password: operator + update_password: always + existing_users_config: + - path: "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost" + response: + code: 200 + value: + sonic-device-metadata:DEVICE_METADATA_LIST: + - intf_naming_mode: native + - path: "data/openconfig-system:system/aaa/authentication/users" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-system:system/aaa/authentication/users/user=sysadmin" + method: "patch" + data: + openconfig-system:user: + - username: sysadmin + config: + username: sysadmin + role: admin + password: admin + password-hashed: '' + - path: "data/openconfig-system:system/aaa/authentication/users/user=sysoperator" + method: "patch" + data: + openconfig-system:user: + - username: sysoperator + config: + username: sysoperator + role: operator + password: operator + password-hashed: '' + +deleted_01: + module_args: + state: deleted + existing_users_config: + - path: "data/openconfig-system:system/aaa/authentication/users" + response: + code: 200 + value: + openconfig-system:users: + user: + - username: sysadmin + config: + role: admin + - username: sysoperator + config: + role: operator + expected_config_requests: + - path: "data/openconfig-system:system/aaa/authentication/users/user=sysadmin" + method: "delete" + data: + - path: "data/openconfig-system:system/aaa/authentication/users/user=sysoperator" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + - name: sysadmin + role: admin + password: admin + update_password: always + existing_users_config: + - path: "data/openconfig-system:system/aaa/authentication/users" + response: + code: 200 + value: + openconfig-system:users: + user: + - username: sysadmin + config: + role: admin + - username: sysoperator + config: + role: operator + expected_config_requests: + - path: "data/openconfig-system:system/aaa/authentication/users/user=sysadmin" + method: "delete" + data: diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_vlans.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_vlans.yaml new file mode 100644 index 000000000..19e183475 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_vlans.yaml @@ -0,0 +1,190 @@ +--- +merged_01: + module_args: + config: + - vlan_id: 10 + description: "Internal" + existing_vlans_config: + - path: "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost" + response: + code: 200 + value: + sonic-device-metadata:DEVICE_METADATA_LIST: + - intf_naming_mode: native + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces" + method: "patch" + data: + openconfig-interfaces:interfaces: + interface: + - name: Vlan10 + config: + name: Vlan10 + - path: "data/openconfig-interfaces:interfaces/interface=Vlan10/config" + method: "patch" + data: + openconfig-interfaces:config: + name: Vlan10 + description: Internal + +merged_02: + module_args: + config: + - vlan_id: 10 + description: "Decr2" + - vlan_id: 20 + existing_vlans_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - config: + name: Vlan10 + description: Decr1 + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces" + method: "patch" + data: + openconfig-interfaces:interfaces: + interface: + - name: Vlan10 + config: + name: Vlan10 + - path: "data/openconfig-interfaces:interfaces" + method: "patch" + data: + openconfig-interfaces:interfaces: + interface: + - name: Vlan20 + config: + name: Vlan20 + - path: "data/openconfig-interfaces:interfaces/interface=Vlan10/config" + method: "patch" + data: + openconfig-interfaces:config: + name: Vlan10 + description: Decr2 + +deleted_01_vlan_descr: + module_args: + state: deleted + config: + - vlan_id: 10 + description: "Internal" + existing_vlans_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - config: + name: Vlan10 + description: Internal + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Vlan10/config/description" + method: "delete" + data: + +deleted_02_vlan: + module_args: + state: deleted + existing_vlans_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - config: + name: Vlan10 + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Vlan10" + method: "delete" + data: + +replaced_01: + module_args: + state: replaced + config: + - vlan_id: 10 + description: "Desc2" + - vlan_id: 30 + existing_vlans_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - config: + name: Vlan10 + description: Decr1 + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces" + method: "patch" + data: + openconfig-interfaces:interfaces: + interface: + - name: Vlan10 + config: + name: Vlan10 + - path: "data/openconfig-interfaces:interfaces" + method: "patch" + data: + openconfig-interfaces:interfaces: + interface: + - name: Vlan30 + config: + name: Vlan30 + - path: "data/openconfig-interfaces:interfaces/interface=Vlan10/config" + method: "patch" + data: + openconfig-interfaces:config: + name: Vlan10 + description: Desc2 +overridden_01: + module_args: + state: overridden + config: + - vlan_id: 10 + description: "Decr3" + - vlan_id: 40 + existing_vlans_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - config: + name: Vlan10 + description: Decr1 + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces" + method: "patch" + data: + openconfig-interfaces:interfaces: + interface: + - name: Vlan10 + config: + name: Vlan10 + - path: "data/openconfig-interfaces:interfaces" + method: "patch" + data: + openconfig-interfaces:interfaces: + interface: + - name: Vlan40 + config: + name: Vlan40 + - path: "data/openconfig-interfaces:interfaces/interface=Vlan10/config" + method: "patch" + data: + openconfig-interfaces:config: + name: Vlan10 + description: Decr3 diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_vrfs.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_vrfs.yaml new file mode 100644 index 000000000..300d0858b --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_vrfs.yaml @@ -0,0 +1,249 @@ +--- +merged_01: + module_args: + config: + - name: Vrfcheck4 + members: + interfaces: + - name: Eth1/5 + - name: Eth1/6 + - name: Vrfcheck3 + members: + interfaces: + - name: Eth1/3 + - name: Eth1/4 + existing_vrfs_config: + - path: "data/openconfig-network-instance:network-instances" + response: + code: 200 + value: + openconfig-network-instance:network-instances: + network-instance: + - name: mgmt + - name: VrfCheck1 + expected_config_requests: + - path: "data/openconfig-network-instance:network-instances" + method: "patch" + data: + openconfig-network-instance:network-instances: + network-instance: + - name: Vrfcheck4 + config: + name: Vrfcheck4 + enabled: True + type: L3VRF + - path: "data/openconfig-network-instance:network-instances" + method: "patch" + data: + openconfig-network-instance:network-instances: + network-instance: + - name: Vrfcheck3 + config: + name: Vrfcheck3 + enabled: True + type: L3VRF + - path: "data/openconfig-network-instance:network-instances/network-instance=Vrfcheck3/interfaces/interface" + method: "patch" + data: + openconfig-network-instance:interface: + - id: Eth1/3 + config: + id: Eth1/3 + - id: Eth1/4 + config: + id: Eth1/4 + - path: "data/openconfig-network-instance:network-instances/network-instance=Vrfcheck4/interfaces/interface" + method: "patch" + data: + openconfig-network-instance:interface: + - id: Eth1/5 + config: + id: Eth1/5 + - id: Eth1/6 + config: + id: Eth1/6 + +deleted_01: + module_args: + state: deleted + existing_vrfs_config: + - path: "data/openconfig-network-instance:network-instances" + response: + code: 200 + value: + openconfig-network-instance:network-instances: + network-instance: + - name: VrfCheck1 + interfaces: + interface: + - id: Eth1/1 + - id: Eth1/2 + expected_config_requests: + - path: "data/openconfig-network-instance:network-instances/network-instance=VrfCheck1" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + - name: VrfCheck6 + members: + interfaces: + - name: Eth1/1 + - name: mgmt + members: + interfaces: + - name: Eth1/4 + existing_vrfs_config: + - path: "data/openconfig-network-instance:network-instances" + response: + code: 200 + value: + openconfig-network-instance:network-instances: + network-instance: + - name: VrfCheck6 + interfaces: + interface: + - id: Eth1/1 + - id: Eth1/2 + - name: mgmt + interfaces: + interface: + - id: Eth1/3 + - id: Eth1/4 + expected_config_requests: + - path: "data/openconfig-network-instance:network-instances/network-instance=VrfCheck6/interfaces/interface=Eth1%2f1" + method: "delete" + data: + - path: "data/openconfig-network-instance:network-instances/network-instance=mgmt/interfaces/interface=Eth1%2f4" + method: "delete" + data: + +replaced_01: + module_args: + state: replaced + config: + - name: VrfCheck6 + members: + interfaces: + - name: Eth1/6 + - name: VrfCheck7 + members: + interfaces: + - name: Eth1/4 + existing_vrfs_config: + - path: "data/openconfig-network-instance:network-instances" + response: + code: 200 + value: + openconfig-network-instance:network-instances: + network-instance: + - name: VrfCheck6 + interfaces: + interface: + - id: Eth1/1 + - id: Eth1/2 + - name: VrfCheck7 + interfaces: + interface: + - id: Eth1/3 + - id: Eth1/4 + expected_config_requests: + - path: "data/openconfig-network-instance:network-instances/network-instance=VrfCheck6/interfaces/interface" + method: "patch" + data: + openconfig-network-instance:interface: + - id: Eth1/6 + config: + id: Eth1/6 + - path: "data/openconfig-network-instance:network-instances/network-instance=VrfCheck6/interfaces/interface=Eth1%2f1" + method: "delete" + data: + - path: "data/openconfig-network-instance:network-instances/network-instance=VrfCheck6/interfaces/interface=Eth1%2f2" + method: "delete" + data: + - path: "data/openconfig-network-instance:network-instances/network-instance=VrfCheck7/interfaces/interface" + method: "patch" + data: + openconfig-network-instance:interface: + - id: Eth1/4 + config: + id: Eth1/4 + - path: "data/openconfig-network-instance:network-instances/network-instance=VrfCheck7/interfaces/interface=Eth1%2f3" + method: "delete" + data: + - path: "data/openconfig-network-instance:network-instances/network-instance=VrfCheck7/interfaces/interface=Eth1%2f4" + method: "delete" + data: + +overridden_01: + module_args: + state: overridden + config: + - name: VrfCheck6 + members: + interfaces: + - name: Eth1/6 + - name: VrfCheck7 + members: + interfaces: + - name: Eth1/4 + existing_vrfs_config: + - path: "data/openconfig-network-instance:network-instances" + response: + code: 200 + value: + openconfig-network-instance:network-instances: + network-instance: + - name: VrfCheck11 + interfaces: + interface: + - id: Eth1/1 + - id: Eth1/2 + - name: VrfCheck12 + interfaces: + interface: + - id: Eth1/3 + - id: Eth1/4 + expected_config_requests: + - path: "data/openconfig-network-instance:network-instances" + method: "patch" + data: + openconfig-network-instance:network-instances: + network-instance: + - name: VrfCheck6 + config: + name: VrfCheck6 + enabled: True + type: L3VRF + - path: "data/openconfig-network-instance:network-instances" + method: "patch" + data: + openconfig-network-instance:network-instances: + network-instance: + - name: VrfCheck7 + config: + name: VrfCheck7 + enabled: True + type: L3VRF + - path: "data/openconfig-network-instance:network-instances/network-instance=VrfCheck11" + method: "delete" + data: + - path: "data/openconfig-network-instance:network-instances/network-instance=VrfCheck12" + method: "delete" + data: + - path: "data/openconfig-network-instance:network-instances/network-instance=VrfCheck6/interfaces/interface" + method: "patch" + data: + openconfig-network-instance:interface: + - id: Eth1/6 + config: + id: Eth1/6 + - path: "data/openconfig-network-instance:network-instances/network-instance=VrfCheck7/interfaces/interface" + method: "patch" + data: + openconfig-network-instance:interface: + - id: Eth1/4 + config: + id: Eth1/4 diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_vxlans.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_vxlans.yaml new file mode 100644 index 000000000..f60bd2c5a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/fixtures/sonic_vxlans.yaml @@ -0,0 +1,418 @@ +--- +merged_01: + module_args: + config: + - name: vteptest1 + source_ip: 1.1.1.1 + primary_ip: 2.2.2.2 + evpn_nvo: nvo1 + vlan_map: + - vni: 101 + vlan: 11 + - vni: 102 + vlan: 12 + vrf_map: + - vni: 101 + vrf: Vrfcheck1 + - vni: 102 + vrf: Vrfcheck2 + existing_vxlans_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + - path: "data/sonic-vxlan:sonic-vxlan" + response: + code: 200 + - path: "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST" + response: + code: 200 + expected_config_requests: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST=Vrfcheck1/vni" + method: "patch" + data: + sonic-vrf:vni: 101 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST=Vrfcheck2/vni" + method: "patch" + data: + sonic-vrf:vni: 102 + - path: "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST" + method: "patch" + data: + sonic-vxlan:EVPN_NVO_LIST: + - name: nvo1 + source_vtep: vteptest1 + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL" + method: "patch" + data: + sonic-vxlan:VXLAN_TUNNEL: + VXLAN_TUNNEL_LIST: + - name: vteptest1 + src_ip: 1.1.1.1 + primary_ip: 2.2.2.2 + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL_MAP" + method: "patch" + data: + sonic-vxlan:VXLAN_TUNNEL_MAP: + VXLAN_TUNNEL_MAP_LIST: + - name: vteptest1 + mapname: map_101_Vlan11 + vlan: Vlan11 + vni: 101 + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL_MAP" + method: "patch" + data: + sonic-vxlan:VXLAN_TUNNEL_MAP: + VXLAN_TUNNEL_MAP_LIST: + - name: vteptest1 + mapname: map_102_Vlan12 + vlan: Vlan12 + vni: 102 + +deleted_01: + module_args: + state: deleted + existing_vxlans_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: Vrfcheck1 + vni: 101 + - vrf_name: Vrfcheck2 + vni: 102 + - vrf_name: default + - path: "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST" + response: + code: 200 + value: + sonic-vxlan:EVPN_NVO_LIST: + - name: nvo1 + source_vtep: vteptest1 + - path: "data/sonic-vxlan:sonic-vxlan" + response: + code: 200 + value: + sonic-vxlan:sonic-vxlan: + VXLAN_TUNNEL: + VXLAN_TUNNEL_LIST: + - name: vteptest1 + src_ip: 1.1.1.1 + primary_ip: 2.2.2.2 + VXLAN_TUNNEL_MAP: + VXLAN_TUNNEL_MAP_LIST: + - name: vteptest1 + mapname: map_101_Vlan11 + vni: 101 + vlan: Vlan11 + - name: vteptest1 + mapname: map_102_Vlan12 + vni: 102 + vlan: Vlan12 + expected_config_requests: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST=Vrfcheck1/vni" + method: "delete" + data: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST=Vrfcheck2/vni" + method: "delete" + data: + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL/VXLAN_TUNNEL_LIST=vteptest1" + method: "delete" + data: + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL/VXLAN_TUNNEL_LIST=vteptest1/primary_ip" + method: "delete" + data: + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL/VXLAN_TUNNEL_LIST=vteptest1/src_ip" + method: "delete" + data: + - path: "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST=nvo1" + method: "delete" + data: + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL_MAP/VXLAN_TUNNEL_MAP_LIST=vteptest1,map_101_Vlan11" + method: "delete" + data: + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL_MAP/VXLAN_TUNNEL_MAP_LIST=vteptest1,map_102_Vlan12" + method: "delete" + data: + +deleted_02: + module_args: + state: deleted + config: + - name: vteptest1 + source_ip: 1.1.1.1 + primary_ip: 2.2.2.2 + evpn_nvo: nvo1 + vlan_map: + - vni: 101 + vlan: 11 + - vni: 102 + vlan: 12 + vrf_map: + - vni: 101 + vrf: Vrfcheck1 + - vni: 102 + vrf: Vrfcheck2 + existing_vxlans_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: Vrfcheck1 + vni: 101 + - vrf_name: Vrfcheck2 + vni: 102 + - vrf_name: default + - path: "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST" + response: + code: 200 + value: + sonic-vxlan:EVPN_NVO_LIST: + - name: nvo1 + source_vtep: vteptest1 + - path: "data/sonic-vxlan:sonic-vxlan" + response: + code: 200 + value: + sonic-vxlan:sonic-vxlan: + VXLAN_TUNNEL: + VXLAN_TUNNEL_LIST: + - name: vteptest1 + src_ip: 1.1.1.1 + primary_ip: 2.2.2.2 + VXLAN_TUNNEL_MAP: + VXLAN_TUNNEL_MAP_LIST: + - name: vteptest1 + mapname: map_101_Vlan11 + vni: 101 + vlan: Vlan11 + - name: vteptest1 + mapname: map_102_Vlan12 + vni: 102 + vlan: Vlan12 + expected_config_requests: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST=Vrfcheck1/vni" + method: "delete" + data: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST=Vrfcheck2/vni" + method: "delete" + data: + - path: "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST=nvo1" + method: "delete" + data: + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL/VXLAN_TUNNEL_LIST=vteptest1/primary_ip" + method: "delete" + data: + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL/VXLAN_TUNNEL_LIST=vteptest1/src_ip" + method: "delete" + data: + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL_MAP/VXLAN_TUNNEL_MAP_LIST=vteptest1,map_101_Vlan11" + method: "delete" + data: + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL_MAP/VXLAN_TUNNEL_MAP_LIST=vteptest1,map_102_Vlan12" + method: "delete" + data: + +replaced_02: + module_args: + state: replaced + config: + - name: vteptest1 + source_ip: 1.1.1.9 + primary_ip: 2.2.2.9 + evpn_nvo: nvo1 + vlan_map: + - vni: 101 + vlan: 21 + - vni: 102 + vlan: 22 + vrf_map: + - vni: 101 + vrf: Vrfcheck3 + - vni: 102 + vrf: Vrfcheck4 + existing_vxlans_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: Vrfcheck1 + vni: 101 + - vrf_name: Vrfcheck2 + vni: 102 + - vrf_name: default + - vrf_name: Vrfcheck3 + - vrf_name: Vrfcheck4 + - path: "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST" + response: + code: 200 + value: + sonic-vxlan:EVPN_NVO_LIST: + - name: nvo1 + source_vtep: vteptest1 + - path: "data/sonic-vxlan:sonic-vxlan" + response: + code: 200 + value: + sonic-vxlan:sonic-vxlan: + VXLAN_TUNNEL: + VXLAN_TUNNEL_LIST: + - name: vteptest1 + src_ip: 1.1.1.1 + primary_ip: 2.2.2.2 + VXLAN_TUNNEL_MAP: + VXLAN_TUNNEL_MAP_LIST: + - name: vteptest1 + mapname: map_101_Vlan11 + vni: 101 + vlan: Vlan11 + - name: vteptest1 + mapname: map_102_Vlan12 + vni: 102 + vlan: Vlan12 + expected_config_requests: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST=Vrfcheck3/vni" + method: "patch" + data: + sonic-vrf:vni: 101 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST=Vrfcheck4/vni" + method: "patch" + data: + sonic-vrf:vni: 102 + - path: "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST" + method: "patch" + data: + sonic-vxlan:EVPN_NVO_LIST: + - name: nvo1 + source_vtep: vteptest1 + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL" + method: "patch" + data: + sonic-vxlan:VXLAN_TUNNEL: + VXLAN_TUNNEL_LIST: + - name: vteptest1 + src_ip: 1.1.1.9 + primary_ip: 2.2.2.9 + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL_MAP" + method: "patch" + data: + sonic-vxlan:VXLAN_TUNNEL_MAP: + VXLAN_TUNNEL_MAP_LIST: + - name: vteptest1 + mapname: map_101_Vlan21 + vlan: Vlan21 + vni: 101 + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL_MAP" + method: "patch" + data: + sonic-vxlan:VXLAN_TUNNEL_MAP: + VXLAN_TUNNEL_MAP_LIST: + - name: vteptest1 + mapname: map_102_Vlan22 + vlan: Vlan22 + vni: 102 + +overridden_02: + module_args: + state: overridden + config: + - name: vteptest1 + source_ip: 1.1.1.9 + primary_ip: 2.2.2.9 + evpn_nvo: nvo1 + vlan_map: + - vni: 101 + vlan: 21 + - vni: 102 + vlan: 22 + vrf_map: + - vni: 101 + vrf: Vrfcheck3 + - vni: 102 + vrf: Vrfcheck4 + existing_vxlans_config: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: Vrfcheck1 + vni: 101 + - vrf_name: Vrfcheck2 + vni: 102 + - vrf_name: default + - vrf_name: Vrfcheck3 + - vrf_name: Vrfcheck4 + - path: "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST" + response: + code: 200 + value: + sonic-vxlan:EVPN_NVO_LIST: + - name: nvo1 + source_vtep: vteptest1 + - path: "data/sonic-vxlan:sonic-vxlan" + response: + code: 200 + value: + sonic-vxlan:sonic-vxlan: + VXLAN_TUNNEL: + VXLAN_TUNNEL_LIST: + - name: vteptest1 + src_ip: 1.1.1.1 + primary_ip: 2.2.2.2 + VXLAN_TUNNEL_MAP: + VXLAN_TUNNEL_MAP_LIST: + - name: vteptest1 + mapname: map_101_Vlan11 + vni: 101 + vlan: Vlan11 + - name: vteptest1 + mapname: map_102_Vlan12 + vni: 102 + vlan: Vlan12 + expected_config_requests: + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST=Vrfcheck3/vni" + method: "patch" + data: + sonic-vrf:vni: 101 + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST=Vrfcheck4/vni" + method: "patch" + data: + sonic-vrf:vni: 102 + - path: "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST" + method: "patch" + data: + sonic-vxlan:EVPN_NVO_LIST: + - name: nvo1 + source_vtep: vteptest1 + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL" + method: "patch" + data: + sonic-vxlan:VXLAN_TUNNEL: + VXLAN_TUNNEL_LIST: + - name: vteptest1 + src_ip: 1.1.1.9 + primary_ip: 2.2.2.9 + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL_MAP" + method: "patch" + data: + sonic-vxlan:VXLAN_TUNNEL_MAP: + VXLAN_TUNNEL_MAP_LIST: + - name: vteptest1 + mapname: map_101_Vlan21 + vlan: Vlan21 + vni: 101 + - path: "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL_MAP" + method: "patch" + data: + sonic-vxlan:VXLAN_TUNNEL_MAP: + VXLAN_TUNNEL_MAP_LIST: + - name: vteptest1 + mapname: map_102_Vlan22 + vlan: Vlan22 + vni: 102 diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/sonic_module.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/sonic_module.py new file mode 100644 index 000000000..a4c411371 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/sonic_module.py @@ -0,0 +1,149 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import os +import yaml + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + AnsibleExitJson, + AnsibleFailJson, + ModuleTestCase, +) + +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + update_url +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + get_diff +) + + +class TestSonicModule(ModuleTestCase): + """Enterprise SONiC ansible module base unit test class""" + + def setUp(self): + super(TestSonicModule, self).setUp() + + self.config_requests_valid = [] + self.config_requests_sent = [] + + self._config_requests_dict = {} + self._facts_requests_dict = {} + + @staticmethod + def load_fixtures(file_name, content="yaml"): + """Load data from specified fixture file and format""" + fixture_path = os.path.join(os.path.dirname(__file__), "fixtures") + file_path = os.path.join(fixture_path, file_name) + + file_stream = open(file_path, "r") + if content == "yaml": + data = yaml.full_load(file_stream) + else: + data = file_stream.read() + file_stream.close() + + return data + + def initialize_facts_get_requests(self, facts_get_requests): + for request in facts_get_requests: + self._facts_requests_dict[request['path']] = request['response'] + + def initialize_config_requests(self, config_requests): + for request in config_requests: + valid_request = request.copy() + path = valid_request['path'] + method = valid_request['method'].lower() + data = valid_request.get('data', {}) + if valid_request.get('response'): + response = valid_request.pop('response') + else: + response = {} + + self.config_requests_valid.append(valid_request) + if self._config_requests_dict.get(path) is None: + self._config_requests_dict[path] = {} + + config_request_dict = self._config_requests_dict[path] + if config_request_dict.get(method) is None: + config_request_dict[method] = [] + + config_request_dict[method].append([ + data, + {'code': response.get('code', 200), 'value': response.get('value', {})} + ]) + + def facts_side_effect(self, module, commands): + """Side effect function for 'facts' GET requests mock""" + responses = [] + for command in commands: + response = [] + path = update_url(command['path']) + method = command['method'].lower() + + if method == 'get': + if self._facts_requests_dict.get(path): + response.append(self._facts_requests_dict[path]['code']) + response.append(self._facts_requests_dict[path].get('value', {})) + else: + self.module.fail_json(msg="Non GET REST API request made in get facts {0}".format(command)) + + responses.append(response) + + return responses + + def config_side_effect(self, module, commands): + """Side effect function for 'config' requests mock""" + responses = [] + for command in commands: + response = [] + path = update_url(command['path']) + method = command['method'].lower() + data = command['data'] + + self.config_requests_sent.append({'path': path, 'method': method, 'data': data}) + entries = self._config_requests_dict.get(path, {}).get(method, []) + for entry in entries: + if data == entry[0]: + response.append(entry[1]['code']) + response.append(entry[1]['value']) + break + + responses.append(response) + + return responses + + def execute_module(self, failed=False, changed=False): + if failed: + result = self.failed() + else: + result = self.changed(changed) + + return result + + def failed(self): + with self.assertRaises(AnsibleFailJson) as exc: + self.module.main() + + result = exc.exception.args[0] + self.assertTrue(result["failed"], result) + return result + + def changed(self, changed=False): + with self.assertRaises(AnsibleExitJson) as exc: + self.module.main() + + result = exc.exception.args[0] + self.assertEqual(result["changed"], changed, result) + return result + + def validate_config_requests(self, requests_sorted=False): + """Check if both list of requests sent and expected are same""" + if not requests_sorted: + # Sort by 'path' (primary) followed by 'method' (secondary) + self.config_requests_valid.sort(key=lambda request: (request['path'], request['method'])) + self.config_requests_sent.sort(key=lambda request: (request['path'], request['method'])) + + self.assertEqual(len(self.config_requests_valid), len(self.config_requests_sent)) + for valid_request, sent_request in zip(self.config_requests_valid, self.config_requests_sent): + self.assertEqual(get_diff(valid_request, sent_request, [{'path': "", 'method': "", 'data': {}}]), {}) diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_aaa.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_aaa.py new file mode 100644 index 000000000..de747c2c8 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_aaa.py @@ -0,0 +1,69 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_aaa, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicAaaModule(TestSonicModule): + module = sonic_aaa + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.aaa.aaa.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.aaa.aaa.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_aaa.yaml') + + def setUp(self): + super(TestSonicAaaModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicAaaModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_aaa_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_aaa_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_aaa_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_aaa_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_aaa_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_aaa_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_acl_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_acl_interfaces.py new file mode 100644 index 000000000..0bddd6801 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_acl_interfaces.py @@ -0,0 +1,96 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_acl_interfaces, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicAclInterfacesModule(TestSonicModule): + module = sonic_acl_interfaces + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.acl_interfaces.acl_interfaces.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.acl_interfaces.acl_interfaces.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_acl_interfaces.yaml') + + def setUp(self): + super(TestSonicAclInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicAclInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_acl_interfaces_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['merged_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_acl_interfaces_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['merged_02']['config_requests']) + + result = self.execute_module(changed=False) + self.validate_config_requests() + + def test_sonic_acl_interfaces_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['replaced_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_acl_interfaces_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['overridden_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_acl_interfaces_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['deleted_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_acl_interfaces_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['deleted_02']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_api.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_api.py new file mode 100644 index 000000000..aa3e05e2f --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_api.py @@ -0,0 +1,38 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_api, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_api + + @classmethod + def setUpClass(cls): + cls.mock_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.modules.sonic_api.edit_config" + ) + cls.fixture_data = cls.load_fixtures('sonic_api.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.edit_config = self.mock_edit_config.start() + self.edit_config.return_value = [(204, '')] + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_edit_config.stop() + + def test_sonic_api_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + result = self.execute_module(changed=True) diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bfd.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bfd.py new file mode 100644 index 000000000..f30c90c79 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bfd.py @@ -0,0 +1,86 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_bfd, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicBfdModule(TestSonicModule): + module = sonic_bfd + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.bfd.bfd.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bfd.bfd.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.mock_send_requests = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bfd.bfd.send_requests" + ) + cls.fixture_data = cls.load_fixtures('sonic_bfd.yaml') + + def setUp(self): + super(TestSonicBfdModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'native' + self.send_requests = self.mock_send_requests.start() + self.send_requests.return_value = None + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicBfdModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + self.mock_send_requests.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_bfd_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_bfd_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bfd_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_bfd_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bfd_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_bfd_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bfd_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_bfd_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp.py new file mode 100644 index 000000000..ad46f73f9 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp.py @@ -0,0 +1,75 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_bgp, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicBgpModule(TestSonicModule): + module = sonic_bgp + + @classmethod + def setUpClass(cls): + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp.bgp.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.fixture_data = cls.load_fixtures('sonic_bgp.yaml') + + def setUp(self): + super(TestSonicBgpModule, self).setUp() + self.config_edit_config = self.mock_config_edit_config.start() + self.config_edit_config.side_effect = self.config_side_effect + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicBgpModule, self).tearDown() + self.mock_config_edit_config.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_bgp_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_af.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_af.py new file mode 100644 index 000000000..fe50c303d --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_af.py @@ -0,0 +1,82 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_bgp_af, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicBgpModule(TestSonicModule): + module = sonic_bgp_af + + @classmethod + def setUpClass(cls): + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp_af.bgp_af.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.fixture_data = cls.load_fixtures('sonic_bgp_af.yaml') + + def setUp(self): + super(TestSonicBgpModule, self).setUp() + self.config_edit_config = self.mock_config_edit_config.start() + self.config_edit_config.side_effect = self.config_side_effect + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicBgpModule, self).tearDown() + self.mock_config_edit_config.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_bgp_af_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_af_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['merged_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_af_merged_03(self): + set_module_args(self.fixture_data['merged_03']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_03']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['merged_03']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_af_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_af_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_af_deleted_03(self): + set_module_args(self.fixture_data['deleted_03']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_03']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_03']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_as_paths.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_as_paths.py new file mode 100644 index 000000000..edcb1a440 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_as_paths.py @@ -0,0 +1,83 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_bgp_as_paths, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicBgpAsPathsModule(TestSonicModule): + module = sonic_bgp_as_paths + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.bgp_as_paths.bgp_as_paths.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp_as_paths.bgp_as_paths.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.fixture_data = cls.load_fixtures('sonic_bgp_as_paths.yaml') + + def setUp(self): + super(TestSonicBgpAsPathsModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicBgpAsPathsModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_bgp_as_paths_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_as_paths_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_as_paths_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_as_paths_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_as_paths_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_communities.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_communities.py new file mode 100644 index 000000000..64451a890 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_communities.py @@ -0,0 +1,104 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_bgp_communities, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicBgpCommunitiesModule(TestSonicModule): + module = sonic_bgp_communities + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.bgp_communities.bgp_communities.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp_communities.bgp_communities.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.fixture_data = cls.load_fixtures('sonic_bgp_communities.yaml') + + def setUp(self): + super(TestSonicBgpCommunitiesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicBgpCommunitiesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_bgp_communities_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_communities_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['merged_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_communities_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_communities_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_communities_deleted_03(self): + set_module_args(self.fixture_data['deleted_03']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_03']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_03']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_communities_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_communities_replaced_02(self): + set_module_args(self.fixture_data['replaced_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_02']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['replaced_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_communities_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_ext_communities.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_ext_communities.py new file mode 100644 index 000000000..983554455 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_ext_communities.py @@ -0,0 +1,146 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_bgp_ext_communities, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicBgpExtCommunitiesModule(TestSonicModule): + module = sonic_bgp_ext_communities + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.bgp_ext_communities.bgp_ext_communities.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp_ext_communities.bgp_ext_communities.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.fixture_data = cls.load_fixtures('sonic_bgp_ext_communities.yaml') + + def setUp(self): + super(TestSonicBgpExtCommunitiesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicBgpExtCommunitiesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_bgp_ext_communities_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['merged_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_merged_03(self): + set_module_args(self.fixture_data['merged_03']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_03']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['merged_03']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_deleted_03(self): + set_module_args(self.fixture_data['deleted_03']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_03']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_03']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_deleted_04(self): + set_module_args(self.fixture_data['deleted_04']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_04']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_04']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_deleted_05(self): + set_module_args(self.fixture_data['deleted_05']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_05']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_05']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_deleted_06(self): + set_module_args(self.fixture_data['deleted_06']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_06']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_06']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_deleted_07(self): + set_module_args(self.fixture_data['deleted_07']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_07']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_07']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_deleted_08(self): + set_module_args(self.fixture_data['deleted_08']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_08']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_08']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_deleted_09(self): + set_module_args(self.fixture_data['deleted_09']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_09']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_09']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_neighbors.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_neighbors.py new file mode 100644 index 000000000..113e9f5c8 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_neighbors.py @@ -0,0 +1,75 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_bgp_neighbors, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicBgpModule(TestSonicModule): + module = sonic_bgp_neighbors + + @classmethod + def setUpClass(cls): + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp_neighbors.bgp_neighbors.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_bgp_neighbors.yaml') + + def setUp(self): + super(TestSonicBgpModule, self).setUp() + self.config_edit_config = self.mock_config_edit_config.start() + self.config_edit_config.side_effect = self.config_side_effect + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicBgpModule, self).tearDown() + self.mock_config_edit_config.stop() + self.mock_utils_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_bgp_neighbors_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + # Merge test when neighbor and peer-group are already present in existing config + def test_sonic_bgp_neighbors_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['merged_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_neighbors_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_neighbors_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_neighbors_af.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_neighbors_af.py new file mode 100644 index 000000000..574cbf114 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_bgp_neighbors_af.py @@ -0,0 +1,74 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_bgp_neighbors_af, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicBgpModule(TestSonicModule): + module = sonic_bgp_neighbors_af + + @classmethod + def setUpClass(cls): + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp_neighbors_af.bgp_neighbors_af.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_bgp_neighbors_af.yaml') + + def setUp(self): + super(TestSonicBgpModule, self).setUp() + self.config_edit_config = self.mock_config_edit_config.start() + self.config_edit_config.side_effect = self.config_side_effect + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicBgpModule, self).tearDown() + self.mock_config_edit_config.stop() + self.mock_utils_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_bgp_neighbors_af_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_neighbors_af_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_neighbors_af_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_neighbors_af_deleted_03(self): + set_module_args(self.fixture_data['deleted_03']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_03']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['deleted_03']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_command.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_command.py new file mode 100644 index 000000000..2a317ff2c --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_command.py @@ -0,0 +1,59 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_command, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_command + + @classmethod + def setUpClass(cls): + cls.mock_run_commands = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.modules.sonic_command.run_commands" + ) + cls.fixture_data = cls.load_fixtures('sonic_command.yaml') + + def run_commands_side_effect(self, module, commands): + """Side effect function for run commands mock""" + + for cmd in commands: + self.config_commands_sent.append(cmd['command']) + # Purpose of the Unit testing for sonic_command is to check whether the passed command goes to device. + # Response from device is validated against the expected values. + # Simulate a dummy return value for the "show version" command that is being unit tested. + return ['Software Version : dell_sonic_4.x_share.770-0beb2c821\n'] + + def validate_config_commands(self): + """Check if both list of requests sent and expected are same""" + + self.assertEqual(len(self.config_commands_valid), len(self.config_commands_sent)) + for valid_command, sent_command in zip(self.config_commands_valid, self.config_commands_sent): + self.assertEqual(valid_command, sent_command) + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.config_commands_sent = [] + self.config_commands_valid = [] + self.run_commands = self.mock_run_commands.start() + self.run_commands.side_effect = self.run_commands_side_effect + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_run_commands.stop() + + def test_sonic_commands_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.config_commands_valid = self.fixture_data['merged_01']['expected_command_requests'] + result = self.execute_module(changed=False) + self.validate_config_commands() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_config.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_config.py new file mode 100644 index 000000000..1bbd00766 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_config.py @@ -0,0 +1,78 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_config, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_config + + @classmethod + def setUpClass(cls): + cls.mock_get_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.modules.sonic_config.get_config" + ) + cls.mock_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.modules.sonic_config.edit_config" + ) + cls.mock_run_commands = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.modules.sonic_config.run_commands" + ) + cls.fixture_data = cls.load_fixtures('sonic_config.yaml') + + def edit_config_side_effect(self, module, commands): + """Side effect function for 'config' requests mock""" + + self.config_commands_sent.extend(commands) + + def run_commands_side_effect(self, module, commands): + """Side effect function for run_commands mock""" + + for cmd in commands: + self.config_commands_sent.append(cmd['command']) + + def validate_config_commands(self): + """Check if both list of requests sent and expected are same""" + + self.assertEqual(len(self.config_commands_valid), len(self.config_commands_sent)) + for valid_command, sent_command in zip(self.config_commands_valid, self.config_commands_sent): + self.assertEqual(valid_command, sent_command) + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.config_commands_sent = [] + self.config_commands_valid = [] + self.get_config = self.mock_get_config.start() + self.get_config.return_value = "show running-configuration\nip load-share hash ipv4 ipv4-dst-ip" + self.edit_config = self.mock_edit_config.start() + self.edit_config.side_effect = self.edit_config_side_effect + self.run_commands = self.mock_run_commands.start() + self.run_commands.side_effect = self.run_commands_side_effect + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_get_config.stop() + self.mock_edit_config.stop() + self.mock_run_commands.stop() + + def test_sonic_config_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.config_commands_valid = self.fixture_data['merged_01']['expected_commands_to_device'] + result = self.execute_module(changed=True) + self.validate_config_commands() + + def test_sonic_config_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.config_commands_valid = self.fixture_data['merged_02']['expected_commands_to_device'] + result = self.execute_module(changed=True) + self.validate_config_commands() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_copp.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_copp.py new file mode 100644 index 000000000..7e7c1be43 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_copp.py @@ -0,0 +1,86 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_copp, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicCoppModule(TestSonicModule): + module = sonic_copp + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.copp.copp.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.copp.copp.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.mock_send_requests = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.copp.copp.send_requests" + ) + cls.fixture_data = cls.load_fixtures('sonic_copp.yaml') + + def setUp(self): + super(TestSonicCoppModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'native' + self.send_requests = self.mock_send_requests.start() + self.send_requests.return_value = None + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicCoppModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + self.mock_send_requests.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_copp_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_copp_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_copp_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_copp_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_copp_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_copp_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_copp_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_copp_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_dhcp_relay.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_dhcp_relay.py new file mode 100644 index 000000000..227e08993 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_dhcp_relay.py @@ -0,0 +1,120 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_dhcp_relay, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicDhcpRelayModule(TestSonicModule): + module = sonic_dhcp_relay + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.dhcp_relay.dhcp_relay.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.dhcp_relay.dhcp_relay.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_dhcp_relay.yaml') + + def setUp(self): + super(TestSonicDhcpRelayModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicDhcpRelayModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_dhcp_relay_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['merged_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_relay_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['merged_02']['config_requests']) + + result = self.execute_module(changed=False) + self.validate_config_requests() + + def test_sonic_dhcp_relay_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['deleted_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_relay_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['deleted_02']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_relay_deleted_03(self): + set_module_args(self.fixture_data['deleted_03']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_03']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['deleted_03']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_relay_deleted_04(self): + set_module_args(self.fixture_data['deleted_04']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_04']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['deleted_04']['config_requests']) + + result = self.execute_module(changed=False) + self.validate_config_requests() + + def test_sonic_dhcp_relay_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['replaced_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_relay_replaced_02(self): + set_module_args(self.fixture_data['replaced_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_02']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['replaced_02']['config_requests']) + + result = self.execute_module(changed=False) + self.validate_config_requests() + + def test_sonic_dhcp_relay_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['overridden_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_dhcp_snooping.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_dhcp_snooping.py new file mode 100644 index 000000000..b1d2f4a03 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_dhcp_snooping.py @@ -0,0 +1,213 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_dhcp_snooping, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicDhcpSnoopingModule(TestSonicModule): + module = sonic_dhcp_snooping + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.dhcp_snooping.dhcp_snooping.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.dhcp_snooping.dhcp_snooping.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.edit_config" + ) + cls.fixture_data = cls.load_fixtures('sonic_dhcp_snooping.yaml') + + def setUp(self): + super(TestSonicDhcpSnoopingModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicDhcpSnoopingModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_dhcp_snooping_merged_01(self): + test_name = "merged_01" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_merged_02(self): + test_name = "merged_02" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_merged_03(self): + test_name = "merged_03" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_merged_04(self): + test_name = "merged_04_blank" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=False) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_deleted_01(self): + test_name = "deleted_01" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_deleted_02(self): + test_name = "deleted_02_clear_vlans" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_deleted_02_2(self): + test_name = "deleted_02_2_select_vlans" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_deleted_03(self): + test_name = "deleted_03" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_deleted_04(self): + test_name = "deleted_04_clear_bindings" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_deleted_05(self): + test_name = "deleted_05_select_bindings" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_deleted_06(self): + test_name = "deleted_06_clear_trusted" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_deleted_07(self): + test_name = "deleted_07_select_trusted" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_deleted_08(self): + test_name = "deleted_08_booleans" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_deleted_09(self): + test_name = "deleted_09_empty" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_overridden_01(self): + test_name = "overridden_01" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_replaced_01(self): + test_name = "replaced_01" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_replaced_02(self): + test_name = "replaced_02" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_replaced_03(self): + test_name = "replaced_03_vlan_replace" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_replaced_04(self): + test_name = "replaced_04_trusted" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_replaced_05(self): + test_name = "replaced_05_verify" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_dhcp_snooping_replaced_06(self): + test_name = "replaced_06_empty_bindings" + set_module_args(self.fixture_data[test_name]['module_args']) + self.initialize_facts_get_requests(self.fixture_data[test_name]['existing_config']) + self.initialize_config_requests(self.fixture_data[test_name]['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_facts.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_facts.py new file mode 100644 index 000000000..fdbe55905 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_facts.py @@ -0,0 +1,45 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_facts, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_facts + + @classmethod + def setUpClass(cls): + cls.mock_get_network_resources_facts = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts.FactsBase.get_network_resources_facts" + ) + cls.fixture_data = cls.load_fixtures('sonic_facts.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.get_network_resources_facts = self.mock_get_network_resources_facts.start() + self.get_network_resources_facts.return_value = ({ + 'ansible_network_resources': {'bgp': [{'bgp_as': '24', 'router_id': '10.1.1.1', 'log_neighbor_changes': True, 'vrf_name': 'default', + 'timers': {'holdtime': 180, 'keepalive_interval': 60}, + 'bestpath': {'as_path': {'confed': False, 'ignore': False, 'multipath_relax': True, + 'multipath_relax_as_set': False}, + 'med': {'confed': False, 'missing_as_worst': False, 'always_compare_med': False}, + 'compare_routerid': False}, 'max_med': None}]}, + 'ansible_net_gather_network_resources': ['bgp'], 'ansible_net_gather_subset': []}, []) + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_get_network_resources_facts.stop() + + def test_sonic_facts_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + result = self.execute_module(changed=False) diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_interfaces.py new file mode 100644 index 000000000..fadc8d0de --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_interfaces.py @@ -0,0 +1,90 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_interfaces, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_interfaces + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.interfaces.interfaces.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.interfaces.interfaces.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.interfaces_util.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_interfaces.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_utils_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_interfaces_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_interfaces_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_interfaces_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_interfaces_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_interfaces_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_interfaces_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_interfaces_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_interfaces_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_interfaces_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_interfaces_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_ip_neighbor.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_ip_neighbor.py new file mode 100644 index 000000000..a8e6a40cd --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_ip_neighbor.py @@ -0,0 +1,87 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_ip_neighbor, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_ip_neighbor + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.ip_neighbor.ip_neighbor.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.ip_neighbor.ip_neighbor.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_ip_neighbor.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_ip_neighbor_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_ip_neighbor_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_ip_neighbor_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_ip_neighbor_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_ip_neighbor_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_ip_neighbor_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_ip_neighbor_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_ip_neighbor_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_ip_neighbor_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_ip_neighbor_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_l2_acls.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_l2_acls.py new file mode 100644 index 000000000..2afad3e54 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_l2_acls.py @@ -0,0 +1,105 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_l2_acls, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicL2AclsModule(TestSonicModule): + module = sonic_l2_acls + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.l2_acls.l2_acls.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.l2_acls.l2_acls.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_l2_acls.yaml') + + def setUp(self): + super(TestSonicL2AclsModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicL2AclsModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def validate_config_requests(self): + # Sort by payload content for requests with same path + acl_path = 'data/openconfig-acl:acl/acl-sets/acl-set' + self.config_requests_valid.sort(key=lambda request: (request['path'], request['method'], + request['data']['acl-set'][0]['name'] if request['path'] == acl_path else '')) + self.config_requests_sent.sort(key=lambda request: (request['path'], request['method'], + request['data']['acl-set'][0]['name'] if request['path'] == acl_path else '')) + super(TestSonicL2AclsModule, self).validate_config_requests(requests_sorted=True) + + def test_sonic_l2_acls_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['merged_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l2_acls_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['merged_02']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l2_acls_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['replaced_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l2_acls_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['overridden_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l2_acls_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['deleted_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l2_acls_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['deleted_02']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_l2_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_l2_interfaces.py new file mode 100644 index 000000000..d8475bb0d --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_l2_interfaces.py @@ -0,0 +1,76 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_l2_interfaces, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_l2_interfaces + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.l2_interfaces.l2_interfaces.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.l2_interfaces.l2_interfaces.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_l2_interfaces.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_l2_interfaces_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_l2_interfaces_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l2_interfaces_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['existing_l2_interfaces_config']) + self.initialize_config_requests(self.fixture_data['merged_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l2_interfaces_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_l2_interfaces_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l2_interfaces_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_l2_interfaces_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_l3_acls.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_l3_acls.py new file mode 100644 index 000000000..4ad2b6477 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_l3_acls.py @@ -0,0 +1,105 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_l3_acls, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicL3AclsModule(TestSonicModule): + module = sonic_l3_acls + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.l3_acls.l3_acls.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.l3_acls.l3_acls.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_l3_acls.yaml') + + def setUp(self): + super(TestSonicL3AclsModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicL3AclsModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def validate_config_requests(self): + # Sort by payload content for requests with same path + acl_path = 'data/openconfig-acl:acl/acl-sets/acl-set' + self.config_requests_valid.sort(key=lambda request: (request['path'], request['method'], + request['data']['acl-set'][0]['name'] if request['path'] == acl_path else '')) + self.config_requests_sent.sort(key=lambda request: (request['path'], request['method'], + request['data']['acl-set'][0]['name'] if request['path'] == acl_path else '')) + super(TestSonicL3AclsModule, self).validate_config_requests(requests_sorted=True) + + def test_sonic_l3_acls_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['merged_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l3_acls_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['merged_02']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l3_acls_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['replaced_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l3_acls_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['overridden_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l3_acls_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['deleted_01']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l3_acls_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['facts_get_requests']) + self.initialize_config_requests(self.fixture_data['deleted_02']['config_requests']) + + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_l3_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_l3_interfaces.py new file mode 100644 index 000000000..7f57382a0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_l3_interfaces.py @@ -0,0 +1,83 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_l3_interfaces, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_l3_interfaces + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.l3_interfaces.l3_interfaces.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.l3_interfaces.l3_interfaces.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_l3_interfaces.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_l3_interfaces_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_l3_interfaces_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l3_interfaces_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_l3_interfaces_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l3_interfaces_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_l3_interfaces_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l3_interfaces_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_l3_interfaces_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_l3_interfaces_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_l3_interfaces_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_lag_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_lag_interfaces.py new file mode 100644 index 000000000..2e3baff92 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_lag_interfaces.py @@ -0,0 +1,76 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_lag_interfaces, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_lag_interfaces + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.lag_interfaces.lag_interfaces.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.lag_interfaces.lag_interfaces.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_lag_interfaces.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_lag_interfaces_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_lag_interfaces_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_lag_interfaces_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_lag_interfaces_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_lag_interfaces_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_lag_interfaces_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_lag_interfaces_deleted_03(self): + set_module_args(self.fixture_data['deleted_03']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_03']['existing_lag_interfaces_config']) + self.initialize_config_requests(self.fixture_data['deleted_03']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_logging.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_logging.py new file mode 100644 index 000000000..87c2f03e4 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_logging.py @@ -0,0 +1,87 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_logging, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_logging + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.logging.logging.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.logging.logging.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_logging.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.config_side_effect + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_logging_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_logging_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_logging_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_logging_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_logging_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_logging_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_logging_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_logging_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_logging_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_logging_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_mac.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_mac.py new file mode 100644 index 000000000..1fb06e4a3 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_mac.py @@ -0,0 +1,86 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_mac, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicMacModule(TestSonicModule): + module = sonic_mac + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.mac.mac.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.mac.mac.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.mock_send_requests = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.mac.mac.send_requests" + ) + cls.fixture_data = cls.load_fixtures('sonic_mac.yaml') + + def setUp(self): + super(TestSonicMacModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'native' + self.send_requests = self.mock_send_requests.start() + self.send_requests.return_value = None + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicMacModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + self.mock_send_requests.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_mac_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_mac_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_mac_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_mac_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_mac_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_mac_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_mac_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_mac_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_mclag.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_mclag.py new file mode 100644 index 000000000..743d5c758 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_mclag.py @@ -0,0 +1,104 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_mclag, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicMclagModule(TestSonicModule): + module = sonic_mclag + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.mclag.mclag.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.mclag.mclag.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_mclag.yaml') + + def setUp(self): + super(TestSonicMclagModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicMclagModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_mclag_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_mclag_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_mclag_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['existing_mclag_config']) + self.initialize_config_requests(self.fixture_data['merged_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_mclag_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_mclag_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_mclag_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_mclag_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_mclag_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_mclag_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_mclag_replaced_02(self): + set_module_args(self.fixture_data['replaced_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_02']['existing_mclag_config']) + self.initialize_config_requests(self.fixture_data['replaced_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_mclag_replaced_03(self): + set_module_args(self.fixture_data['replaced_03']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_03']['existing_mclag_config']) + self.initialize_config_requests(self.fixture_data['replaced_03']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_mclag_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_mclag_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_ntp.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_ntp.py new file mode 100644 index 000000000..4337fedbd --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_ntp.py @@ -0,0 +1,96 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_ntp, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_ntp + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.ntp.ntp.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.ntp.ntp.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_ntp.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.utils_edit_config = self.mock_utils_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.utils_edit_config.side_effect = self.config_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_utils_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_ntp_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_ntp_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_ntp_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['existing_ntp_config']) + self.initialize_config_requests(self.fixture_data['merged_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_ntp_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_ntp_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_ntp_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_ntp_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_ntp_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_ntp_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_ntp_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_ntp_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_pki.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_pki.py new file mode 100644 index 000000000..9eab58e03 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_pki.py @@ -0,0 +1,85 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_pki, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_pki + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.pki.pki.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.pki.pki.edit_config" + ) + + cls.fixture_data = cls.load_fixtures("sonic_pki.yaml") + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + + def test_sonic_pki_merged_01(self): + set_module_args(self.fixture_data["merged_01"]["module_args"]) + self.initialize_facts_get_requests( + self.fixture_data["merged_01"]["existing_pki_config"] + ) + self.initialize_config_requests( + self.fixture_data["merged_01"]["expected_config_requests"] + ) + self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_pki_deleted_01(self): + set_module_args(self.fixture_data["deleted_01"]["module_args"]) + self.initialize_facts_get_requests( + self.fixture_data["deleted_01"]["existing_pki_config"] + ) + self.initialize_config_requests( + self.fixture_data["deleted_01"]["expected_config_requests"] + ) + self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_pki_replaced_01(self): + set_module_args(self.fixture_data["replaced_01"]["module_args"]) + self.initialize_facts_get_requests( + self.fixture_data["replaced_01"]["existing_pki_config"] + ) + self.initialize_config_requests( + self.fixture_data["replaced_01"]["expected_config_requests"] + ) + self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_pki_overridden_01(self): + set_module_args(self.fixture_data["overridden_01"]["module_args"]) + self.initialize_facts_get_requests( + self.fixture_data["overridden_01"]["existing_pki_config"] + ) + self.initialize_config_requests( + self.fixture_data["overridden_01"]["expected_config_requests"] + ) + self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_port_breakout.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_port_breakout.py new file mode 100644 index 000000000..8f093bf8c --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_port_breakout.py @@ -0,0 +1,89 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_port_breakout, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicPortBreakoutModule(TestSonicModule): + module = sonic_port_breakout + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.port_breakout.port_breakout.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.port_breakout.port_breakout.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_port_breakout.yaml') + + def setUp(self): + super(TestSonicPortBreakoutModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.utils_edit_config = self.mock_utils_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.utils_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicPortBreakoutModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_utils_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_port_breakout_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_port_breakout_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_port_breakout_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_port_breakout_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_port_breakout_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_port_breakout_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_port_breakout_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_port_breakout_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_port_breakout_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_port_breakout_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_port_group.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_port_group.py new file mode 100644 index 000000000..89a5280af --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_port_group.py @@ -0,0 +1,105 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_port_group, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( + update_url +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_port_group + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.port_group.port_group.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.port_group.port_group.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_port_group.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + # edit_config is getting called from port_group module for 2 purposes. 1 for getting default speeds and other for sending requests. + # Hence the sonic_module definition for config_side_effect is modified to support both GET and send requests as follows. + def config_side_effect(self, module, commands): + """Side effect function for 'config' requests mock""" + responses = [] + for command in commands: + response = [] + path = update_url(command['path']) + method = command['method'].lower() + if method == 'get': + if self._facts_requests_dict.get(path): + response.append(self._facts_requests_dict[path]['code']) + response.append(self._facts_requests_dict[path].get('value', {})) + else: + data = command['data'] + + self.config_requests_sent.append({'path': path, 'method': method, 'data': data}) + entries = self._config_requests_dict.get(path, {}).get(method, []) + for entry in entries: + if data == entry[0]: + response.append(entry[1]['code']) + response.append(entry[1]['value']) + break + + responses.append(response) + + return responses + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_port_group_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_port_group_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_port_group_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_port_group_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_port_group_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_port_group_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_port_group_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_port_group_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_prefix_lists.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_prefix_lists.py new file mode 100644 index 000000000..426239237 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_prefix_lists.py @@ -0,0 +1,69 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_prefix_lists, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_prefix_lists + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.prefix_lists.prefix_lists.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.prefix_lists.prefix_lists.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_prefix_lists.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_prefix_lists_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_prefix_lists_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_prefix_lists_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_prefix_lists_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_prefix_lists_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_prefix_lists_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_radius_server.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_radius_server.py new file mode 100644 index 000000000..35a22085a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_radius_server.py @@ -0,0 +1,69 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_radius_server, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_radius_server + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.radius_server.radius_server.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.radius_server.radius_server.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_radius_server.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + + def test_sonic_radius_server_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_radius_server_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_radius_server_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_radius_server_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_radius_server_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_radius_server_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_static_routes.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_static_routes.py new file mode 100644 index 000000000..21881e6e0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_static_routes.py @@ -0,0 +1,94 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_static_routes, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_static_routes + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.static_routes.static_routes.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.static_routes.static_routes.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_static_routes.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_static_routes_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_static_routes_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_static_routes_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_static_routes_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_static_routes_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_static_routes_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_static_routes_replaced_1(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_static_routes_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_static_routes_replaced_2(self): + set_module_args(self.fixture_data['replaced_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_02']['existing_static_routes_config']) + self.initialize_config_requests(self.fixture_data['replaced_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_static_routes_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_static_routes_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_stp.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_stp.py new file mode 100644 index 000000000..f4860bb2c --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_stp.py @@ -0,0 +1,155 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_stp, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_stp + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.stp.stp.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.stp.stp.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_stp.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'native' + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.config_side_effect + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_stp_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_stp_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['merged_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_stp_merged_03(self): + set_module_args(self.fixture_data['merged_03']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_03']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['merged_03']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_stp_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_stp_replaced_02(self): + set_module_args(self.fixture_data['replaced_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_02']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['replaced_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_stp_replaced_03(self): + set_module_args(self.fixture_data['replaced_03']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_03']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['replaced_03']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_stp_replaced_04(self): + set_module_args(self.fixture_data['replaced_04']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_04']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['replaced_04']['expected_config_requests']) + result = self.execute_module(changed=True) + + def test_sonic_stp_replaced_05(self): + set_module_args(self.fixture_data['replaced_05']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_05']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['replaced_05']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_stp_replaced_06(self): + set_module_args(self.fixture_data['replaced_06']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_06']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['replaced_06']['expected_config_requests']) + result = self.execute_module(changed=True) + + def test_sonic_stp_replaced_07(self): + set_module_args(self.fixture_data['replaced_07']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_07']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['replaced_07']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_stp_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_stp_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_stp_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_stp_deleted_03(self): + set_module_args(self.fixture_data['deleted_03']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_03']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['deleted_03']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_stp_deleted_04(self): + set_module_args(self.fixture_data['deleted_04']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_04']['existing_stp_config']) + self.initialize_config_requests(self.fixture_data['deleted_04']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_system.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_system.py new file mode 100644 index 000000000..f567461f0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_system.py @@ -0,0 +1,93 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_system, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_system + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.system.system.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.system.system.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.mock_send_requests = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.system.system.send_requests" + ) + cls.fixture_data = cls.load_fixtures('sonic_system.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + self.send_requests = self.mock_send_requests.start() + self.send_requests.return_value = None + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + self.mock_send_requests.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_system_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_system_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_system_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_system_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_system_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_system_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_system_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_system_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_system_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_system_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_tacacs_server.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_tacacs_server.py new file mode 100644 index 000000000..d7297e10a --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_tacacs_server.py @@ -0,0 +1,73 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_tacacs_server, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_tacacs_server + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.tacacs_server.tacacs_server.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.tacacs_server.tacacs_server.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_tacacs_server.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_tacacs_server_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_tacacs_server_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_tacacs_server_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_tacacs_server_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_tacacs_server_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_tacacs_server_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_users.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_users.py new file mode 100644 index 000000000..a5f2e7358 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_users.py @@ -0,0 +1,73 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_users, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_users + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.users.users.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.users.users.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_users.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_users_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_users_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_users_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_users_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_users_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_users_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_vlans.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_vlans.py new file mode 100644 index 000000000..0dbc81159 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_vlans.py @@ -0,0 +1,94 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_vlans, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_vlans + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.vlans.vlans.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.vlans.vlans.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_vlans.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_vlans_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_vlans_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_vlans_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['existing_vlans_config']) + self.initialize_config_requests(self.fixture_data['merged_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_vlans_deleted_01_vlan_descr(self): + set_module_args(self.fixture_data['deleted_01_vlan_descr']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01_vlan_descr']['existing_vlans_config']) + self.initialize_config_requests(self.fixture_data['deleted_01_vlan_descr']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_vlans_deleted_02_vlan(self): + set_module_args(self.fixture_data['deleted_02_vlan']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02_vlan']['existing_vlans_config']) + self.initialize_config_requests(self.fixture_data['deleted_02_vlan']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_vlans_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_vlans_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_vlans_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_vlans_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_vrfs.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_vrfs.py new file mode 100644 index 000000000..0fb6bf4e1 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_vrfs.py @@ -0,0 +1,87 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_vrfs, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_vrfs + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.vrfs.vrfs.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.vrfs.vrfs.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.fixture_data = cls.load_fixtures('sonic_vrfs.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.config_side_effect + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_vrfs_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_vrfs_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_vrfs_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_vrfs_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_vrfs_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_vrfs_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_vrfs_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_vrfs_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_vrfs_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_vrfs_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_vxlans.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_vxlans.py new file mode 100644 index 000000000..29a125ce0 --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/network/sonic/test_sonic_vxlans.py @@ -0,0 +1,96 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import ( + patch, +) +from ansible_collections.dellemc.enterprise_sonic.plugins.modules import ( + sonic_vxlans, +) +from ansible_collections.dellemc.enterprise_sonic.tests.unit.modules.utils import ( + set_module_args, +) +from .sonic_module import TestSonicModule + + +class TestSonicInterfacesModule(TestSonicModule): + module = sonic_vxlans + + @classmethod + def setUpClass(cls): + cls.mock_facts_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.vxlans.vxlans.edit_config" + ) + cls.mock_config_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.vxlans.vxlans.edit_config" + ) + cls.mock_utils_edit_config = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils.edit_config" + ) + cls.mock_get_interface_naming_mode = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" + ) + cls.mock_send_requests = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.vxlans.vxlans.send_requests" + ) + cls.fixture_data = cls.load_fixtures('sonic_vxlans.yaml') + + def setUp(self): + super(TestSonicInterfacesModule, self).setUp() + self.facts_edit_config = self.mock_facts_edit_config.start() + self.config_edit_config = self.mock_config_edit_config.start() + self.facts_edit_config.side_effect = self.facts_side_effect + self.config_edit_config.side_effect = self.config_side_effect + self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() + self.get_interface_naming_mode.return_value = 'standard' + self.send_requests = self.mock_send_requests.start() + self.send_requests.return_value = None + self.utils_edit_config = self.mock_utils_edit_config.start() + self.utils_edit_config.side_effect = self.facts_side_effect + + def tearDown(self): + super(TestSonicInterfacesModule, self).tearDown() + self.mock_facts_edit_config.stop() + self.mock_config_edit_config.stop() + self.mock_get_interface_naming_mode.stop() + self.mock_send_requests.stop() + self.mock_utils_edit_config.stop() + + def test_sonic_vxlans_merged_01(self): + set_module_args(self.fixture_data['merged_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_01']['existing_vxlans_config']) + self.initialize_config_requests(self.fixture_data['merged_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_vxlans_deleted_01(self): + set_module_args(self.fixture_data['deleted_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_vxlans_config']) + self.initialize_config_requests(self.fixture_data['deleted_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_vxlans_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_vxlans_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + # When replace is executed, it first deletes the existing config and then patches the new config. + # As part of UT, sonic_module.py does a SORTING before comparison and hence the sequence of the actual configs sent to device varies from the sequence. + # in which the UT test case compares with expected results. The actual sequence in which the requests are sent to device should be working fine. + def test_sonic_vxlans_replaced_02(self): + set_module_args(self.fixture_data['replaced_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_02']['existing_vxlans_config']) + self.initialize_config_requests(self.fixture_data['replaced_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_vxlans_overridden_02(self): + set_module_args(self.fixture_data['overridden_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_02']['existing_vxlans_config']) + self.initialize_config_requests(self.fixture_data['overridden_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/utils.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/utils.py new file mode 100644 index 000000000..0157649ce --- /dev/null +++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/modules/utils.py @@ -0,0 +1,51 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json + +from ansible.module_utils import basic +from ansible.module_utils._text import to_bytes + +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat import unittest +from ansible_collections.dellemc.enterprise_sonic.tests.unit.compat.mock import patch + + +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) |