diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 031505895..357a60aa4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - id: update-docs - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v5.0.0 hooks: - id: check-merge-conflict - id: check-symlinks @@ -22,8 +22,8 @@ repos: hooks: - id: add-trailing-comma - - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v4.0.0-alpha.8" + - repo: https://github.com/pycontribs/mirrors-prettier + rev: "v3.4.2" hooks: - id: prettier entry: env CI=1 bash -c "prettier --list-different . || ec=$? && prettier --loglevel=error --write . && exit $ec" @@ -41,11 +41,11 @@ repos: args: ["--filter-files"] - repo: https://github.com/psf/black - rev: 24.3.0 + rev: 24.10.0 hooks: - id: black - repo: https://github.com/pycqa/flake8 - rev: 7.0.0 + rev: 7.1.1 hooks: - id: flake8 diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9fdf040df..e5b5b0cd9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,91 @@ Cisco Ios Collection Release Notes .. contents:: Topics +v9.0.3 +====== + +Bugfixes +-------- + +- ios_bgp_address_family - fix parsing of password_options while gathering password configuration from appliance. +- ios_bgp_global - fix parsing of password_options while gathering password configuration from appliance. + +Documentation Changes +--------------------- + +- Includes a new support related section in the README. +- Removed the Roadmap section from the README. + +v9.0.2 +====== + +Bugfixes +-------- + +- ios_bgp_address_family - Add support for maximum-paths configuration. +- ios_bgp_address_family - Add support for maximum-secondary-paths configuration. +- ios_interfaces - Fixes rendering of FiftyGigabitEthernet as it was wrongly rendering FiftyGigabitEthernet as FiveGigabitEthernet. +- ios_snmp_server - Fixes an issue where enabling the read-only (ro) attribute in communities was not idempotent. +- ios_static_routes - Fix processing of metric_distance as it was wrongly populated under the forward_router_address attribute. + +v9.0.1 +====== + +Bugfixes +-------- + +- bgp_global - fix ebgp_multihop recognnition and hop_count settings +- ios_service - Fix a typo causing log timestamps not being configurable +- ios_vlans - Make the module fail when vlan name is longer than 32 characters with configuration as VTPv1 and VTPv2. +- static_routes - add TenGigabitEthernet as valid interface + +Documentation Changes +--------------------- + +- ios_facts - update documentation for ansible_net_memtotal_mb, ansible_net_memfree_mb return values as mebibytes (MiB), not megabits (Mb) + +v9.0.0 +====== + +Release Summary +--------------- + +Starting from this release, the minimum `ansible-core` version this collection requires is `2.15.0`. The last known version compatible with ansible-core<2.15 is v8.0.0. + +Major Changes +------------- + +- Bumping `requires_ansible` to `>=2.15.0`, since previous ansible-core versions are EoL now. + +Minor Changes +------------- + +- Add ios_vrf_global resource module in favor of ios_vrf module (fixes - https://github.com/ansible-collections/cisco.ios/pull/1055) + +Deprecated Features +------------------- + +- ios_bgp_address_family - deprecated attribute password in favour of password_options within neigbhors. +- ios_bgp_global - deprecated attributes aggregate_address, bestpath, inject_map, ipv4_with_subnet, ipv6_with_subnet, nopeerup_delay, distribute_list, address, tag, ipv6_addresses, password, route_map, route_server_context and scope +- ios_linkagg - deprecate legacy module ios_linkagg +- ios_lldp - deprecate legacy module ios_lldp + +Bugfixes +-------- + +- ios_acls - fix incorrect mapping of port 135/udp to msrpc. +- ios_l3_interfaces - Fix gathering wrong facts for source interface in ipv4. +- ios_service - Add tcp_small_servers and udp_small_servers attributes, to generate configuration. +- ios_service - Fix timestamps attribute, to generate right configuration. +- ios_static_routes - Fix gathering facts by properly distinguising routes. +- l2_interfaces - If a large number of VLANs are affected, the configuration will now be correctly split into several commands. +- snmp_server - Fix configuration command for snmp-server host. +- snmp_server - Fix wrong syntax of snmp-server host command generation. + +Documentation Changes +--------------------- + +- logging_global - update documentation for severity attribute within buffered. v8.0.0 ====== @@ -686,7 +771,7 @@ Documentation Changes New Modules ----------- -- ios_hostname - Resource module to configure hostname. +- ios_hostname - hostname resource module v2.6.0 ====== @@ -717,7 +802,7 @@ Documentation Changes New Modules ----------- -- ios_snmp_server - Resource module to configure snmp server. +- ios_snmp_server - snmp_server resource module v2.5.0 ====== @@ -750,7 +835,7 @@ Documentation Changes New Modules ----------- -- ios_ntp_global - Resource module to configure NTP. +- ios_ntp_global - ntp_global resource module v2.4.0 ====== @@ -837,8 +922,8 @@ Bugfixes New Modules ----------- -- ios_logging_global - Resource module to configure logging. -- ios_prefix_lists - Resource module to configure prefix lists. +- ios_logging_global - Logging resource module. +- ios_prefix_lists - Prefix Lists resource module. v2.1.0 ====== @@ -862,7 +947,7 @@ Bugfixes New Modules ----------- -- ios_route_maps - Resource module to configure route maps. +- ios_route_maps - Route Maps resource module. v2.0.1 ====== @@ -907,7 +992,7 @@ Bugfixes New Modules ----------- -- ios_bgp_address_family - Resource module to configure BGP Address family. +- ios_bgp_address_family - BGP Address Family resource module. v1.3.0 ====== @@ -930,7 +1015,7 @@ Bugfixes New Modules ----------- -- ios_bgp_global - Resource module to configure BGP. +- ios_bgp_global - BGP Global resource module v1.2.1 ====== @@ -961,7 +1046,7 @@ Bugfixes New Modules ----------- -- ios_ospf_interfaces - Resource module to configure OSPF interfaces. +- ios_ospf_interfaces - OSPF Interfaces resource module v1.1.0 ====== @@ -982,7 +1067,7 @@ Bugfixes New Modules ----------- -- ios_ospfv3 - Resource module to configure OSPFv3. +- ios_ospfv3 - OSPFv3 resource module v1.0.3 ====== @@ -1034,26 +1119,34 @@ Cliconf New Modules ----------- -- ios_acl_interfaces - Resource module to configure ACL interfaces. -- ios_acls - Resource module to configure ACLs. -- ios_banner - Module to configure multiline banners. -- ios_command - Module to run commands on remote devices. -- ios_config - Module to manage configuration sections. -- ios_facts - Module to collect facts from remote devices. -- ios_interfaces - Resource module to configure interfaces. -- ios_l2_interfaces - Resource module to configure L2 interfaces. -- ios_l3_interfaces - Resource module to configure L3 interfaces. -- ios_lacp - Resource module to configure LACP. -- ios_lacp_interfaces - Resource module to configure LACP interfaces. -- ios_lag_interfaces - Resource module to configure LAG interfaces. -- ios_linkagg - Module to configure link aggregation groups. -- ios_lldp - (deprecated, removed after 2024-06-01) Manage LLDP configuration on Cisco IOS network devices. -- ios_lldp_global - Resource module to configure LLDP. -- ios_lldp_interfaces - Resource module to configure LLDP interfaces. -- ios_ospfv2 - Resource module to configure OSPFv2. -- ios_ping - Tests reachability using ping from IOS switch. -- ios_static_routes - Resource module to configure static routes. -- ios_system - Module to manage the system attributes. -- ios_user - Module to manage the aggregates of local users. -- ios_vlans - Resource module to configure VLANs. -- ios_vrf - Module to configure VRF definitions. +- ios_acl_interfaces - ACL interfaces resource module +- ios_acls - ACLs resource module +- ios_banner - Manage multiline banners on Cisco IOS devices +- ios_bgp - Configure global BGP protocol settings on Cisco IOS. +- ios_command - Run commands on remote devices running Cisco IOS +- ios_config - Manage Cisco IOS configuration sections +- ios_facts - Collect facts from remote devices running Cisco IOS +- ios_interface - (deprecated, removed after 2022-06-01) Manage Interface on Cisco IOS network devices +- ios_interfaces - Interfaces resource module +- ios_l2_interface - (deprecated, removed after 2022-06-01) Manage Layer-2 interface on Cisco IOS devices. +- ios_l2_interfaces - L2 interfaces resource module +- ios_l3_interface - (deprecated, removed after 2022-06-01) Manage Layer-3 interfaces on Cisco IOS network devices. +- ios_l3_interfaces - L3 interfaces resource module +- ios_lacp - LACP resource module +- ios_lacp_interfaces - LACP interfaces resource module +- ios_lag_interfaces - LAG interfaces resource module +- ios_linkagg - Manage link aggregation groups on Cisco IOS network devices +- ios_lldp - Manage LLDP configuration on Cisco IOS network devices. +- ios_lldp_global - LLDP resource module +- ios_lldp_interfaces - LLDP interfaces resource module +- ios_logging - Manage logging on network devices +- ios_ntp - Manages core NTP configuration. +- ios_ospfv2 - OSPFv2 resource module +- ios_ping - Tests reachability using ping from Cisco IOS network devices +- ios_static_route - (deprecated, removed after 2022-06-01) Manage static IP routes on Cisco IOS network devices +- ios_static_routes - Static routes resource module +- ios_system - Manage the system attributes on Cisco IOS devices +- ios_user - Manage the aggregate of local users on Cisco IOS device +- ios_vlan - (deprecated, removed after 2022-06-01) Manage VLANs on IOS network devices +- ios_vlans - VLANs resource module +- ios_vrf - Manage the collection of VRF definitions on Cisco IOS devices diff --git a/README.md b/README.md index 8d8456157..58187e9ec 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,33 @@ The Ansible Cisco IOS collection includes a variety of Ansible content to help a This collection has been tested against Cisco IOS XE Version 17.3 on CML. +## Support + +As a Red Hat Ansible [Certified Content](https://catalog.redhat.com/software/search?target_platforms=Red%20Hat%20Ansible%20Automation%20Platform), this collection is entitled to [support](https://access.redhat.com/support/) through [Ansible Automation Platform](https://www.redhat.com/en/technologies/management/ansible) (AAP). + +If a support case cannot be opened with Red Hat and the collection has been obtained either from [Galaxy](https://galaxy.ansible.com/ui/) or [GitHub](https://github.com/ansible-collections/cisco.ios), there is community support available at no charge. + +You can join us on [#network:ansible.com](https://matrix.to/#/#network:ansible.com) room or the [Ansible Forum Network Working Group](https://forum.ansible.com/g/network-wg). + +For more information you can check the communication section below. + +## Communication + +* Join the Ansible forum: + * [Get Help](https://forum.ansible.com/c/help/6): get help or help others. + * [Posts tagged with 'network'](https://forum.ansible.com/tag/network): subscribe to participate in collection-related conversations. + * [Ansible Network Automation Working Group](https://forum.ansible.com/g/network-wg/): by joining the team you will automatically get subscribed to the posts tagged with [network](https://forum.ansible.com/tags/network). + * [Social Spaces](https://forum.ansible.com/c/chat/4): gather and interact with fellow enthusiasts. + * [News & Announcements](https://forum.ansible.com/c/news/5): track project-wide announcements including social events. + +* The Ansible [Bullhorn newsletter](https://docs.ansible.com/ansible/devel/community/communication.html#the-bullhorn): used to announce releases and important changes. + +For more information about communication, see the [Ansible communication guide](https://docs.ansible.com/ansible/devel/community/communication.html). + ## Ansible version compatibility -This collection has been tested against following Ansible versions: **>=2.14.0**. +This collection has been tested against following Ansible versions: **>=2.15.0**. For collections that support Ansible 2.9, please ensure you update your `network_os` to use the fully qualified collection name (for example, `cisco.ios.ios`). @@ -52,8 +75,6 @@ Name | Description [cisco.ios.ios_lacp](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_lacp_module.rst)|Resource module to configure LACP. [cisco.ios.ios_lacp_interfaces](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_lacp_interfaces_module.rst)|Resource module to configure LACP interfaces. [cisco.ios.ios_lag_interfaces](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_lag_interfaces_module.rst)|Resource module to configure LAG interfaces. -[cisco.ios.ios_linkagg](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_linkagg_module.rst)|Module to configure link aggregation groups. -[cisco.ios.ios_lldp](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_lldp_module.rst)|(deprecated, removed after 2024-06-01) Manage LLDP configuration on Cisco IOS network devices. [cisco.ios.ios_lldp_global](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_lldp_global_module.rst)|Resource module to configure LLDP. [cisco.ios.ios_lldp_interfaces](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_lldp_interfaces_module.rst)|Resource module to configure LLDP interfaces. [cisco.ios.ios_logging_global](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_logging_global_module.rst)|Resource module to configure logging. @@ -71,6 +92,8 @@ Name | Description [cisco.ios.ios_user](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_user_module.rst)|Module to manage the aggregates of local users. [cisco.ios.ios_vlans](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_vlans_module.rst)|Resource module to configure VLANs. [cisco.ios.ios_vrf](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_vrf_module.rst)|Module to configure VRF definitions. +[cisco.ios.ios_vrf_global](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_vrf_global_module.rst)|Resource module to configure global VRF definitions. +[cisco.ios.ios_vrf_interfaces](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_vrf_interfaces_module.rst)|Manages VRF configuration on interfaces. [cisco.ios.ios_vxlan_vtep](https://github.com/ansible-collections/cisco.ios/blob/main/docs/cisco.ios.ios_vxlan_vtep_module.rst)|Resource module to configure VXLAN VTEP interface. @@ -142,10 +165,6 @@ Please read and familiarize yourself with this document. Release notes are available [here](https://github.com/ansible-collections/cisco.ios/blob/main/CHANGELOG.rst). -## Roadmap - - - ## More information - [Ansible network resources](https://docs.ansible.com/ansible/latest/network/getting_started/network_resources.html) diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index e57571cd7..a49ff81a1 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -2,75 +2,107 @@ ancestor: null releases: 1.0.0: modules: - - description: Resource module to configure ACL interfaces. + - description: ACL interfaces resource module name: ios_acl_interfaces namespace: "" - - description: Resource module to configure ACLs. + - description: ACLs resource module name: ios_acls namespace: "" - - description: Module to configure multiline banners. + - description: Manage multiline banners on Cisco IOS devices name: ios_banner namespace: "" - - description: Module to run commands on remote devices. + - description: Configure global BGP protocol settings on Cisco IOS. + name: ios_bgp + namespace: "" + - description: Run commands on remote devices running Cisco IOS name: ios_command namespace: "" - - description: Module to manage configuration sections. + - description: Manage Cisco IOS configuration sections name: ios_config namespace: "" - - description: Module to collect facts from remote devices. + - description: Collect facts from remote devices running Cisco IOS name: ios_facts namespace: "" - - description: Resource module to configure interfaces. + - description: + (deprecated, removed after 2022-06-01) Manage Interface on Cisco + IOS network devices + name: ios_interface + namespace: "" + - description: Interfaces resource module name: ios_interfaces namespace: "" - - description: Resource module to configure L2 interfaces. + - description: + (deprecated, removed after 2022-06-01) Manage Layer-2 interface + on Cisco IOS devices. + name: ios_l2_interface + namespace: "" + - description: L2 interfaces resource module name: ios_l2_interfaces namespace: "" - - description: Resource module to configure L3 interfaces. + - description: + (deprecated, removed after 2022-06-01) Manage Layer-3 interfaces + on Cisco IOS network devices. + name: ios_l3_interface + namespace: "" + - description: L3 interfaces resource module name: ios_l3_interfaces namespace: "" - - description: Resource module to configure LACP. + - description: LACP resource module name: ios_lacp namespace: "" - - description: Resource module to configure LACP interfaces. + - description: LACP interfaces resource module name: ios_lacp_interfaces namespace: "" - - description: Resource module to configure LAG interfaces. + - description: LAG interfaces resource module name: ios_lag_interfaces namespace: "" - - description: Module to configure link aggregation groups. + - description: Manage link aggregation groups on Cisco IOS network devices name: ios_linkagg namespace: "" - - description: - (deprecated, removed after 2024-06-01) Manage LLDP configuration - on Cisco IOS network devices. + - description: Manage LLDP configuration on Cisco IOS network devices. name: ios_lldp namespace: "" - - description: Resource module to configure LLDP. + - description: LLDP resource module name: ios_lldp_global namespace: "" - - description: Resource module to configure LLDP interfaces. + - description: LLDP interfaces resource module name: ios_lldp_interfaces namespace: "" - - description: Resource module to configure OSPFv2. + - description: Manage logging on network devices + name: ios_logging + namespace: "" + - description: Manages core NTP configuration. + name: ios_ntp + namespace: "" + - description: OSPFv2 resource module name: ios_ospfv2 namespace: "" - - description: Tests reachability using ping from IOS switch. + - description: Tests reachability using ping from Cisco IOS network devices name: ios_ping namespace: "" - - description: Resource module to configure static routes. + - description: + (deprecated, removed after 2022-06-01) Manage static IP routes + on Cisco IOS network devices + name: ios_static_route + namespace: "" + - description: Static routes resource module name: ios_static_routes namespace: "" - - description: Module to manage the system attributes. + - description: Manage the system attributes on Cisco IOS devices name: ios_system namespace: "" - - description: Module to manage the aggregates of local users. + - description: Manage the aggregate of local users on Cisco IOS device name: ios_user namespace: "" - - description: Resource module to configure VLANs. + - description: + (deprecated, removed after 2022-06-01) Manage VLANs on IOS network + devices + name: ios_vlan + namespace: "" + - description: VLANs resource module name: ios_vlans namespace: "" - - description: Module to configure VRF definitions. + - description: Manage the collection of VRF definitions on Cisco IOS devices name: ios_vrf namespace: "" plugins: @@ -121,7 +153,7 @@ releases: minor_changes: - Add ios_ospfv3 module. modules: - - description: Resource module to configure OSPFv3. + - description: OSPFv3 resource module name: ios_ospfv3 namespace: "" release_date: "2020-10-01" @@ -140,7 +172,7 @@ releases: minor_changes: - Add ios_ospf_interfaces module. modules: - - description: Resource module to configure OSPF interfaces. + - description: OSPF Interfaces resource module name: ios_ospf_interfaces namespace: "" release_date: "2020-11-01" @@ -164,7 +196,7 @@ releases: minor_changes: - Add ios_bgp_global module. modules: - - description: Resource module to configure BGP. + - description: BGP Global resource module name: ios_bgp_global namespace: "" release_date: "2021-01-29" @@ -182,7 +214,7 @@ releases: - Add ios_bgp_address_family Resource Module. (https://github.com/ansible-collections/cisco.ios/pull/219). - Adds support for single_user_mode command output caching. (https://github.com/ansible-collections/cisco.ios/pull/204). modules: - - description: Resource module to configure BGP Address family. + - description: BGP Address Family resource module. name: ios_bgp_address_family namespace: "" release_date: "2021-02-25" @@ -214,7 +246,7 @@ releases: security_fixes: - To fix Cisco IOS no log issue and add ignore txt for 2.12 (https://github.com/ansible-collections/cisco.ios/pull/304). modules: - - description: Resource module to configure route maps. + - description: Route Maps resource module. name: ios_route_maps namespace: "" release_date: "2021-04-27" @@ -233,10 +265,10 @@ releases: - Add ios_logging_global module. - IOS Prefix list resource module. modules: - - description: Resource module to configure logging. + - description: Logging resource module. name: ios_logging_global namespace: "" - - description: Resource module to configure prefix lists. + - description: Prefix Lists resource module. name: ios_prefix_lists namespace: "" release_date: "2021-05-18" @@ -345,7 +377,7 @@ releases: - sd_wan_support.yaml - set_allow_duplicates.yaml modules: - - description: Resource module to configure NTP. + - description: ntp_global resource module name: ios_ntp_global namespace: "" release_date: "2021-09-24" @@ -377,7 +409,7 @@ releases: - sanity-213.yaml - snmp_server_resource_module.yaml modules: - - description: Resource module to configure snmp server. + - description: snmp_server resource module name: ios_snmp_server namespace: "" release_date: "2021-12-07" @@ -420,7 +452,7 @@ releases: - snmp_server_feature_user.yaml - static_route_interface_issue.yaml modules: - - description: Resource module to configure hostname. + - description: hostname resource module name: ios_hostname namespace: "" release_date: "2022-01-31" @@ -1160,3 +1192,99 @@ releases: - add_restore_support.yaml - sanityfix.yml release_date: "2024-04-12" + 9.0.0: + changes: + bugfixes: + - ios_acls - fix incorrect mapping of port 135/udp to msrpc. + - ios_l3_interfaces - Fix gathering wrong facts for source interface in ipv4. + - ios_service - Add tcp_small_servers and udp_small_servers attributes, to generate + configuration. + - ios_service - Fix timestamps attribute, to generate right configuration. + - ios_static_routes - Fix gathering facts by properly distinguising routes. + - l2_interfaces - If a large number of VLANs are affected, the configuration + will now be correctly split into several commands. + - snmp_server - Fix configuration command for snmp-server host. + - snmp_server - Fix wrong syntax of snmp-server host command generation. + deprecated_features: + - ios_bgp_address_family - deprecated attribute password in favour of password_options + within neigbhors. + - ios_bgp_global - deprecated attributes aggregate_address, bestpath, inject_map, + ipv4_with_subnet, ipv6_with_subnet, nopeerup_delay, distribute_list, address, + tag, ipv6_addresses, password, route_map, route_server_context and scope + - ios_linkagg - deprecate legacy module ios_linkagg + - ios_lldp - deprecate legacy module ios_lldp + doc_changes: + - logging_global - update documentation for severity attribute within buffered. + major_changes: + - Bumping `requires_ansible` to `>=2.15.0`, since previous ansible-core versions + are EoL now. + minor_changes: + - Add ios_vrf_global resource module in favor of ios_vrf module (fixes - https://github.com/ansible-collections/cisco.ios/pull/1055) + release_summary: + Starting from this release, the minimum `ansible-core` version + this collection requires is `2.15.0`. The last known version compatible with + ansible-core<2.15 is v8.0.0. + fragments: + - 1055_add_vrf_global_module.yaml + - ios_acls_udp.yml + - ios_logging.yml + - ios_service_fix.yml + - l2_interfaces_multiline_trunk.yml + - l3_sinterface_parser.yml + - min_215.yaml + - remove_dep.yml + - snmp_parse_fix.yml + - snmp_server_host.yml + - static_routes_facts_fix.yml + release_date: "2024-06-11" + 9.0.1: + changes: + bugfixes: + - bgp_global - fix ebgp_multihop recognnition and hop_count settings + - ios_service - Fix a typo causing log timestamps not being configurable + - ios_vlans - Make the module fail when vlan name is longer than 32 characters + with configuration as VTPv1 and VTPv2. + - static_routes - add TenGigabitEthernet as valid interface + doc_changes: + - ios_facts - update documentation for ansible_net_memtotal_mb, ansible_net_memfree_mb + return values as mebibytes (MiB), not megabits (Mb) + fragments: + - 1097-ios-facts-MiB.yml + - 1101-typo-ios_service.yml + - pr-1095-fragment.yaml + - vlan_err_reg.yaml + release_date: "2024-08-05" + 9.0.2: + changes: + bugfixes: + - ios_bgp_address_family - Add support for maximum-paths configuration. + - ios_bgp_address_family - Add support for maximum-secondary-paths configuration. + - ios_interfaces - Fixes rendering of FiftyGigabitEthernet as it was wrongly + rendering FiftyGigabitEthernet as FiveGigabitEthernet. + - ios_snmp_server - Fixes an issue where enabling the read-only (ro) attribute + in communities was not idempotent. + - ios_static_routes - Fix processing of metric_distance as it was wrongly populated + under the forward_router_address attribute. + fragments: + - 0-readme.yml + - bgp_add_fam_max_path.yaml + - fix-snmp-server-idempotency.yml + - fix_interface_fifty_interface.yml + - ios_static_routes.yaml + - pylint_fix.yaml + release_date: "2024-09-16" + 9.0.3: + changes: + bugfixes: + - ios_bgp_address_family - fix parsing of password_options while gathering password + configuration from appliance. + - ios_bgp_global - fix parsing of password_options while gathering password + configuration from appliance. + doc_changes: + - Includes a new support related section in the README. + - Removed the Roadmap section from the README. + fragments: + - bgp_global_render_pass.yml + - support_section.yml + - trivial_ignore_files.yaml + release_date: "2024-10-16" diff --git a/changelogs/fragments/fix_purge_and_overridden_operations.yaml b/changelogs/fragments/fix_purge_and_overridden_operations.yaml new file mode 100644 index 000000000..3bd498f6f --- /dev/null +++ b/changelogs/fragments/fix_purge_and_overridden_operations.yaml @@ -0,0 +1,6 @@ +--- +bugfixes: + - Fix purged state operation to enable users to completely remove VLAN configurations. + - Fix overridden state operations to ensure excluded VLANs in the provided configuration are removed, thus overriding the VLAN configuration. + - Improve documentation to provide clarity on the "shutdown" variable. + - Improve unit tests to align with the changes made. diff --git a/changelogs/fragments/ios_acls_udp.yml b/changelogs/fragments/ios_acls_udp.yml deleted file mode 100644 index 71df169e5..000000000 --- a/changelogs/fragments/ios_acls_udp.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - ios_acls - fix incorrect mapping of port 135/udp to msrpc. diff --git a/changelogs/fragments/ios_service_fix.yml b/changelogs/fragments/ios_service_fix.yml deleted file mode 100644 index 3ec24d6a9..000000000 --- a/changelogs/fragments/ios_service_fix.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -bugfixes: - - ios_service - Fix timestamps attribute, to generate right configuration. - - ios_service - Add tcp_small_servers and udp_small_servers attributes, to generate configuration. diff --git a/changelogs/fragments/l3_sinterface_parser.yml b/changelogs/fragments/l3_sinterface_parser.yml deleted file mode 100644 index c16a5e179..000000000 --- a/changelogs/fragments/l3_sinterface_parser.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - ios_l3_interfaces - Fix gathering wrong facts for source interface in ipv4. diff --git a/changelogs/fragments/rm_vrf_interfaces.yaml b/changelogs/fragments/rm_vrf_interfaces.yaml new file mode 100644 index 000000000..9ed25308a --- /dev/null +++ b/changelogs/fragments/rm_vrf_interfaces.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Added ios_vrf_interfaces resource module,that helps with configuration of vrfs within interface diff --git a/changelogs/fragments/snmp_parse_fix.yml b/changelogs/fragments/snmp_parse_fix.yml deleted file mode 100644 index fb2672474..000000000 --- a/changelogs/fragments/snmp_parse_fix.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - snmp_server - Fix configuration command for snmp-server host. diff --git a/changelogs/fragments/snmp_server_host.yml b/changelogs/fragments/snmp_server_host.yml deleted file mode 100644 index b8bbe1ee0..000000000 --- a/changelogs/fragments/snmp_server_host.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - snmp_server - Fix wrong syntax of snmp-server host command generation. diff --git a/changelogs/fragments/static_routes_facts_fix.yml b/changelogs/fragments/static_routes_facts_fix.yml deleted file mode 100644 index 82bc47710..000000000 --- a/changelogs/fragments/static_routes_facts_fix.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - ios_static_routes - Fix gathering facts by properly distinguising routes. diff --git a/docs/cisco.ios.ios_acls_module.rst b/docs/cisco.ios.ios_acls_module.rst index bb0328d87..b098bcd4a 100644 --- a/docs/cisco.ios.ios_acls_module.rst +++ b/docs/cisco.ios.ios_acls_module.rst @@ -424,25 +424,6 @@ Parameters
Evaluate an access list
- - - - - -
- fragments - -
- string -
- - - - -
Check non-initial fragments.
-
This option is DEPRECATED and is replaced with enable_fragments which accepts bool as input this attribute will be removed after 2024-01-01.
- - diff --git a/docs/cisco.ios.ios_bgp_address_family_module.rst b/docs/cisco.ios.ios_bgp_address_family_module.rst index 268217811..810b0a56c 100644 --- a/docs/cisco.ios.ios_bgp_address_family_module.rst +++ b/docs/cisco.ios.ios_bgp_address_family_module.rst @@ -1402,6 +1402,150 @@ Parameters + + + + +
+ maximum_paths + +
+ dictionary +
+ + + + +
Forward packets over multiple paths
+ + + + + + + +
+ eibgp + +
+ integer +
+ + + + +
Both eBGP and iBGP paths as multipath
+ + + + + + + +
+ ibgp + +
+ integer +
+ + + + +
iBGP-multipath
+ + + + + + + +
+ paths + +
+ integer +
+ + + + +
Number of paths
+ + + + + + + +
+ maximum_secondary_paths + +
+ dictionary +
+ + + + +
Maximum secondary paths
+ + + + + + + +
+ eibgp + +
+ integer +
+ + + + +
Both eBGP and iBGP paths as secondary multipath
+ + + + + + + +
+ ibgp + +
+ integer +
+ + + + +
iBGP-secondary-multipath
+ + + + + + + +
+ paths + +
+ integer +
+ + + + +
Number of secondary paths
+ + + @@ -3512,25 +3656,6 @@ Parameters - - - - - -
- password - -
- string -
- - - - -
Set a password
-
This option is DEPRECATED and is replaced with password_options which accepts dict as input, this attribute will be removed after 2024-06-01.
- - diff --git a/docs/cisco.ios.ios_bgp_global_module.rst b/docs/cisco.ios.ios_bgp_global_module.rst index 77eb908c5..f3ec41e59 100644 --- a/docs/cisco.ios.ios_bgp_global_module.rst +++ b/docs/cisco.ios.ios_bgp_global_module.rst @@ -50,172 +50,6 @@ Parameters - -
- aggregate_address - -
- dictionary -
- - - - -
Configure BGP aggregate entry
-
This option is DEPRECATED and is replaced with aggregate_addresses which accepts list of dict as input, this attribute will be removed after 2024-06-01.
- - - - - - -
- address - -
- string -
- - - - -
Specify aggregate address
- - - - - - -
- advertise_map - -
- string -
- - - - -
Set condition to advertise attribute
- - - - - - -
- as_confed_set - -
- boolean -
- - - - - -
Generate AS confed set path information
- - - - - - -
- as_set - -
- boolean -
- - - - - -
Generate AS set path information
- - - - - - -
- attribute_map - -
- string -
- - - - -
Set attributes of aggregate
- - - - - - -
- netmask - -
- string -
- - - - -
Specify aggregate mask
- - - - - - -
- summary_only - -
- boolean -
- - - - - -
Filter more specific routes from updates
- - - - - - -
- suppress_map - -
- string -
- - - - -
Conditionally filter more specific routes from updates
- - - - -
aggregate_addresses @@ -596,186 +430,17 @@ Parameters -
Select group-best path
- - - - - - - - -
- send - -
- boolean -
- - - - - -
Send additional paths to neighbors
- - - - - - - -
- advertise_best_external - -
- boolean -
- - - - - -
Advertise best external path to internal peers
- - - - - - -
- aggregate_timer - -
- integer -
- - - - -
Configure Aggregation Timer
-
Please refer vendor documentation for valid values
- - - - - - -
- always_compare_med - -
- boolean -
- - - - - -
Allow comparing MED from different neighbors
- - - - - - -
- asnotation - -
- boolean -
- - - - - -
Change the default as plain notation
-
asdot notation
- - - - - - -
- bestpath - -
- list - / elements=dictionary -
- - - - -
Change the default bestpath selection
-
This option is DEPRECATED and replaced with bestpath_options of type dict, this attribute will be removed after 2024-06-01.
- - - - - - - -
- aigp - -
- boolean -
- - - - - -
if both paths doesn't have aigp ignore on bestpath comparison
-
ignore
- - - - - - - -
- compare_routerid - -
- boolean -
- - - - - -
Compare router-id for identical EBGP paths
+
Select group-best path
+
- cost_community + send
boolean @@ -788,16 +453,16 @@ Parameters -
cost community
+
Send additional paths to neighbors
+ - - +
- igp_metric + advertise_best_external
boolean @@ -810,36 +475,33 @@ Parameters -
igp metric
-
Ignore igp metric in bestpath selection
+
Advertise best external path to internal peers
- - +
- med + aggregate_timer
- dictionary + integer
-
MED attribute
+
Configure Aggregation Timer
+
Please refer vendor documentation for valid values
- - - + - +
- confed + always_compare_med
boolean @@ -852,17 +514,15 @@ Parameters -
Compare MED among confederation paths
+
Allow comparing MED from different neighbors
- - - +
- missing_as_worst + asnotation
boolean @@ -875,11 +535,10 @@ Parameters -
Treat missing MED as the least preferred one
+
Change the default as plain notation
+
asdot notation
- - @@ -1929,83 +1588,6 @@ Parameters - - - - -
- inject_map - -
- dictionary -
- - - - -
Routemap which specifies prefixes to inject
-
This option is DEPRECATED and is updated with inject_maps which is a list of dict, this attribute will be removed after 2024-06-01.
- - - - - - - -
- copy_attributes - -
- boolean -
- - -
    Choices: -
  • no
  • -
  • yes
  • -
- - -
Copy attributes from aggregate
- - - - - - - -
- exist_map_name - -
- string -
- - - - -
route-map name
- - - - - - - -
- name - -
- string -
- - - - -
route-map name
- - - @@ -2157,46 +1739,6 @@ Parameters
IPv6 subnet range(X:X:X:X::X/<0-128>)
- - - - - - -
- ipv4_with_subnet - -
- string -
- - - - -
IPv4 subnet range(A.B.C.D/nn)
-
This option is DEPRECATED and is updated with host_with_subnet which is a common attribute for address, this attribute will be removed after 2024-06-01.
- - - - - - - - -
- ipv6_with_subnet - -
- string -
- - - - -
IPv6 subnet range(X:X:X:X::X/<0-128>)
-
This option is DEPRECATED and is updated with host_with_subnet which is a common attribute for address attribute will be removed after 2024-06-01.
- - @@ -2307,107 +1849,7 @@ Parameters -
Nexthop tracking commands
- - - - - - - -
- route_map - -
- string -
- - - - -
Route map for valid nexthops
- - - - - - - -
- trigger - -
- dictionary -
- - - - -
nexthop trackings
- - - - - - - - -
- delay - -
- integer -
- - - - -
Set the delay to trigger nexthop tracking
-
Please refer vendor documentation for valid values
- - - - - - - - -
- enable - -
- boolean -
- - -
    Choices: -
  • no
  • -
  • yes
  • -
- - -
Enable nexthop tracking
- - - - - - - - -
- nopeerup_delay - -
- list - / elements=dictionary -
- - - - -
Set how long BGP will wait for the first peer to come up before beginning the update delay or graceful restart timers (in seconds)
-
This option is DEPRECATED and is replaced with nopeerup_delay_options which is of type dict, this attribute will be removed after 2024-06-01.
+
Nexthop tracking commands
@@ -2416,17 +1858,16 @@ Parameters
- cold_boot + route_map
- integer + string
-
How long to wait for the first peer to come up upon a cold boot
-
Please refer vendor documentation for valid values
+
Route map for valid nexthops
@@ -2435,26 +1876,26 @@ Parameters
- nsf_switchover + trigger
- integer + dictionary
-
How long to wait for the first peer, post NSF switchover
-
Please refer vendor documentation for valid values
+
nexthop trackings
- + - + +
- post_boot + delay
integer @@ -2463,7 +1904,7 @@ Parameters -
How long to wait for the first peer to come up once the system is already booted and all peers go down
+
Set the delay to trigger nexthop tracking
Please refer vendor documentation for valid values
@@ -2471,22 +1912,27 @@ Parameters - + +
- user_initiated + enable
- integer + boolean
+
    Choices: +
  • no
  • +
  • yes
  • +
-
How long to wait for the first peer, post a manual clear of BGP peers by the admin user
-
Please refer vendor documentation for valid values
+
Enable nexthop tracking
+ @@ -3683,100 +3129,6 @@ Parameters - - - -
- distribute_list - -
- dictionary -
- - - - -
Filter networks in routing updates
-
This option is DEPRECATED and is replaced with distributes which is of type list of dict, this attribute will be removed after 2024-06-01.
- - - - - - -
- acl - -
- string -
- - - - -
IP access list number/name
- - - - - - -
- in - -
- boolean -
- - -
    Choices: -
  • no
  • -
  • yes
  • -
- - -
Filter incoming routing updates
- - - - - - -
- interface - -
- string -
- - - - -
interface details
- - - - - - -
- out - -
- boolean -
- - -
    Choices: -
  • no
  • -
  • yes
  • -
- - -
Filter outgoing routing updates
- - - @@ -4164,24 +3516,6 @@ Parameters - - - - -
- address - -
- string -
- - - - -
Neighbor address (A.B.C.D)
-
This option is DEPRECATED and replaced with neighbor_address, this attribute will be removed after 2024-06-01.
- - @@ -5445,24 +4779,6 @@ Parameters
Inherit a peer-session template and Template name
- - - - -
- ipv6_adddress - -
- string -
- - - - -
Neighbor ipv6 address (X:X:X:X::X)
-
This option is DEPRECATED and replaced with neighbor_address, this attribute will be removed after 2024-06-01.
- - @@ -5909,24 +5225,6 @@ Parameters - - - - -
- password - -
- string -
- - - - -
Set a password
-
This option is DEPRECATED and is replaced with password_options which accepts dict as input, this attribute will be removed after 2024-06-01.
- - @@ -6211,167 +5509,83 @@ Parameters
- start - -
- integer -
- - - - -
path attribute range start value
-
Please refer vendor documentation for valid values
- - - - - - - - - -
- type - -
- integer -
- - - - -
path attribute type
-
Please refer vendor documentation for valid values
- - - - - - - - -
- peer_group - -
- string -
- - - - -
Member of the peer-group
- - - - - - -
- remote_as - -
- string -
- - - - -
Specify a BGP neighbor
-
AS of remote neighbor
- - - - - - -
- remove_private_as + start
- dictionary + integer
-
Remove private AS number from outbound updates
+
path attribute range start value
+
Please refer vendor documentation for valid values
- + + - + +
- all + type
- boolean + integer
-
    Choices: -
  • no
  • -
  • yes
  • -
-
Remove all private AS numbers
+
path attribute type
+
Please refer vendor documentation for valid values
+ + - - +
- replace_as + peer_group
- boolean + string
-
    Choices: -
  • no
  • -
  • yes
  • -
-
Replace all private AS numbers with local AS
+
Member of the peer-group
- - +
- set + remote_as
- boolean + string
-
    Choices: -
  • no
  • -
  • yes
  • -
-
Remove private AS number
+
Specify a BGP neighbor
+
AS of remote neighbor
-
- route_map + remove_private_as
dictionary @@ -6380,8 +5594,7 @@ Parameters -
Apply route map to neighbor
-
This option is DEPRECATED and is replaced with route_maps which accepts list of dict as input, this attribute will be removed after 2024-06-01.
+
Remove private AS number from outbound updates
@@ -6390,7 +5603,7 @@ Parameters
- in + all
boolean @@ -6403,7 +5616,7 @@ Parameters -
Apply map to incoming routes
+
Remove all private AS numbers
@@ -6412,13 +5625,17 @@ Parameters
- name + replace_as
- string + boolean
+
    Choices: +
  • no
  • +
  • yes
  • +
Replace all private AS numbers with local AS
@@ -6430,7 +5647,7 @@ Parameters
- out + set
boolean @@ -6443,7 +5660,7 @@ Parameters -
Apply map to outbound routes
+
Remove private AS number
@@ -7134,24 +6351,6 @@ Parameters
Allow inbound soft reconfiguration for this neighbor
- - - - -
- tag - -
- string -
- - - - -
Neighbor tag
-
This option is DEPRECATED and replaced with neighbor_address, this attribute will be removed after 2024-06-01.
- - @@ -9072,196 +8271,6 @@ Parameters - - - -
- route_server_context - -
- dictionary -
- - - - -
Enter route server context command mode
-
This option is DEPRECATED as it is out of scope of the module, this attribute will be removed after 2024-06-01.
- - - - - - -
- address_family - -
- dictionary -
- - - - -
Enter address family command mode
- - - - - - - -
- afi - -
- string -
- - -
    Choices: -
  • ipv4
  • -
  • ipv6
  • -
- - -
Address family
- - - - - - - -
- import_map - -
- string -
- - - - -
Import matching routes using a route map
-
Name of route map
- - - - - - - -
- modifier - -
- string -
- - -
    Choices: -
  • multicast
  • -
  • unicast
  • -
- - -
Address Family modifier
- - - - - - - -
- description - -
- string -
- - - - -
Textual description of the router server context
- - - - - - -
- name - -
- string -
- - - - -
Name of route server context
- - - - - - -
- scope - -
- dictionary -
- - - - -
Enter scope command mode
-
This option is DEPRECATED as is not valid within the scope of module, this attribute will be removed after 2024-06-01.
- - - - - - -
- global - -
- boolean -
- - -
    Choices: -
  • no
  • -
  • yes
  • -
- - -
Global scope
- - - - - - -
- vrf - -
- string -
- - - - -
VRF scope
-
VPN Routing/Forwarding instance name
- - - diff --git a/docs/cisco.ios.ios_facts_module.rst b/docs/cisco.ios.ios_facts_module.rst index 39b210f19..cc2035803 100644 --- a/docs/cisco.ios.ios_facts_module.rst +++ b/docs/cisco.ios.ios_facts_module.rst @@ -371,7 +371,7 @@ Common return values are documented `here when hardware is configured -
The available free memory on the remote device in Mb
+
The available free memory on the remote device in MiB

@@ -386,7 +386,7 @@ Common return values are documented `here when hardware is configured -
The total memory on the remote device in Mb
+
The total memory on the remote device in MiB

diff --git a/docs/cisco.ios.ios_linkagg_module.rst b/docs/cisco.ios.ios_linkagg_module.rst deleted file mode 100644 index 1a3c30505..000000000 --- a/docs/cisco.ios.ios_linkagg_module.rst +++ /dev/null @@ -1,327 +0,0 @@ -.. _cisco.ios.ios_linkagg_module: - - -********************* -cisco.ios.ios_linkagg -********************* - -**Module to configure link aggregation groups.** - - -Version added: 1.0.0 - -.. contents:: - :local: - :depth: 1 - -DEPRECATED ----------- -:Removed in collection release after 2024-06-01 -:Why: Updated modules released with more functionality. -:Alternative: ios_lag_interfaces - - - -Synopsis --------- -- This module provides declarative management of link aggregation groups on Cisco IOS network devices. - - - - -Parameters ----------- - -.. raw:: html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterChoices/DefaultsComments
-
- aggregate - -
- list - / elements=dictionary -
-
- -
List of link aggregation definitions.
-
-
- group - -
- string - / required -
-
- -
Channel-group number for the port-channel Link aggregation group. Range 1-255.
-
-
- members - -
- list - / elements=string -
-
- -
List of members of the link aggregation group.
-
-
- mode - -
- string -
-
-
    Choices: -
  • active
  • -
  • on
  • -
  • passive
  • -
  • auto
  • -
  • desirable
  • -
-
-
Mode of the link aggregation group.
-
On mode has to be quoted as 'on' or else pyyaml will convert to True before it gets to Ansible.
-
-
- state - -
- string -
-
-
    Choices: -
  • present
  • -
  • absent
  • -
-
-
State of the link aggregation group.
-
-
- group - -
- integer -
-
- -
Channel-group number for the port-channel Link aggregation group. Range 1-255.
-
-
- members - -
- list - / elements=string -
-
- -
List of members of the link aggregation group.
-
-
- mode - -
- string -
-
-
    Choices: -
  • active
  • -
  • on
  • -
  • passive
  • -
  • auto
  • -
  • desirable
  • -
-
-
Mode of the link aggregation group.
-
On mode has to be quoted as 'on' or else pyyaml will convert to True before it gets to Ansible.
-
-
- purge - -
- boolean -
-
-
    Choices: -
  • no ←
  • -
  • yes
  • -
-
-
Purge links not defined in the aggregate parameter.
-
-
- state - -
- string -
-
-
    Choices: -
  • present ←
  • -
  • absent
  • -
-
-
State of the link aggregation group.
-
-
- - -Notes ------ - -.. note:: - - Tested against IOS 15.2 - - This module works with connection ``network_cli``. See https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html - - For more information on using Ansible to manage network devices see the :ref:`Ansible Network Guide ` - - For more information on using Ansible to manage Cisco devices see the `Cisco integration page `_. - - - -Examples --------- - -.. code-block:: yaml - - - name: Create link aggregation group - cisco.ios.ios_linkagg: - group: 10 - state: present - - - name: Delete link aggregation group - cisco.ios.ios_linkagg: - group: 10 - state: absent - - - name: Set link aggregation group to members - cisco.ios.ios_linkagg: - group: 200 - mode: active - members: - - GigabitEthernet0/0 - - GigabitEthernet0/1 - - - name: Remove link aggregation group from GigabitEthernet0/0 - cisco.ios.ios_linkagg: - group: 200 - mode: active - members: - - GigabitEthernet0/1 - - - name: Create aggregate of linkagg definitions - cisco.ios.ios_linkagg: - aggregate: - - { group: 3, mode: "on", members: [GigabitEthernet0/1] } - - { group: 100, mode: passive, members: [GigabitEthernet0/2] } - - - -Return Values -------------- -Common return values are documented `here `_, the following are the fields unique to this module: - -.. raw:: html - - - - - - - - - - - - -
KeyReturnedDescription
-
- commands - -
- list -
-
always, except for the platforms that use Netconf transport to manage the device. -
The list of configuration mode commands to send to the device
-
-
Sample:
-
['interface port-channel 30', 'interface GigabitEthernet0/3', 'channel-group 30 mode on', 'no interface port-channel 30']
-
-

- - -Status ------- - - -- This module will be removed in a release after 2024-06-01. *[deprecated]* -- For more information see `DEPRECATED`_. - - -Authors -~~~~~~~ - -- Trishna Guha (@trishnaguha) diff --git a/docs/cisco.ios.ios_lldp_module.rst b/docs/cisco.ios.ios_lldp_module.rst deleted file mode 100644 index 5ccf0b4b3..000000000 --- a/docs/cisco.ios.ios_lldp_module.rst +++ /dev/null @@ -1,138 +0,0 @@ -.. _cisco.ios.ios_lldp_module: - - -****************** -cisco.ios.ios_lldp -****************** - -**(deprecated, removed after 2024-06-01) Manage LLDP configuration on Cisco IOS network devices.** - - -Version added: 1.0.0 - -.. contents:: - :local: - :depth: 1 - -DEPRECATED ----------- -:Removed in collection release after 2024-06-01 -:Why: Newer and updated modules released with more functionality. -:Alternative: ios_lldp_global - - - -Synopsis --------- -- This module provides declarative management of LLDP service on Cisco IOS network devices. - - - - -Parameters ----------- - -.. raw:: html - - - - - - - - - - - - -
ParameterChoices/DefaultsComments
-
- state - -
- string -
-
-
    Choices: -
  • present ←
  • -
  • absent
  • -
  • enabled
  • -
  • disabled
  • -
-
-
State of the LLDP configuration. If value is present lldp will be enabled else if it is absent it will be disabled.
-
-
- - -Notes ------ - -.. note:: - - Tested against IOS 15.2 - - This module works with connection ``network_cli``. See https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html - - For more information on using Ansible to manage network devices see the :ref:`Ansible Network Guide ` - - For more information on using Ansible to manage Cisco devices see the `Cisco integration page `_. - - - -Examples --------- - -.. code-block:: yaml - - - name: Enable LLDP service - cisco.ios.ios_lldp: - state: present - - - name: Disable LLDP service - cisco.ios.ios_lldp: - state: absent - - - -Return Values -------------- -Common return values are documented `here `_, the following are the fields unique to this module: - -.. raw:: html - - - - - - - - - - - - -
KeyReturnedDescription
-
- commands - -
- list -
-
always, except for the platforms that use Netconf transport to manage the device. -
The list of configuration mode commands to send to the device
-
-
Sample:
-
['lldp run']
-
-

- - -Status ------- - - -- This module will be removed in a release after 2024-06-01. *[deprecated]* -- For more information see `DEPRECATED`_. - - -Authors -~~~~~~~ - -- Ganesh Nalawade (@ganeshrn) diff --git a/docs/cisco.ios.ios_logging_global_module.rst b/docs/cisco.ios.ios_logging_global_module.rst index 504441719..979918e67 100644 --- a/docs/cisco.ios.ios_logging_global_module.rst +++ b/docs/cisco.ios.ios_logging_global_module.rst @@ -127,6 +127,7 @@ Parameters
Logging severity level
+
Idempotency is not maintained for this attribute for the value 'debugging' since this value is not shown in running-config.
@@ -633,6 +634,7 @@ Parameters
Logging severity level
+
Idempotency is not maintained for this attribute for the value 'debugging' since this value is not shown in running-config.
@@ -1461,6 +1463,7 @@ Parameters
Logging severity level
+
Idempotency is not maintained for this attribute for the value 'debugging' since this value is not shown in running-config.
@@ -1970,6 +1973,7 @@ Parameters
Logging severity level
+
Idempotency is not maintained for this attribute for the value 'debugging' since this value is not shown in running-config.
diff --git a/docs/cisco.ios.ios_vlans_module.rst b/docs/cisco.ios.ios_vlans_module.rst index 78fa5ec97..1ed4c1d23 100644 --- a/docs/cisco.ios.ios_vlans_module.rst +++ b/docs/cisco.ios.ios_vlans_module.rst @@ -229,7 +229,10 @@ Parameters -
Shutdown VLAN switching.
+
Specifies whether VLAN switching should be administratively enabled or disabled.
+
When set to enabled, the VLAN interface is administratively shut down, which prevents it from forwarding traffic.
+
When set to disabled, the VLAN interface is administratively enabled by issuing the internal no shutdown command, allowing it to forward traffic.
+
The operational state of the VLAN depends on both the administrative state (shutdown or no shutdown) and the physical link status.
@@ -448,100 +451,330 @@ Examples # Before state: # ------------- # - # vios_l2#show vlan + # S1#show vlan + # VLAN Name Status Ports # ---- -------------------------------- --------- ------------------------------- - # 1 default active Gi0/1, Gi0/2 - # 10 vlan_10 active - # 20 vlan_20 act/lshut - # 30 vlan_30 sus/lshut + # 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 + # 10 Vlan_10 active + # 20 Vlan_20 active + # 30 Vlan_30 suspended + # 44 Vlan_44 suspended # 1002 fddi-default act/unsup # 1003 token-ring-default act/unsup # 1004 fddinet-default act/unsup # 1005 trnet-default act/unsup - # + # VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 # ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ # 1 enet 100001 1500 - - - - - 0 0 # 10 enet 100010 1500 - - - - - 0 0 # 20 enet 100020 610 - - - - - 0 0 # 30 enet 100030 1500 - - - - - 0 0 + # 44 enet 100044 1500 - - - - - 0 0 # 1002 fddi 101002 1500 - - - - - 0 0 # 1003 tr 101003 1500 - - - - - 0 0 # 1004 fdnet 101004 1500 - - - ieee - 0 0 # 1005 trnet 101005 1500 - - - ibm - 0 0 - # + # Remote SPAN VLANs # ------------------------------------------------------------------------------ - # 10 + + + # Primary Secondary Type Ports + # ------- --------- ----------------- ------------------------------------------ + # S1# + - name: Override device configuration of all VLANs with provided configuration cisco.ios.ios_vlans: config: - - name: Vlan_10 - vlan_id: 10 - mtu: 1000 + - name: Vlan_2020 + state: active + vlan_id: 20 + shutdown: disabled state: overridden + # Task output: + # ------------ + + # after: + # - mtu: 1500 + # name: default + # shutdown: disabled + # state: active + # vlan_id: 1 + # - mtu: 1500 + # name: Vlan_2020 + # shutdown: disabled + # state: active + # vlan_id: 20 + # - mtu: 1500 + # name: fddi-default + # shutdown: enabled + # state: active + # vlan_id: 1002 + # - mtu: 1500 + # name: token-ring-default + # shutdown: enabled + # state: active + # vlan_id: 1003 + # - mtu: 1500 + # name: fddinet-default + # shutdown: enabled + # state: active + # vlan_id: 1004 + # - mtu: 1500 + # name: trnet-default + # shutdown: enabled + # state: active + # vlan_id: 1005 + + # before: + # - mtu: 1500 + # name: default + # shutdown: disabled + # state: active + # vlan_id: 1 + # - mtu: 1500 + # name: Vlan_10 + # shutdown: disabled + # state: active + # vlan_id: 10 + # - mtu: 610 + # name: Vlan_20 + # shutdown: disabled + # state: active + # vlan_id: 20 + # - mtu: 1500 + # name: Vlan_30 + # shutdown: disabled + # state: suspend + # vlan_id: 30 + # - mtu: 1500 + # name: Vlan_44 + # shutdown: disabled + # state: suspend + # vlan_id: 44 + # - mtu: 1500 + # name: fddi-default + # shutdown: enabled + # state: active + # vlan_id: 1002 + # - mtu: 1500 + # name: token-ring-default + # shutdown: enabled + # state: active + # vlan_id: 1003 + # - mtu: 1500 + # name: fddinet-default + # shutdown: enabled + # state: active + # vlan_id: 1004 + # - mtu: 1500 + # name: trnet-default + # shutdown: enabled + # state: active + # vlan_id: 1005 + + # commands: + # - no vlan 1 + # - no vlan 10 + # - no vlan 30 + # - no vlan 44 + # - no vlan 1002 + # - no vlan 1003 + # - no vlan 1004 + # - no vlan 1005 + # - vlan 20 + # - name Vlan_2020 + # - no mtu 610 + # - no vlan configuration 1 + # - no vlan configuration 10 + # - no vlan configuration 30 + # - no vlan configuration 44 + # - no vlan configuration 1002 + # - no vlan configuration 1003 + # - no vlan configuration 1004 + # - no vlan configuration 1005 + # After state: # ------------ # # vios_l2#show vlan # VLAN Name Status Ports # ---- -------------------------------- --------- ------------------------------- - # 1 default active Gi0/1, Gi0/2 - # 10 Vlan_10 active + # 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 + # 20 Vlan_2020 active # 1002 fddi-default act/unsup # 1003 token-ring-default act/unsup # 1004 fddinet-default act/unsup # 1005 trnet-default act/unsup - # + # VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 # ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ # 1 enet 100001 1500 - - - - - 0 0 - # 10 enet 100010 1000 - - - - - 0 0 + # 20 enet 100020 1500 - - - - - 0 0 # 1002 fddi 101002 1500 - - - - - 0 0 # 1003 tr 101003 1500 - - - - - 0 0 # 1004 fdnet 101004 1500 - - - ieee - 0 0 # 1005 trnet 101005 1500 - - - ibm - 0 0 + # Remote SPAN VLANs + # ------------------------------------------------------------------------------ - # Using overridden + + # Primary Secondary Type Ports + # ------- --------- ----------------- ------------------------------------------ + + # Using purged # Before state: # ------------- # - # Leaf-01#show run nve | sec ^vlan configuration - # vlan configuration 101 - # member evpn-instance 101 vni 10101 - # vlan configuration 102 - # member evpn-instance 102 vni 10102 - # vlan configuration 201 - # member evpn-instance 201 vni 10201 - # vlan configuration 901 - # member vni 50901 + # S1#show vlan - - name: Override device configuration of all VLANs with provided configuration + # VLAN Name Status Ports + # ---- -------------------------------- --------- ------------------------------- + # 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 + # 20 Vlan_2020 active + # 1002 fddi-default act/unsup + # 1003 token-ring-default act/unsup + # 1004 fddinet-default act/unsup + # 1005 trnet-default act/unsup + + # VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 + # ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ + # 1 enet 100001 1500 - - - - - 0 0 + # 20 enet 100020 1500 - - - - - 0 0 + # 1002 fddi 101002 1500 - - - - - 0 0 + # 1003 tr 101003 1500 - - - - - 0 0 + # 1004 fdnet 101004 1500 - - - ieee - 0 0 + # 1005 trnet 101005 1500 - - - ibm - 0 0 + + # Remote SPAN VLANs + # ------------------------------------------------------------------------------ + + + # Primary Secondary Type Ports + # ------- --------- ----------------- ------------------------------------------ + + # S1#show running-config | section ^vlan configuration .+ + # vlan configuration 20 + + + - name: Purge all vlans configuration cisco.ios.ios_vlans: config: - - vlan_id: 101 - member: - vni: 10102 - evi: 102 - - vlan_id: 102 - member: - vni: 10101 - evi: 101 - state: overridden + state: purged + + # Task output: + # ------------ + + # after: + # - mtu: 1500 + # name: default + # shutdown: disabled + # state: active + # vlan_id: 1 + # - mtu: 1500 + # name: fddi-default + # shutdown: enabled + # state: active + # vlan_id: 1002 + # - mtu: 1500 + # name: token-ring-default + # shutdown: enabled + # state: active + # vlan_id: 1003 + # - mtu: 1500 + # name: fddinet-default + # shutdown: enabled + # state: active + # vlan_id: 1004 + # - mtu: 1500 + # name: trnet-default + # shutdown: enabled + # state: active + # vlan_id: 1005 + + # before: + # - mtu: 1500 + # name: default + # shutdown: disabled + # state: active + # vlan_id: 1 + # - mtu: 1500 + # name: Vlan_2020 + # shutdown: disabled + # state: active + # vlan_id: 20 + # - mtu: 1500 + # name: fddi-default + # shutdown: enabled + # state: active + # vlan_id: 1002 + # - mtu: 1500 + # name: token-ring-default + # shutdown: enabled + # state: active + # vlan_id: 1003 + # - mtu: 1500 + # name: fddinet-default + # shutdown: enabled + # state: active + # vlan_id: 1004 + # - mtu: 1500 + # name: trnet-default + # shutdown: enabled + # state: active + # vlan_id: 1005 + + # commands: + # - no vlan 1 + # - no vlan 20 + # - no vlan 1002 + # - no vlan 1003 + # - no vlan 1004 + # - no vlan 1005 + # - no vlan configuration 1 + # - no vlan configuration 20 + # - no vlan configuration 1002 + # - no vlan configuration 1003 + # - no vlan configuration 1004 + # - no vlan configuration 1005 # After state: # ------------ # - # Leaf-01#show run nve | sec ^vlan configuration - # vlan configuration 101 - # member evpn-instance 102 vni 10102 - # vlan configuration 102 - # member evpn-instance 101 vni 10101 + # S1#show vlan + + # VLAN Name Status Ports + # ---- -------------------------------- --------- ------------------------------- + # 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 + # 1002 fddi-default act/unsup + # 1003 token-ring-default act/unsup + # 1004 fddinet-default act/unsup + # 1005 trnet-default act/unsup + + # VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 + # ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ + # 1 enet 100001 1500 - - - - - 0 0 + # 1002 fddi 101002 1500 - - - - - 0 0 + # 1003 tr 101003 1500 - - - - - 0 0 + # 1004 fdnet 101004 1500 - - - ieee - 0 0 + # 1005 trnet 101005 1500 - - - ibm - 0 0 + + # Remote SPAN VLANs + # ------------------------------------------------------------------------------ + + + # Primary Secondary Type Ports + # ------- --------- ----------------- ------------------------------------------ + + # S1#show running-config | section ^vlan configuration .+ + # S1# + # Using replaced diff --git a/docs/cisco.ios.ios_vrf_global_module.rst b/docs/cisco.ios.ios_vrf_global_module.rst new file mode 100644 index 000000000..eb92b8d70 --- /dev/null +++ b/docs/cisco.ios.ios_vrf_global_module.rst @@ -0,0 +1,1253 @@ +.. _cisco.ios.ios_vrf_global_module: + + +************************ +cisco.ios.ios_vrf_global +************************ + +**Resource module to configure global VRF definitions.** + + +Version added: 8.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- This module provides declarative management of VRF definitions on Cisco IOS. + + + + +Parameters +---------- + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterChoices/DefaultsComments
+
+ config + +
+ dictionary +
+
+ +
A dictionary containing device configurations for VRF, including a list of VRF definitions.
+
+
+ vrfs + +
+ list + / elements=dictionary +
+
+ +
List of VRF definitions.
+
+
+ description + +
+ string +
+
+ +
VRF specific description
+
+
+ ipv4 + +
+ dictionary +
+
+ +
VRF IPv4 configuration
+
+
+ multicast + +
+ dictionary +
+
+ +
IPv4 multicast configuration
+
+
+ multitopology + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Enable multicast-specific topology
+
+
+ ipv6 + +
+ dictionary +
+
+ +
VRF IPv6 configuration
+
+
+ multicast + +
+ dictionary +
+
+ +
IPv6 multicast configuration
+
+
+ multitopology + +
+ boolean +
+
+
    Choices: +
  • no
  • +
  • yes
  • +
+
+
Enable multicast-specific topology
+
+
+ name + +
+ string + / required +
+
+ +
Name of the VRF.
+
+
+ rd + +
+ string +
+
+ +
Specify route distinguisher (RD).
+
+
+ route_target + +
+ dictionary +
+
+ +
Specify target VPN extended configurations.
+
+
+ both + +
+ string +
+
+ +
Both export and import target-VPN configuration.
+
+
+ export + +
+ string +
+
+ +
Export target-VPN configuration.
+
+
+ import_config + +
+ string +
+
+ +
Import target-VPN configuration.
+
+
+ vnet + +
+ dictionary +
+
+ +
Virtual networking configuration.
+
+
+ tag + +
+ integer +
+
+ +
Identifier used to tag packets associated with this VNET.
+
+
+ vpn + +
+ dictionary +
+
+ +
Configure vpn-id for the VRF as specified in RFC 2685.
+
+
+ id + +
+ string +
+
+ +
Configure vpn-id in RFC 2685 format.
+
+
+ running_config + +
+ string +
+
+ +
This option is used only with state parsed.
+
The value of this option should be the output received from the IOS device by executing the command show running-config | section ^vrf.
+
The state parsed reads the configuration from running_config option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the parsed key within the result.
+
+
+ state + +
+ string +
+
+
    Choices: +
  • parsed
  • +
  • gathered
  • +
  • deleted
  • +
  • merged ←
  • +
  • replaced
  • +
  • rendered
  • +
  • overridden
  • +
  • purged
  • +
+
+
The state the configuration should be left in
+
The states rendered, gathered and parsed does not perform any change on the device.
+
The state rendered will transform the configuration in config option to platform specific CLI commands which will be returned in the rendered key within the result. For state rendered active connection to remote host is not required.
+
The state gathered will fetch the running configuration from device and transform it into structured data in the format as per the resource module argspec and the value is returned in the gathered key within the result.
+
The state parsed reads the configuration from running_config option and transforms it into JSON format as per the resource module parameters and the value is returned in the parsed key within the result. The value of running_config option should be the same format as the output of command show running-config | section vrf. connection to remote host is not required.
+
The state deleted only removes the VRF attributes that this module manages and does not negate the VRF completely. Thereby, preserving address-family related configurations under VRF context.
+
The state purged removes all the VRF definitions from the target device. Use caution with this state.
+
Refer to examples for more details.
+
+
+ + +Notes +----- + +.. note:: + - Tested against Cisco IOS-XE version 17.3 on CML. + - This module works with connection ``network_cli``. See https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html + - The module examples uses callback plugin (stdout_callback = yaml) to generate task output in yaml format. + + + +Examples +-------- + +.. code-block:: yaml + + # Using merged + + # Before state: + # ------------- + # + # admin#show running-config | section ^vrf + + - name: Merge provided configuration with device configuration + cisco.ios.ios_vrf_global: + config: + vrfs: + - name: VRF2 + description: This is a test VRF for merged state + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "2:3" + route_target: + export: "192.0.2.0:100" + import_config: "192.0.2.3:200" + vpn: + id: "2:45" + vnet: + tag: 200 + state: merged + + # Task output + # ------------- + # + # before: {} + # + # commands: + # - vrf definition VRF2 + # - description This is a test VRF for merged state + # - ipv4 multicast multitopology + # - ipv6 multicast multitopology + # - rd 2:3 + # - route-target export 192.0.2.0:100 + # - route-target import 192.0.2.3:200 + # - vnet tag 200 + # - vpn id 2:45 + # + # after: + # - name: VRF2 + # description: This is a test VRF for merged state + # ipv4: + # multicast: + # multitopology: true + # ipv6: + # multicast: + # multitopology: true + # rd: "2:3" + # route_target: + # export: "192.0.2.0:100" + # import_config: "192.0.2.3:200" + # vnet: + # tag: 200 + # vpn: + # id: "2:45" + + # After state: + # ------------- + # + # admin#show running-config | section ^vrf + # vrf definition VRF2 + # vnet tag 200 + # description This is a test VRF for merged state + # ipv4 multicast multitopology + # ipv6 multicast multitopology + # rd 2:3 + # vpn id 2:45 + # route-target export 192.0.2.0:100 + # route-target import 192.0.2.3:200 + + # Using replaced + + # Before state: + # ------------- + # + # admin#show running-config | section ^vrf + # vrf definition VRF2 + # vnet tag 200 + # description This is a test VRF for merged state + # ipv4 multicast multitopology + # ipv6 multicast multitopology + # rd 2:3 + # vpn id 2:45 + # route-target export 192.0.2.0:100 + # route-target import 192.0.2.3:200 + + - name: Replace the provided configuration with the existing running configuration + cisco.ios.ios_vrf_global: + config: + vrfs: + - name: VRF7 + description: VRF7 description + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "7:8" + route_target: + export: "198.51.100.112:500" + import_config: "192.0.2.4:400" + vpn: + id: "5:45" + vnet: + tag: 300 + state: replaced + + # Task Output: + # ------------ + # + # before: + # - name: VRF2 + # description: This is a test VRF for merged state + # ipv4: + # multicast: + # multitopology: true + # ipv6: + # multicast: + # multitopology: true + # rd: "2:3" + # route_target: + # export: "192.0.2.0:100" + # import_config: "192.0.2.3:200" + # vnet: + # tag: 200 + # vpn: + # id: "2:45" + # + # commands: + # - vrf definition VRF7 + # - description VRF7 description + # - ipv4 multicast multitopology + # - ipv6 multicast multitopology + # - rd 7:8 + # - route-target export 198.51.100.112:500 + # - route-target import 192.0.2.4:400 + # - vnet tag 300 + # - vpn id 5:45 + # + # after: + # - name: VRF2 + # description: This is a test VRF for merged state + # ipv4: + # multicast: + # multitopology: true + # ipv6: + # multicast: + # multitopology: true + # rd: "2:3" + # route_target: + # export: "192.0.2.0:100" + # import_config: "192.0.2.3:200" + # vnet: + # tag: 200 + # vpn: + # id: "2:45 + # - name: VRF7 + # description: VRF7 description + # ipv4: + # multicast: + # multitopology: true + # ipv6: + # multicast: + # multitopology: true + # rd: "7:8" + # route_target: + # export: "198.51.100.112:500" + # import_config: "192.0.2.4:400" + # vnet: + # tag: 300 + # vpn: + # id: "5:45" + # + # After state: + # ------------- + # + # admin#show running-config | section ^vrf + # vrf definition VRF2 + # vnet tag 200 + # description This is a test VRF for merged state + # ipv4 multicast multitopology + # ipv6 multicast multitopology + # rd 2:3 + # vpn id 2:45 + # route-target export 192.0.2.0:100 + # route-target import 192.0.2.3:200 + # vrf definition VRF7 + # vnet tag 300 + # description VRF7 description + # ipv4 multicast multitopology + # ipv6 multicast multitopology + # rd 7:8 + # route-target export 198.51.100.112:500 + # route-target import 192.0.2.4:400 + # vpn id 5:45 + + # Using Overridden + + # Before state: + # ------------- + # + # admin#show running-config | section ^vrf + # vrf definition VRF2 + # vnet tag 200 + # description This is a test VRF for merged state + # ipv4 multicast multitopology + # ipv6 multicast multitopology + # rd 2:3 + # vpn id 2:45 + # route-target export 192.0.2.0:100 + # route-target import 192.0.2.3:200 + # vrf definition VRF7 + # vnet tag 300 + # description VRF7 description + # ipv4 multicast multitopology + # ipv6 multicast multitopology + # rd 7:8 + # route-target export 198.51.100.112:500 + # route-target import 192.0.2.4:400 + # vpn id 5:45 + + - name: Override the provided configuration with the existing running configuration + cisco.ios.ios_vrf_global: + config: + vrfs: + - name: VRF6 + description: VRF6 description + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "6:7" + route_target: + export: "198.51.0.2:400" + import_config: "198.51.0.5:200" + vpn: + id: "4:5" + vnet: + tag: 500 + state: overridden + + # Task Output: + # ------------ + # + # before: + # - name: VRF2 + # description: This is a test VRF for merged state + # ipv4: + # multicast: + # multitopology: true + # ipv6: + # multicast: + # multitopology: true + # rd: "2:3" + # route_target: + # export: "192.0.2.0:100" + # import_config: "192.0.2.3:200" + # vnet: + # tag: 200 + # vpn: + # id: "2:45 + # - name: VRF7 + # description: VRF7 description + # ipv4: + # multicast: + # multitopology: true + # ipv6: + # multicast: + # multitopology: true + # rd: "7:8" + # route_target: + # export: "198.51.100.112:500" + # import_config: "192.0.2.4:400" + # vnet: + # tag: 300 + # vpn: + # id: "5:45" + # + # commands: + # - vrf definition VRF2 + # - no description This is a test VRF for merged state + # - no ipv4 multicast multitopology + # - no ipv6 multicast multitopology + # - no rd 2:3 + # - no route-target export 192.0.2.0:100 + # - no route-target import 192.0.2.3:200 + # - no vnet tag 200 + # - no vpn id 2:45 + # - vrf definition VRF7 + # - no description VRF7 description + # - no ipv4 multicast multitopology + # - no ipv6 multicast multitopology + # - no rd 7:8 + # - no route-target export 198.51.100.112:500 + # - no route-target import 192.0.2.4:400 + # - no vnet tag 300 + # - no vpn id 5:45 + # - vrf definition VRF6 + # - description VRF6 description + # - ipv4 multicast multitopology + # - ipv6 multicast multitopology + # - rd 6:7 + # - route-target export 198.51.0.2:400 + # - route-target import 198.51.0.5:200 + # - vnet tag 500 + # - vpn id 4:5 + # + # after: + # - name: VRF2 + # - name: VRF6 + # description: VRF6 description + # ipv4: + # multicast: + # multitopology: true + # ipv6: + # multicast: + # multitopology: true + # rd: "6:7" + # route_target: + # export: "198.51.0.2:400" + # import_config: "198.51.0.5:200" + # vnet: + # tag: 500 + # vpn: + # id: "4:5 + # - name: VRF7 + + # After state: + # ------------ + # + # admin#show running-config | section ^vrf + # vrf definition VRF2 + # vrf definition VRF6 + # vnet tag 500 + # description VRF6 description + # ipv4 multicast multitopology + # ipv6 multicast multitopology + # rd 6:7 + # vpn id 4:5 + # route-target export 198.51.0.2:400 + # route-target import 198.51.0.5:200 + # vrf definition VRF7 + + # Using Deleted + + # Before state: + # ------------- + # + # admin#show running-config | section ^vrf + # vrf definition VRF2 + # vrf definition VRF6 + # vnet tag 500 + # description VRF6 description + # ipv4 multicast multitopology + # ipv6 multicast multitopology + # rd 6:7 + # vpn id 4:5 + # route-target export 198.51.0.2:400 + # route-target import 198.51.0.5:200 + # vrf definition VRF7 + + - name: Delete the provided configuration when config is given + cisco.ios.ios_vrf_global: + config: + vrfs: + - name: VRF2 + - name: VRF6 + - name: VRF7 + state: deleted + + # Task Output: + # ------------ + # + # before: + # - name: VRF2 + # - name: VRF6 + # description: VRF6 description + # ipv4: + # multicast: + # multitopology: true + # ipv6: + # multicast: + # multitopology: true + # rd: "6:7" + # route_target: + # export: "198.51.0.2:400" + # import_config: "198.51.0.5:200" + # vnet: + # tag: 500 + # vpn: + # id: "4:5" + # - name: VRF7 + # + # commands: + # - vrf definition VRF2 + # - vrf definition VRF6 + # - no description VRF6 description + # - no ipv4 multicast multitopology + # - no ipv6 multicast multitopology + # - no rd 6:7 + # - no route-target export 198.51.0.2:400 + # - no route-target import 198.51.0.5:200 + # - no vnet tag 500 + # - no vpn id 4:5 + # - vrf definition VRF7 + # + # after: + # - name: VRF2 + # - name: VRF6 + # - name: VRF7 + + # After state: + # ------------- + # admin#show running-config | section ^vrf + # vrf definition VRF2 + # vrf definition VRF6 + # vrf definition VRF7 + + # Using Deleted with empty config + + # Before state: + # ------------- + # + # admin#show running-config | section ^vrf + # vrf definition VRF2 + # vrf definition VRF6 + # vnet tag 500 + # description VRF6 description + # ipv4 multicast multitopology + # ipv6 multicast multitopology + # rd 6:7 + # vpn id 4:5 + # route-target export 198.51.0.2:400 + # route-target import 198.51.0.5:200 + # vrf definition VRF7 + + - name: Delete the provided configuration when config is empty + cisco.ios.ios_vrf_global: + config: + state: deleted + + # Task Output: + # ------------ + # + # before: + # - name: VRF2 + # - name: VRF6 + # description: VRF6 description + # ipv4: + # multicast: + # multitopology: true + # ipv6: + # multicast: + # multitopology: true + # rd: "6:7" + # route_target: + # export: "198.51.0.2:400" + # import_config: "198.51.0.5:200" + # vnet: + # tag: 500 + # vpn: + # id: "4:5" + # - name: VRF7 + + # commands: + # - vrf definition VRF2 + # - vrf definition VRF6 + # - no description VRF6 description + # - no ipv4 multicast multitopology + # - no ipv6 multicast multitopology + # - no rd 6:7 + # - no route-target export 198.51.0.2:400 + # - no route-target import 198.51.0.5:200 + # - no vnet tag 500 + # - no vpn id 4:5 + # - vrf definition VRF7 + # + # after: + # - name: VRF2 + # - name: VRF6 + # - name: VRF7 + + # After state: + # ------------- + # + # admin#show running-config | section ^vrf + # vrf definition VRF2 + # vrf definition VRF6 + # vrf definition VRF7 + + # Using purged - would delete all the VRF definitions + + # Before state: + # ------------- + # + # admin#show running-config | section ^vrf + # vrf definition VRF2 + # vrf definition VRF6 + # vrf definition VRF7 + + - name: Purge all the configuration from the device + cisco.ios.ios_vrf_global: + state: purged + + # Task Output: + # ------------ + # + # before: + # - name: VRF2 + # - name: VRF6 + # - name: VRF7 + # commands: + # - no vrf definition VRF2 + # - no vrf definition VRF6 + # - no vrf definition VRF7 + # after: {} + + # After state: + # ------------- + # + # admin#show running-config | section ^vrf + + # Using Rendered + + - name: Render provided configuration with device configuration + cisco.ios.ios_vrf_global: + config: + vrfs: + - name: VRF2 + description: This is a test VRF for merged state + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "2:3" + route_target: + export: "192.0.2.0:100" + import_config: "192.0.2.3:200" + vpn: + id: "2:45" + vnet: + tag: 200 + state: rendered + + # Task Output: + # ------------ + # + # rendered: + # - vrf definition VRF2 + # - description This is a test VRF for merged state + # - ipv4 multicast multitopology + # - ipv6 multicast multitopology + # - rd 2:3 + # - route-target export 192.0.2.0:100 + # - route-target import 192.0.2.3:200 + # - vnet tag 200 + # - vpn id 2:45 + + # Using Gathered + + # Before state: + # ------------- + # + # admin#show running-config | section ^vrf + # vrf definition VRF2 + # vnet tag 200 + # description This is a test VRF for merged state + # ipv4 multicast multitopology + # ipv6 multicast multitopology + # rd 2:3 + # vpn id 2:45 + # route-target export 192.0.2.0:100 + # route-target import 192.0.2.3:200 + + - name: Gather existing running configuration + cisco.ios.ios_vrf_global: + config: + state: gathered + + # Task Output: + # ------------ + # + # gathered: + # vrfs: + # - name: VRF2 + # description: This is a test VRF for merged state + # ipv4: + # multicast: + # multitopology: true + # ipv6: + # multicast: + # multitopology: true + # rd: "2:3" + # route_target: + # export: "192.0.2.0:100" + # import_config: "192.0.2.3:200" + # vnet: + # tag: 200 + # vpn: + # id: "2:45" + + # Using parsed + + # File: parsed.cfg + # ---------------- + # + # vrf definition test + # vnet tag 34 + # description This is test VRF + # ipv4 multicast multitopology + # ipv6 multicast multitopology + # rd 192.0.2.0:300 + # vpn id 3:4 + # route-target export 192.0.2.0:100 + # route-target import 192.0.2.2:300 + # vrf definition test2 + # vnet tag 35 + # description This is test VRF + # ipv4 multicast multitopology + # ipv6 multicast multitopology + # rd 192.0.2.3:300 + + - name: Parse the provided configuration + cisco.ios.ios_vrf_global: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + + # Task Output: + # ------------ + # + # parsed: + # vrfs: + # - name: test + # description: This is test VRF + # ipv4: + # multicast: + # multitopology: true + # ipv6: + # multicast: + # multitopology: true + # rd: "192.0.2.0:300" + # route_target: + # export: "192.0.2.0:100" + # import_config: "192.0.2.2:300" + # vnet: + # tag: 34 + # vpn: + # id: "3:4" + # - name: test2 + # description: This is test VRF + # ipv4: + # multicast: + # multitopology: true + # ipv6: + # multicast: + # multitopology: true + # rd: "192.0.2.3:300" + # vnet: + # tag: 35 + + + +Return Values +------------- +Common return values are documented `here `_, the following are the fields unique to this module: + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyReturnedDescription
+
+ after + +
+ dictionary +
+
when changed +
The resulting configuration after module execution.
+
+
Sample:
+
This output will always be in the same format as the module argspec.
+
+
+ before + +
+ dictionary +
+
when state is merged, replaced, overridden, deleted or purged +
The configuration prior to the module execution.
+
+
Sample:
+
This output will always be in the same format as the module argspec.
+
+
+ commands + +
+ list +
+
when state is merged, replaced, overridden, deleted or purged +
The set of commands pushed to the remote device.
+
+
Sample:
+
['vrf definition test', 'description This is a test VRF', 'rd: 2:3']
+
+
+ gathered + +
+ list +
+
when state is gathered +
Facts about the network resource gathered from the remote device as structured data.
+
+
Sample:
+
This output will always be in the same format as the module argspec.
+
+
+ parsed + +
+ list +
+
when state is parsed +
The device native config provided in running_config option parsed into structured data as per module argspec.
+
+
Sample:
+
This output will always be in the same format as the module argspec.
+
+
+ rendered + +
+ list +
+
when state is rendered +
The provided configuration in the task rendered in device-native format (offline).
+
+
Sample:
+
['vrf definition management', 'description This is a test VRF', 'rd: 2:3', 'route-target export 190.0.2.3:400', 'route-target import 190.0.2.1:300']
+
+

+ + +Status +------ + + +Authors +~~~~~~~ + +- Ruchi Pakhle (@Ruchip16) diff --git a/docs/cisco.ios.ios_vrf_interfaces_module.rst b/docs/cisco.ios.ios_vrf_interfaces_module.rst new file mode 100644 index 000000000..2b3a67af4 --- /dev/null +++ b/docs/cisco.ios.ios_vrf_interfaces_module.rst @@ -0,0 +1,738 @@ +.. _cisco.ios.ios_vrf_interfaces_module: + + +**************************** +cisco.ios.ios_vrf_interfaces +**************************** + +**Manages VRF configuration on interfaces.** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Manages Virtual Routing and Forwarding (VRF) configuration on interfaces of Cisco IOS devices. + + + + +Parameters +---------- + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterChoices/DefaultsComments
+
+ config + +
+ list + / elements=dictionary +
+
+ +
A list of interface VRF configurations.
+
+
+ name + +
+ string + / required +
+
+ +
Full name of the interface to be configured.
+
Example - GigabitEthernet1, Loopback24
+
+
+ vrf_name + +
+ string +
+
+ +
Name of the VRF to be configured on the interface.
+
When configured, applies 'vrf forwarding <vrf_name>' under the interface.
+
+
+ running_config + +
+ string +
+
+ +
This option is used only with state parsed.
+
The value of this option should be the output received from the IOS device by executing the command show running-config | section ^interface.
+
The state parsed reads the configuration from running_config option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the parsed key within the result.
+
+
+ state + +
+ string +
+
+
    Choices: +
  • merged ←
  • +
  • replaced
  • +
  • overridden
  • +
  • deleted
  • +
  • gathered
  • +
  • rendered
  • +
  • parsed
  • +
+
+
The state the configuration should be left in.
+
+
+ + +Notes +----- + +.. note:: + - Tested against Cisco IOS XE Version 17.13.01a + - VRF must exist before assigning to an interface + - When removing VRF from interface, associated IP addresses will be removed + - For more information on using Ansible to manage network devices see the :ref:`Ansible Network Guide ` + - For more information on using Ansible to manage Cisco devices see the `Cisco integration page `_. + + + +Examples +-------- + +.. code-block:: yaml + + # Using merged + + # Before state: + # ------------- + # + # vios#show running-config | section ^interface + # interface Loopback24 + # no ip address + # interface GigabitEthernet1 + # ip address dhcp + # negotiation auto + # interface GigabitEthernet2 + # no ip address + # shutdown + # negotiation auto + # interface GigabitEthernet3 + # no ip address + # negotiation auto + # interface GigabitEthernet4 + # no ip address + # shutdown + # negotiation auto + + - name: Merge provided configuration with device configuration + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_D + - name: GigabitEthernet3 + - name: GigabitEthernet4 + state: merged + + # Task Output: + # ------------ + # + # before: + # - name: "Loopback24" + # - name: "GigabitEthernet1" + # - name: "GigabitEthernet2" + # - name: "GigabitEthernet3" + # - name: "GigabitEthernet4" + # + # commands: + # - interface GigabitEthernet2 + # - vrf forwarding vrf_D + # + # after: + # - name: "Loopback24" + # - name: "GigabitEthernet1" + # - name: "GigabitEthernet2" + # vrf_name: "vrf_D" + # - name: "GigabitEthernet3" + # - name: "GigabitEthernet4" + + # After state: + # ------------ + # + # vios#show running-config | section ^interface + # interface Loopback24 + # no ip address + # interface GigabitEthernet1 + # ip address dhcp + # negotiation auto + # interface GigabitEthernet2 + # vrf forwarding vrf_D + # no ip address + # shutdown + # negotiation auto + # interface GigabitEthernet3 + # no ip address + # negotiation auto + # interface GigabitEthernet4 + # no ip address + # shutdown + # negotiation auto + + # Using overridden + + # Before state: + # ------------- + # + # vios#show running-config | section ^interface + # interface Loopback24 + # no ip address + # interface GigabitEthernet1 + # ip address dhcp + # negotiation auto + # interface GigabitEthernet2 + # vrf forwarding vrf_B + # no ip address + # shutdown + # negotiation auto + # interface GigabitEthernet3 + # no ip address + # negotiation auto + # interface GigabitEthernet4 + # no ip address + # shutdown + # negotiation auto + + - name: Override device configuration with provided configuration + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + - name: GigabitEthernet3 + - name: GigabitEthernet4 + state: overridden + + # Task Output: + # ------------ + # + # before: + # - name: "Loopback24" + # - name: "GigabitEthernet1" + # - name: "GigabitEthernet2" + # vrf_name: "vrf_B" + # - name: "GigabitEthernet3" + # - name: "GigabitEthernet4" + # + # commands: + # - interface GigabitEthernet2 + # - no vrf forwarding vrf_B + # + # after: + # - name: "Loopback24" + # - name: "GigabitEthernet1" + # - name: "GigabitEthernet2" + # - name: "GigabitEthernet3" + # - name: "GigabitEthernet4" + + # After state: + # ------------ + # + # vios#show running-config | section ^interface + # interface Loopback24 + # no ip address + # interface GigabitEthernet1 + # ip address dhcp + # negotiation auto + # interface GigabitEthernet2 + # no ip address + # shutdown + # negotiation auto + # interface GigabitEthernet3 + # no ip address + # negotiation auto + # interface GigabitEthernet4 + # no ip address + # shutdown + # negotiation auto + + # Using gathered + + # Before state: + # ------------- + # + # vios#show running-config | section ^interface + # interface Loopback24 + # no ip address + # interface GigabitEthernet1 + # ip address dhcp + # negotiation auto + # interface GigabitEthernet2 + # vrf forwarding vrf_B + # no ip address + # shutdown + # negotiation auto + # interface GigabitEthernet3 + # no ip address + # negotiation auto + # interface GigabitEthernet4 + # no ip address + # shutdown + # negotiation auto + + - name: Gather listed VRF interfaces + cisco.ios.ios_vrf_interfaces: + state: gathered + + # Task Output: + # ------------ + # + # gathered: + # - name: "Loopback24" + # - name: "GigabitEthernet1" + # - name: "GigabitEthernet2" + # vrf_name: "vrf_B" + # - name: "GigabitEthernet3" + # - name: "GigabitEthernet4" + + # Using rendered + + - name: Render VRF configuration + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_D + - name: GigabitEthernet3 + - name: GigabitEthernet4 + state: rendered + + # Task Output: + # ------------ + # + # rendered: + # - interface GigabitEthernet2 + # - vrf forwarding vrf_D + + # Using parsed + + # File: parsed.cfg + # --------------- + # + # interface GigabitEthernet1 + # vrf vrf_C + # shutdown + # ! + # interface GigabitEthernet2 + # vrf vrf_D + # shutdown + # ! + # interface GigabitEthernet3 + # shutdown + # ! + # interface GigabitEthernet4 + # shutdown + # ! + + - name: Parse configuration from device running config + cisco.ios.ios_vrf_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + + # Task Output: + # ------------ + # + # parsed: + # - name: "GigabitEthernet1" + # vrf_name: "vrf_C" + # - name: "GigabitEthernet2" + # vrf_name: "vrf_D" + # - name: "GigabitEthernet3" + # - name: "GigabitEthernet4" + + # Using replaced + + # Before state: + # ------------- + # + # vios#show running-config | section ^interface + # interface Loopback24 + # no ip address + # interface GigabitEthernet1 + # vrf forwarding vrf_A + # ip address dhcp + # negotiation auto + # interface GigabitEthernet2 + # vrf forwarding vrf_B + # no ip address + # shutdown + # negotiation auto + # interface GigabitEthernet3 + # no ip address + # negotiation auto + # interface GigabitEthernet4 + # vrf forwarding vrf_C + # no ip address + # shutdown + # negotiation auto + + - name: Replace device configuration of listed VRF interfaces with provided configuration + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + vrf_name: vrf_D + - name: GigabitEthernet2 + vrf_name: vrf_E + state: replaced + + # Task Output: + # ------------ + # + # before: + # - name: "Loopback24" + # - name: "GigabitEthernet1" + # vrf_name: "vrf_A" + # - name: "GigabitEthernet2" + # vrf_name: "vrf_B" + # - name: "GigabitEthernet3" + # - name: "GigabitEthernet4" + # vrf_name: "vrf_C" + # + # commands: + # - interface GigabitEthernet1 + # - no vrf forwarding vrf_A + # - vrf forwarding vrf_D + # - interface GigabitEthernet2 + # - no vrf forwarding vrf_B + # - vrf forwarding vrf_E + # + # after: + # - name: "Loopback24" + # - name: "GigabitEthernet1" + # vrf_name: "vrf_D" + # - name: "GigabitEthernet2" + # vrf_name: "vrf_E" + # - name: "GigabitEthernet3" + # - name: "GigabitEthernet4" + # vrf_name: "vrf_C" + + # Using deleted + + # Before state: + # ------------- + # + # vios#show running-config | section ^interface + # interface Loopback24 + # no ip address + # interface GigabitEthernet1 + # vrf forwarding vrf_A + # ip address dhcp + # negotiation auto + # interface GigabitEthernet2 + # vrf forwarding vrf_B + # no ip address + # shutdown + # negotiation auto + # interface GigabitEthernet3 + # no ip address + # negotiation auto + # interface GigabitEthernet4 + # vrf forwarding vrf_C + # no ip address + # shutdown + # negotiation auto + + - name: Delete VRF configuration of specified interfaces + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + state: deleted + + # Task Output: + # ------------ + # + # before: + # - name: "Loopback24" + # - name: "GigabitEthernet1" + # vrf_name: "vrf_A" + # - name: "GigabitEthernet2" + # vrf_name: "vrf_B" + # - name: "GigabitEthernet3" + # - name: "GigabitEthernet4" + # vrf_name: "vrf_C" + # + # commands: + # - interface GigabitEthernet1 + # - no vrf forwarding vrf_A + # - interface GigabitEthernet2 + # - no vrf forwarding vrf_B + # + # after: + # - name: "Loopback24" + # - name: "GigabitEthernet1" + # - name: "GigabitEthernet2" + # - name: "GigabitEthernet3" + # - name: "GigabitEthernet4" + # vrf_name: "vrf_C" + + # After state: + # ------------ + # + # vios#show running-config | section ^interface + # interface Loopback24 + # no ip address + # interface GigabitEthernet1 + # ip address dhcp + # negotiation auto + # interface GigabitEthernet2 + # no ip address + # shutdown + # negotiation auto + # interface GigabitEthernet3 + # no ip address + # negotiation auto + # interface GigabitEthernet4 + # vrf forwarding vrf_C + # no ip address + # shutdown + # negotiation auto + + + +Return Values +------------- +Common return values are documented `here `_, the following are the fields unique to this module: + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyReturnedDescription
+
+ after + +
+ list +
+
when changed +
The resulting configuration after module execution.
+
+
Sample:
+
[ + { + "name": "Loopback24" + }, + { + "name": "GigabitEthernet1" + }, + { + "name": "GigabitEthernet2", + "vrf_name": "vrf_D" + }, + { + "name": "GigabitEthernet3" + }, + { + "name": "GigabitEthernet4" + } + ]
+
+
+ before + +
+ list +
+
when state is merged, replaced, overridden, deleted +
The configuration prior to the module execution.
+
+
Sample:
+
[ + { + "name": "Loopback24" + }, + { + "name": "GigabitEthernet1" + }, + { + "name": "GigabitEthernet2", + "vrf_name": "vrf_B" + }, + { + "name": "GigabitEthernet3" + }, + { + "name": "GigabitEthernet4" + } + ]
+
+
+ commands + +
+ list +
+
when state is merged, replaced, overridden, deleted +
The set of commands pushed to the remote device.
+
+
Sample:
+
['interface GigabitEthernet2', 'vrf forwarding vrf_D', 'no vrf forwarding vrf_B']
+
+
+ gathered + +
+ list +
+
when state is gathered +
Facts about the network resource gathered from the remote device as structured data.
+
+
Sample:
+
[ + { + "name": "Loopback24" + }, + { + "name": "GigabitEthernet1" + }, + { + "name": "GigabitEthernet2", + "vrf_name": "vrf_B" + }, + { + "name": "GigabitEthernet3" + }, + { + "name": "GigabitEthernet4" + } + ]
+
+
+ parsed + +
+ list +
+
when state is parsed +
The device native config provided in running_config option parsed into structured data as per module argspec.
+
+
Sample:
+
[ + { + "name": "GigabitEthernet1", + "vrf_name": "vrf_C" + }, + { + "name": "GigabitEthernet2", + "vrf_name": "vrf_D" + }, + { + "name": "GigabitEthernet3" + }, + { + "name": "GigabitEthernet4" + } + ]
+
+
+ rendered + +
+ list +
+
when state is rendered +
The provided configuration in the task rendered in device-native format (offline).
+
+
Sample:
+
['interface GigabitEthernet1', 'vrf forwarding vrf_C', 'interface GigabitEthernet2', 'vrf forwarding vrf_D']
+
+

+ + +Status +------ + + +Authors +~~~~~~~ + +- AAYUSH ANAND (@AAYUSH2091) diff --git a/galaxy.yml b/galaxy.yml index 22c3d2613..e08e2ec5a 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -11,4 +11,4 @@ readme: README.md repository: https://github.com/ansible-collections/cisco.ios issues: https://github.com/ansible-collections/cisco.ios/issues tags: [cisco, ios, iosxe, networking] -version: "8.0.0" +version: "9.0.3" diff --git a/meta/runtime.yml b/meta/runtime.yml index 498325272..3ccab7553 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -23,14 +23,6 @@ plugin_routing: redirect: cisco.ios.ios_hostname interfaces: redirect: cisco.ios.ios_interfaces - ios_linkagg: - deprecation: - removal_date: "2024-06-01" - warning_text: See the plugin documentation for more details - ios_lldp: - deprecation: - removal_date: "2024-06-01" - warning_text: See the plugin documentation for more details l2_interfaces: redirect: cisco.ios.ios_l2_interfaces l3_interfaces: @@ -41,16 +33,6 @@ plugin_routing: redirect: cisco.ios.ios_lacp_interfaces lag_interfaces: redirect: cisco.ios.ios_lag_interfaces - linkagg: - deprecation: - removal_date: "2024-06-01" - warning_text: See the plugin documentation for more details - redirect: cisco.ios.ios_linkagg - lldp: - deprecation: - removal_date: "2024-06-01" - warning_text: See the plugin documentation for more details - redirect: cisco.ios.ios_lldp lldp_global: redirect: cisco.ios.ios_lldp_global lldp_interfaces: @@ -81,8 +63,12 @@ plugin_routing: redirect: cisco.ios.ios_vlans vrf: redirect: cisco.ios.ios_vrf + vrf_interfaces: + redirect: cisco.ios.vrf_interfaces vxlan_vtep: redirect: cisco.ios.ios_vxlan_vtep vrf_address_family: redirect: cisco.ios.ios_vrf_address_family -requires_ansible: ">=2.14.0" + vrf_global: + redirect: cisco.ios.ios_vrf_global +requires_ansible: ">=2.15.0" diff --git a/plugins/action/vrf_global.py b/plugins/action/vrf_global.py new file mode 120000 index 000000000..7747aa9dd --- /dev/null +++ b/plugins/action/vrf_global.py @@ -0,0 +1 @@ +ios.py \ No newline at end of file diff --git a/plugins/module_utils/network/ios/argspec/acls/acls.py b/plugins/module_utils/network/ios/argspec/acls/acls.py index a3ca057c5..605dc5ac7 100644 --- a/plugins/module_utils/network/ios/argspec/acls/acls.py +++ b/plugins/module_utils/network/ios/argspec/acls/acls.py @@ -69,7 +69,6 @@ class AclsArgs(object): # pylint: disable=R0903 }, "dscp": {"type": "str"}, "evaluate": {"type": "str"}, - "fragments": {"type": "str"}, "enable_fragments": {"type": "bool"}, "grant": {"choices": ["permit", "deny"], "type": "str"}, "log": { diff --git a/plugins/module_utils/network/ios/argspec/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/ios/argspec/bgp_address_family/bgp_address_family.py index 18fed7891..bee7856d3 100644 --- a/plugins/module_utils/network/ios/argspec/bgp_address_family/bgp_address_family.py +++ b/plugins/module_utils/network/ios/argspec/bgp_address_family/bgp_address_family.py @@ -73,6 +73,22 @@ class Bgp_address_familyArgs(object): # pylint: disable=R0903 }, }, "auto_summary": {"type": "bool"}, + "maximum_paths": { + "type": "dict", + "options": { + "paths": {"type": "int"}, + "eibgp": {"type": "int"}, + "ibgp": {"type": "int"}, + }, + }, + "maximum_secondary_paths": { + "type": "dict", + "options": { + "paths": {"type": "int"}, + "eibgp": {"type": "int"}, + "ibgp": {"type": "int"}, + }, + }, "bgp": { "type": "dict", "options": { @@ -378,7 +394,6 @@ class Bgp_address_familyArgs(object): # pylint: disable=R0903 "allpaths": {"type": "bool"}, }, }, - "password": {"type": "str", "no_log": True}, "password_options": { "type": "dict", "no_log": False, diff --git a/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py b/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py index 6e82c99c2..1a6047041 100644 --- a/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py +++ b/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py @@ -36,19 +36,6 @@ class Bgp_globalArgs(object): # pylint: disable=R0903 "type": "dict", "options": { "as_number": {"type": "str"}, - "aggregate_address": { - "type": "dict", - "options": { - "address": {"type": "str"}, - "netmask": {"type": "str"}, - "advertise_map": {"type": "str"}, - "as_confed_set": {"type": "bool"}, - "as_set": {"type": "bool"}, - "attribute_map": {"type": "str"}, - "summary_only": {"type": "bool"}, - "suppress_map": {"type": "str"}, - }, - }, "aggregate_addresses": { "type": "list", "elements": "dict", @@ -88,23 +75,6 @@ class Bgp_globalArgs(object): # pylint: disable=R0903 "aggregate_timer": {"type": "int"}, "always_compare_med": {"type": "bool"}, "asnotation": {"type": "bool"}, - "bestpath": { - "type": "list", - "elements": "dict", - "options": { - "aigp": {"type": "bool"}, - "compare_routerid": {"type": "bool"}, - "cost_community": {"type": "bool"}, - "igp_metric": {"type": "bool"}, - "med": { - "type": "dict", - "options": { - "confed": {"type": "bool"}, - "missing_as_worst": {"type": "bool"}, - }, - }, - }, - }, "bestpath_options": { "type": "dict", "options": { @@ -212,14 +182,6 @@ class Bgp_globalArgs(object): # pylint: disable=R0903 "local_preference": {"type": "int"}, }, }, - "inject_map": { - "type": "dict", - "options": { - "name": {"type": "str"}, - "exist_map_name": {"type": "str"}, - "copy_attributes": {"type": "bool"}, - }, - }, "inject_maps": { "type": "list", "elements": "dict", @@ -236,8 +198,6 @@ class Bgp_globalArgs(object): # pylint: disable=R0903 "range": { "type": "dict", "options": { - "ipv4_with_subnet": {"type": "str"}, - "ipv6_with_subnet": {"type": "str"}, "host_with_subnet": {"type": "str"}, "peer_group": {"type": "str"}, }, @@ -261,16 +221,6 @@ class Bgp_globalArgs(object): # pylint: disable=R0903 }, }, }, - "nopeerup_delay": { - "type": "list", - "elements": "dict", - "options": { - "cold_boot": {"type": "int"}, - "nsf_switchover": {"type": "int"}, - "post_boot": {"type": "int"}, - "user_initiated": {"type": "int"}, - }, - }, "nopeerup_delay_options": { "type": "dict", "options": { @@ -397,15 +347,6 @@ class Bgp_globalArgs(object): # pylint: disable=R0903 "interface": {"type": "str"}, }, }, - "distribute_list": { - "type": "dict", - "options": { - "acl": {"type": "str"}, - "in": {"type": "bool"}, - "out": {"type": "bool"}, - "interface": {"type": "str"}, - }, - }, "maximum_paths": { "type": "dict", "options": { @@ -428,9 +369,6 @@ class Bgp_globalArgs(object): # pylint: disable=R0903 "aliases": ["neighbor"], "options": { "neighbor_address": {"type": "str"}, - "address": {"type": "str"}, - "tag": {"type": "str"}, - "ipv6_adddress": {"type": "str"}, "activate": {"type": "bool"}, "additional_paths": { "type": "dict", @@ -594,7 +532,6 @@ class Bgp_globalArgs(object): # pylint: disable=R0903 "type": "dict", "options": {"set": {"type": "bool"}, "allpaths": {"type": "bool"}}, }, - "password": {"type": "str", "no_log": True}, "password_options": { "type": "dict", "no_log": False, @@ -646,14 +583,6 @@ class Bgp_globalArgs(object): # pylint: disable=R0903 "replace_as": {"type": "bool"}, }, }, - "route_map": { - "type": "dict", - "options": { - "name": {"type": "str"}, - "in": {"type": "bool"}, - "out": {"type": "bool"}, - }, - }, "route_maps": { "type": "list", "elements": "dict", @@ -911,25 +840,6 @@ class Bgp_globalArgs(object): # pylint: disable=R0903 }, }, }, - "route_server_context": { - "type": "dict", - "options": { - "name": {"type": "str"}, - "address_family": { - "type": "dict", - "options": { - "afi": {"type": "str", "choices": ["ipv4", "ipv6"]}, - "modifier": {"type": "str", "choices": ["multicast", "unicast"]}, - "import_map": {"type": "str"}, - }, - }, - "description": {"type": "str"}, - }, - }, - "scope": { - "type": "dict", - "options": {"global": {"type": "bool"}, "vrf": {"type": "str"}}, - }, "synchronization": {"type": "bool"}, "table_map": { "type": "dict", diff --git a/plugins/module_utils/network/ios/argspec/vrf_global/__init__.py b/plugins/module_utils/network/ios/argspec/vrf_global/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/ios/argspec/vrf_global/vrf_global.py b/plugins/module_utils/network/ios/argspec/vrf_global/vrf_global.py new file mode 100644 index 000000000..87a84bbf7 --- /dev/null +++ b/plugins/module_utils/network/ios/argspec/vrf_global/vrf_global.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# ansible.content_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the documentation in the module file and re-run +# ansible.content_builder commenting out +# the path to external 'docstring' in build.yaml. +# +############################################## + +""" +The arg spec for the ios_vrf_global module +""" + + +class Vrf_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_vrf_global module""" + + argument_spec = { + "config": { + "type": "dict", + "options": { + "vrfs": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "description": {"type": "str"}, + "ipv4": { + "type": "dict", + "options": { + "multicast": { + "type": "dict", + "options": {"multitopology": {"type": "bool"}}, + }, + }, + }, + "ipv6": { + "type": "dict", + "options": { + "multicast": { + "type": "dict", + "options": {"multitopology": {"type": "bool"}}, + }, + }, + }, + "rd": {"type": "str"}, + "route_target": { + "type": "dict", + "options": { + "export": {"type": "str"}, + "import_config": {"type": "str"}, + "both": {"type": "str"}, + }, + }, + "vnet": { + "type": "dict", + "options": {"tag": {"type": "int"}}, + }, + "vpn": { + "type": "dict", + "options": {"id": {"type": "str"}}, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "parsed", + "gathered", + "deleted", + "merged", + "replaced", + "rendered", + "overridden", + "purged", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/ios/argspec/vrf_interfaces/__init__.py b/plugins/module_utils/network/ios/argspec/vrf_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py new file mode 100644 index 000000000..7a5a1bbe7 --- /dev/null +++ b/plugins/module_utils/network/ios/argspec/vrf_interfaces/vrf_interfaces.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# ansible.content_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the documentation in the module file and re-run +# ansible.content_builder commenting out +# the path to external 'docstring' in build.yaml. +# +############################################## + +""" +The arg spec for the ios_vrf_interfaces module +""" + + +class Vrf_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_vrf_interfaces module""" + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "vrf_name": {"type": "str"}, + }, + }, + "running_config": {"type": "str"}, + "state": { + "type": "str", + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + }, + } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/ios/config/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/ios/config/bgp_address_family/bgp_address_family.py index 17cdd11fc..7a9ffdec9 100644 --- a/plugins/module_utils/network/ios/config/bgp_address_family/bgp_address_family.py +++ b/plugins/module_utils/network/ios/config/bgp_address_family/bgp_address_family.py @@ -47,6 +47,12 @@ class Bgp_address_family(ResourceModule): "default_information", "default_metric", "distance", + "maximum_paths.paths", + "maximum_paths.eibgp", + "maximum_paths.ibgp", + "maximum_secondary_paths.paths", + "maximum_secondary_paths.eibgp", + "maximum_secondary_paths.ibgp", "table_map", "bgp.additional_paths.select", # bgp "bgp.additional_paths.install", diff --git a/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py b/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py index 77112ffa2..36c9bf89a 100644 --- a/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py +++ b/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py @@ -56,9 +56,6 @@ class Bgp_global(ResourceModule): "maximum_secondary_paths.paths", "maximum_secondary_paths.eibgp", "maximum_secondary_paths.ibgp", - "route_server_context.name", - "route_server_context.address_family", - "route_server_context.description", "synchronization", "table_map", "template.peer_policy", @@ -333,7 +330,6 @@ def _compare_neighbor_lists(self, want, have): "capability", "default_originate", "default_originate.route_map", - "distribute_list", "dmzlink_bw", "filter_list", "maximum_prefix", @@ -449,14 +445,6 @@ def _bgp_global_list_to_dict(self, tmp_data): def handle_deprecates(self, want, is_nbr=False): """ Handles deprecated values post rewrite - aggregate_address [dict] - aggregate_addresses [list:dict] - bgp.bestpath [list:dict] - bgp.bestpath_options [dict] - bgp.inject_map [dict] - bgp.inject_map [list:dict] - bgp.listen.(ipv4/v6_with_subnet) [multiple] - bgp.listen.host_with_subnet - bgp.nopeerup_delay [list:dict] - bgp.nopeerup_delay_option [dict] - distributed_list [dict] - distributes [list:dict] - neighbor.address.(tag/ipv4/v6_address) [multiple] - neighbor.address.neighbor_address - neighbor.password [str] - neighbor.password [dict] neighbor.route_map [dict] - neighbor.route_maps [list:dict] Args: @@ -464,57 +452,7 @@ def handle_deprecates(self, want, is_nbr=False): is_nbr (bool, optional): activates neighbor part on recursion. Defaults to False. """ if not is_nbr: - if want.get("aggregate_address"): - if want.get("aggregate_addresses"): - want["aggregate_addresses"].append(want.pop("aggregate_address")) - else: - want["aggregate_addresses"] = [want.pop("aggregate_address")] - if want.get("bgp"): - _want_bgp = want.get("bgp", {}) - if _want_bgp.get("bestpath"): - bpath = {} - for i in _want_bgp.pop("bestpath"): - bpath = dict_merge(bpath, i) - _want_bgp["bestpath_options"] = bpath - if _want_bgp.get("nopeerup_delay"): - npdelay = {} - for i in _want_bgp.pop("nopeerup_delay"): - npdelay = dict_merge(npdelay, i) - _want_bgp["nopeerup_delay_options"] = npdelay - if _want_bgp.get("inject_map"): - if _want_bgp.get("inject_maps"): - _want_bgp["inject_maps"].append(_want_bgp.pop("inject_map")) - else: - _want_bgp["inject_maps"] = [_want_bgp.pop("inject_map")] - if _want_bgp.get("listen", {}).get("range"): - if _want_bgp.get("listen").get("range").get("ipv4_with_subnet"): - _want_bgp["listen"]["range"]["host_with_subnet"] = _want_bgp["listen"][ - "range" - ].pop("ipv4_with_subnet") - elif _want_bgp.get("listen").get("range").get("ipv6_with_subnet"): - _want_bgp["listen"]["range"]["host_with_subnet"] = _want_bgp["listen"][ - "range" - ].pop("ipv6_with_subnet") - if want.get("distribute_list"): - if want.get("distributes"): - want["distributes"].append(want.pop("distribute_list")) - else: - want["distributes"] = [want.pop("distribute_list")] if want.get("neighbors"): _want_nbrs = want.get("neighbors", {}) for nbr in _want_nbrs: nbr = self.handle_deprecates(nbr, is_nbr=True) - else: - if want.get("address"): - want["neighbor_address"] = want.pop("address") - if want.get("tag"): - want["neighbor_address"] = want.pop("tag") - if want.get("ipv6_adddress"): - want["neighbor_address"] = want.pop("ipv6_adddress") - if want.get("route_map"): - if want.get("route_maps"): - want["route_maps"].append(want.pop("route_map")) - else: - want["route_maps"] = [want.pop("route_map")] - if want.get("password"): - want["password_options"] = {"pass_key": want.pop("password")} diff --git a/plugins/module_utils/network/ios/config/l2_interfaces/l2_interfaces.py b/plugins/module_utils/network/ios/config/l2_interfaces/l2_interfaces.py index 653b837eb..2cec3779f 100644 --- a/plugins/module_utils/network/ios/config/l2_interfaces/l2_interfaces.py +++ b/plugins/module_utils/network/ios/config/l2_interfaces/l2_interfaces.py @@ -31,6 +31,7 @@ L2_interfacesTemplate, ) from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + generate_switchport_trunk, normalize_interface, vlan_list_to_range, vlan_range_to_list, @@ -136,11 +137,10 @@ def compare_list(self, want, have): ), ) if self.state != "deleted" and cmd_always: # add configuration needed - add = "add " if have.get("trunk", {}).get(vlan, []) else "" - self.commands.append( - "switchport trunk {0} vlan {1}{2}".format( + self.commands.extend( + generate_switchport_trunk( vlan.split("_", maxsplit=1)[0], - add, + have.get("trunk", {}).get(vlan, []), vlan_list_to_range(sorted(cmd_always)), ), ) diff --git a/plugins/module_utils/network/ios/config/snmp_server/snmp_server.py b/plugins/module_utils/network/ios/config/snmp_server/snmp_server.py index d61a70a10..9b1dbafb6 100644 --- a/plugins/module_utils/network/ios/config/snmp_server/snmp_server.py +++ b/plugins/module_utils/network/ios/config/snmp_server/snmp_server.py @@ -20,7 +20,6 @@ from copy import deepcopy -from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) @@ -235,12 +234,13 @@ def _compare(self, want, have): self._compare_lists_attrs(want, have) def _compare_lists_attrs(self, want, have): - """Compare list of dict""" + """Compare list of dicts""" for _parser in self.list_parsers: i_want = want.get(_parser, {}) i_have = have.get(_parser, {}) - for key, wanting in iteritems(i_want): - haveing = i_have.pop(key, {}) + have_keys = set(i_have.keys()) + for key, wanting in i_want.items(): + haveing = i_have.get(key, {}) if wanting != haveing: if haveing and self.state in ["overridden", "replaced"]: if not ( @@ -249,8 +249,9 @@ def _compare_lists_attrs(self, want, have): ): self.addcmd(haveing, _parser, negate=True) self.addcmd(wanting, _parser) - for key, haveing in iteritems(i_have): - self.addcmd(haveing, _parser, negate=True) + have_keys.discard(key) + for key in have_keys: + self.addcmd(i_have[key], _parser, negate=True) def _snmp_list_to_dict(self, data): """Convert all list of dicts to dicts of dicts""" diff --git a/plugins/module_utils/network/ios/config/vlans/vlans.py b/plugins/module_utils/network/ios/config/vlans/vlans.py index d195c317e..a438d3f8e 100644 --- a/plugins/module_utils/network/ios/config/vlans/vlans.py +++ b/plugins/module_utils/network/ios/config/vlans/vlans.py @@ -114,10 +114,12 @@ def generate_commands(self, conf_want, conf_have, resource=None): if self.state == "merged": wantd = dict_merge(haved, wantd) - # if state is deleted, empty out wantd and set haved to wantd - if self.state == "deleted": + # if state is overridden, remove excluded vlans + if self.state == "overridden": + excluded_vlans = {k: v for k, v in iteritems(haved) if k not in wantd or not wantd} haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} - wantd = {} + for k, have in iteritems(excluded_vlans): + self.purge(have, resource) # if state is deleted, empty out wantd and set haved to wantd if self.state in ["deleted", "purged"]: @@ -165,3 +167,5 @@ def purge(self, have, resource): """Handle operation for purged state""" if resource == "vlan_configuration": self.commands.append(self._tmplt.render(have, resource, True)) + elif resource == "vlans": + self.commands.append(self._tmplt.render(have, resource, True)) diff --git a/plugins/module_utils/network/ios/config/vrf_global/__init__.py b/plugins/module_utils/network/ios/config/vrf_global/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/ios/config/vrf_global/vrf_global.py b/plugins/module_utils/network/ios/config/vrf_global/vrf_global.py new file mode 100644 index 000000000..476be76e0 --- /dev/null +++ b/plugins/module_utils/network/ios/config/vrf_global/vrf_global.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios_vrf_global config file. +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 its desired end-state is +created. +""" + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.vrf_global import ( + Vrf_globalTemplate, +) + + +class Vrf_global(ResourceModule): + """ + The ios_vrf_global config class + """ + + def __init__(self, module): + super(Vrf_global, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="vrf_global", + tmplt=Vrf_globalTemplate(), + ) + self.parsers = [ + "description", + "ipv4.multicast.multitopology", + "ipv6.multicast.multitopology", + "rd", + "route_target.export", + "route_target.import_config", + "route_target.both", + "vnet.tag", + "vpn.id", + ] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + haved, wantd = dict(), dict() + + if self.want: + for entry in self.want.get("vrfs", []): + wantd.update({(entry["name"]): entry}) + + if self.have: + for entry in self.have.get("vrfs", []): + haved.update({(entry["name"]): entry}) + + # if state is merged, merge want onto have + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, limit the have to anything in want & set want to nothing + if self.state == "deleted": + haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} + wantd = {} + + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + if k not in wantd: + self._compare(want={}, have=have) + + if self.state == "purged": + for k, have in iteritems(haved): + self.purge(have) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Vrf_global network resource. + """ + if want != have: + self.addcmd(want or have, "name", False) + self.compare(self.parsers, want, have) + + def purge(self, have): + """Purge the VRF configuration""" + self.commands.append("no vrf definition {0}".format(have["name"])) diff --git a/plugins/module_utils/network/ios/config/vrf_interfaces/__init__.py b/plugins/module_utils/network/ios/config/vrf_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py new file mode 100644 index 000000000..4d7dd3a9e --- /dev/null +++ b/plugins/module_utils/network/ios/config/vrf_interfaces/vrf_interfaces.py @@ -0,0 +1,99 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2024 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios_vrf_interfaces config file. +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 its desired end-state is +created. +""" + + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.vrf_interfaces import ( + Vrf_interfacesTemplate, +) + + +class Vrf_interfaces(ResourceModule): + """ + The ios_vrf_interfaces config class + """ + + def __init__(self, module): + super(Vrf_interfaces, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="vrf_interfaces", + tmplt=Vrf_interfacesTemplate(), + ) + self.parsers = [ + "interface", + "vrf_name", + ] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + wantd = {entry["name"]: entry for entry in self.want} + haved = {entry["name"]: entry for entry in self.have} + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to wantd + if self.state == "deleted": + haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} + wantd = {} + + # remove superfluous config for overridden and deleted + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + if k not in wantd: + self._compare(want={}, have=have) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Vrf_interfaces network resource. + """ + begin = len(self.commands) + self.compare(parsers=self.parsers, want=want, have=have) + if len(self.commands) != begin: + self.commands.insert(begin, self._tmplt.render(want or have, "interface", False)) diff --git a/plugins/module_utils/network/ios/facts/facts.py b/plugins/module_utils/network/ios/facts/facts.py index 98b6dde12..fb326ca1b 100644 --- a/plugins/module_utils/network/ios/facts/facts.py +++ b/plugins/module_utils/network/ios/facts/facts.py @@ -99,6 +99,12 @@ from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vlans.vlans import ( VlansFacts, ) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vrf_global.vrf_global import ( + Vrf_globalFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vrf_interfaces.vrf_interfaces import ( + Vrf_interfacesFacts, +) from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vxlan_vtep.vxlan_vtep import ( Vxlan_vtepFacts, ) @@ -143,6 +149,8 @@ evpn_global=Evpn_globalFacts, evpn_evi=Evpn_eviFacts, vrf_address_family=Vrf_address_familyFacts, + vrf_global=Vrf_globalFacts, + vrf_interfaces=Vrf_interfacesFacts, ) diff --git a/plugins/module_utils/network/ios/facts/vrf_global/__init__.py b/plugins/module_utils/network/ios/facts/vrf_global/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/ios/facts/vrf_global/vrf_global.py b/plugins/module_utils/network/ios/facts/vrf_global/vrf_global.py new file mode 100644 index 000000000..3253562e3 --- /dev/null +++ b/plugins/module_utils/network/ios/facts/vrf_global/vrf_global.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios vrf_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 ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vrf_global.vrf_global import ( + Vrf_globalArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.vrf_global import ( + Vrf_globalTemplate, +) + + +class Vrf_globalFacts(object): + """The ios vrf_global facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Vrf_globalArgs.argument_spec + + def get_config(self, connection): + """Get the configuration from the device""" + + return connection.get("show running-config | section ^vrf") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Vrf_global network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + + facts = {} + objs = [] + + if not data: + data = self.get_config(connection) + + vrf_global_parser = Vrf_globalTemplate(lines=data.splitlines(), module=self._module) + objs = vrf_global_parser.parse() + + # Convert the dictionary to a list of dictionaries + objs["vrfs"] = list(objs["vrfs"].values()) if "vrfs" in objs else [] + + ansible_facts["ansible_network_resources"].pop("vrf_global", None) + params = utils.remove_empties( + vrf_global_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), + ) + + facts["vrf_global"] = params.get("config", {}) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/plugins/module_utils/network/ios/facts/vrf_interfaces/__init__.py b/plugins/module_utils/network/ios/facts/vrf_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py b/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py new file mode 100644 index 000000000..61a7f9c1f --- /dev/null +++ b/plugins/module_utils/network/ios/facts/vrf_interfaces/vrf_interfaces.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The cisco.ios vrf_interfaces facts class +It is in this file that the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vrf_interfaces.vrf_interfaces import ( + Vrf_interfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.vrf_interfaces import ( + Vrf_interfacesTemplate, +) + + +class Vrf_interfacesFacts(object): + """The cisco.ios vrf_interfaces facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Vrf_interfacesArgs.argument_spec + + def get_vrf_interfaces_data(self, connection): + """Fetch the configuration data for VRF interfaces. + + :param connection: The device connection object + :returns: The raw configuration data from the device + """ + return connection.get("show running-config | section ^interface") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Vrf_interfaces network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected config (optional) + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + + if not data: + data = self.get_vrf_interfaces_data(connection) + + # Parse native config using the Vrf_interfaces template + vrf_interfaces_parser = Vrf_interfacesTemplate( + lines=data.splitlines(), + module=self._module, + ) + + objs = list(vrf_interfaces_parser.parse().values()) + # Ensure previous facts are removed to avoid duplication + ansible_facts["ansible_network_resources"].pop("vrf_interfaces", None) + + params = utils.remove_empties( + vrf_interfaces_parser.validate_config( + self.argument_spec, + {"config": objs}, + redact=True, + ), + ) + + # Update the ansible_facts dictionary with the VRF interface facts + facts["vrf_interfaces"] = params.get("config", []) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/plugins/module_utils/network/ios/ios.py b/plugins/module_utils/network/ios/ios.py index 9ed7a1f27..3c0441e4d 100644 --- a/plugins/module_utils/network/ios/ios.py +++ b/plugins/module_utils/network/ios/ios.py @@ -141,8 +141,10 @@ def _get_number(name): if_type = "FastEthernet" elif name.lower().startswith("fo"): if_type = "FortyGigabitEthernet" - elif name.lower().startswith("fi"): + elif name.lower().startswith("fiv"): if_type = "FiveGigabitEthernet" + elif name.lower().startswith("fif"): + if_type = "FiftyGigabitEthernet" elif name.lower().startswith("et"): if_type = "Ethernet" elif name.lower().startswith("vl"): diff --git a/plugins/module_utils/network/ios/rm_templates/acls.py b/plugins/module_utils/network/ios/rm_templates/acls.py index 54d082c2e..642128311 100644 --- a/plugins/module_utils/network/ios/rm_templates/acls.py +++ b/plugins/module_utils/network/ios/rm_templates/acls.py @@ -88,7 +88,7 @@ def source_destination_common_config(config_data, command, attr): command += " dscp {dscp}".format(**aces) if aces.get("sequence") and aces.get("afi") == "ipv6": command += " sequence {sequence}".format(**aces) - if aces.get("enable_fragments") or aces.get("fragments"): + if aces.get("enable_fragments"): command += " fragments" if aces.get("log"): command += " log" diff --git a/plugins/module_utils/network/ios/rm_templates/bgp_address_family.py b/plugins/module_utils/network/ios/rm_templates/bgp_address_family.py index eb5a08a37..b0f207c74 100644 --- a/plugins/module_utils/network/ios/rm_templates/bgp_address_family.py +++ b/plugins/module_utils/network/ios/rm_templates/bgp_address_family.py @@ -205,6 +205,102 @@ def __init__(self, lines=None, module=None): }, }, }, + { + "name": "maximum_paths.paths", + "getval": re.compile( + r""" + \s\smaximum-paths + (\s(?P\d+))? + $""", + re.VERBOSE, + ), + "setval": "maximum-paths {{ maximum_paths.paths|string }}", + "result": { + "address_family": { + UNIQUE_AFI: {"maximum_paths": {"paths": "{{ paths }}"}}, + }, + }, + }, + { + "name": "maximum_paths.eibgp", + "getval": re.compile( + r""" + \s\smaximum-paths\seibgp + (\s(?P\d+))? + $""", + re.VERBOSE, + ), + "setval": "maximum-paths eibgp {{ maximum_paths.eibgp|string }}", + "result": { + "address_family": { + UNIQUE_AFI: {"maximum_paths": {"eibgp": "{{ eibgp }}"}}, + }, + }, + }, + { + "name": "maximum_paths.ibgp", + "getval": re.compile( + r""" + \s\smaximum-paths\sibgp + (\s(?P\d+))? + $""", + re.VERBOSE, + ), + "setval": "maximum-paths ibgp {{ maximum_paths.ibgp|string }}", + "result": { + "address_family": { + UNIQUE_AFI: {"maximum_paths": {"ibgp": "{{ ibgp }}"}}, + }, + }, + }, + { + "name": "maximum_secondary_paths.paths", + "getval": re.compile( + r""" + \s\smaximum-secondary-paths + (\s(?P\d+))? + $""", + re.VERBOSE, + ), + "setval": "maximum-secondary-paths {{ maximum_secondary_paths.paths|string }}", + "result": { + "address_family": { + UNIQUE_AFI: {"maximum_secondary_paths": {"paths": "{{ paths }}"}}, + }, + }, + }, + { + "name": "maximum_secondary_paths.eibgp", + "getval": re.compile( + r""" + \s\smaximum-secondary-paths\seibgp + (\s(?P\d+))? + $""", + re.VERBOSE, + ), + "setval": "maximum-secondary-paths eibgp {{ maximum_secondary_paths.eibgp|string }}", + "result": { + "address_family": { + UNIQUE_AFI: {"maximum_secondary_paths": {"eibgp": "{{ eibgp }}"}}, + }, + }, + }, + { + "name": "maximum_secondary_paths.ibgp", + "getval": re.compile( + r""" + \s\smaximum-secondary-paths\sibgp + (\s(?P\d+))? + $""", + re.VERBOSE, + ), + "setval": "maximum-secondary-paths ibgp {{ maximum_secondary_paths.ibgp|string }}", + "result": { + "address_family": { + UNIQUE_AFI: {"maximum_secondary_paths": {"ibgp": "{{ ibgp }}"}}, + }, + }, + }, # bgp starts { "name": "bgp.additional_paths.select", @@ -1490,8 +1586,8 @@ def __init__(self, lines=None, module=None): "getval": re.compile( r""" \s\sneighbor\s(?P\S+)\spassword - \s(?P\d+) - (\s(?P.$))? + (\s(?P\d+)) + (\s(?P.+))? $""", re.VERBOSE, ), diff --git a/plugins/module_utils/network/ios/rm_templates/bgp_global.py b/plugins/module_utils/network/ios/rm_templates/bgp_global.py index 2ce959fcd..641a02240 100644 --- a/plugins/module_utils/network/ios/rm_templates/bgp_global.py +++ b/plugins/module_utils/network/ios/rm_templates/bgp_global.py @@ -400,55 +400,6 @@ def __init__(self, lines=None, module=None): ], }, }, - { - "name": "route_server_context.name", - "getval": re.compile( - r""" - \sroute-server_context - (\s(?P\S+))? - $""", - re.VERBOSE, - ), - "setval": "route-server-context {{ route_server_context.name }}", - "result": {"route_server_context": {"name": "{{ name }}"}}, - }, - { - "name": "route_server_context.address_family", - "getval": re.compile( - r""" - \sroute-server_context\saddress-family - (\s(?Pipv4|ipv6))? - (\s(?Pmulticast|unicast))? - (\simport-map\s(?P\S+))? - $""", - re.VERBOSE, - ), - "setval": "address-family" - "{{ (' ' + route_server_context.address_family.afi) if route_server_context.address_family.afi is defined else '' }}" - "{{ (' ' + route_server_context.address_family.modifier) if route_server_context.address_family.modifier is defined else '' }}" - "{{ (' import-map ' + route_server_context.address_family.import_map) if route_server_context.address_family.import_map is defined else '' }}", - "result": { - "route_server_context": { - "address_family": { - "afi": "{{ afi }}", - "modifier": "{{ modifier }}", - "import_map": "{{ import_map }}", - }, - }, - }, - }, - { - "name": "route_server_context.description", - "getval": re.compile( - r""" - \sroute-server_context\sdescription - (\s(?P.+$))? - """, - re.VERBOSE, - ), - "setval": "description {{ route_server_context.description }}", - "result": {"route_server_context": {"description": "'{{ description }}'"}}, - }, { "name": "synchronization", "getval": re.compile( @@ -1511,13 +1462,13 @@ def __init__(self, lines=None, module=None): "getval": re.compile( r""" \sneighbor\s(?P\S+) - \s(?Pebgp_multihop) + \s(?Pebgp-multihop) (\s(?P\d+))? $""", re.VERBOSE, ), "setval": "neighbor {{ neighbor_address }} ebgp-multihop" - "{{ (' ' + hop_count|string) if hop_count is defined else '' }}", + "{{ (' ' + ebgp_multihop.hop_count|string) if ebgp_multihop.hop_count is defined else '' }}", "result": { "neighbors": { "{{ neighbor_address }}": { @@ -1691,7 +1642,7 @@ def __init__(self, lines=None, module=None): r""" \sneighbor\s(?P\S+)\spassword \s(?P\d+) - (\s(?P.$))? + (\s(?P.+))? $""", re.VERBOSE, ), @@ -2415,34 +2366,6 @@ def __init__(self, lines=None, module=None): }, }, }, - { - "name": "distribute_list", - "getval": re.compile( - r""" - \sneighbor\s(?P\S+)\sdistribute-list - (\s(?P\S+)) - (\s(?Pin))? - (\s(?Pout))? - $""", - re.VERBOSE, - ), - "setval": "neighbor {{ neighbor_address }} distribute-list" - "{{ (' ' + distribute_list.acl) if distribute_list.acl is defined else '' }}" - "{{ (' in') if distribute_list.in|d(False) else '' }}" - "{{ (' out') if distribute_list.out|d(False) else '' }}", - "result": { - "neighbors": { - "{{ neighbor_address }}": { - "neighbor_address": "{{ neighbor_address }}", - "distribute_list": { - "acl": "{{ acl }}", - "in": "{{ not not in }}", - "out": "{{ not not out }}", - }, - }, - }, - }, - }, { "name": "dmzlink_bw", "getval": re.compile( diff --git a/plugins/module_utils/network/ios/rm_templates/ospfv2.py b/plugins/module_utils/network/ios/rm_templates/ospfv2.py index 80548254e..96bdb6d08 100644 --- a/plugins/module_utils/network/ios/rm_templates/ospfv2.py +++ b/plugins/module_utils/network/ios/rm_templates/ospfv2.py @@ -127,6 +127,7 @@ def _tmplt_ospf_neighbor(config_data): def _tmplt_ospf_passive_interfaces(config_data): + cmd = "" # Initialize cmd to avoid potential unbound error if "passive_interfaces" in config_data: if config_data["passive_interfaces"].get("interface"): if config_data["passive_interfaces"].get("set_interface"): diff --git a/plugins/module_utils/network/ios/rm_templates/service.py b/plugins/module_utils/network/ios/rm_templates/service.py index c81593226..b93856d94 100644 --- a/plugins/module_utils/network/ios/rm_templates/service.py +++ b/plugins/module_utils/network/ios/rm_templates/service.py @@ -29,7 +29,7 @@ def handleTimestamp(config_data): if config_data.get("datetime_options"): datetime_op = config_data.get("datetime_options") - command += " mesc" if datetime_op.get("msec") else "" + command += " msec" if datetime_op.get("msec") else "" command += " localtime" if datetime_op.get("localtime") else "" command += " show-timezone" if datetime_op.get("show_timezone") else "" command += " year" if datetime_op.get("year") else "" diff --git a/plugins/module_utils/network/ios/rm_templates/static_routes.py b/plugins/module_utils/network/ios/rm_templates/static_routes.py index bee1aed63..c314447ec 100644 --- a/plugins/module_utils/network/ios/rm_templates/static_routes.py +++ b/plugins/module_utils/network/ios/rm_templates/static_routes.py @@ -37,8 +37,8 @@ def __init__(self, lines=None, module=None): (\svrf\s(?P\S+))? (\s(?P\S+)) (\s(?P\S+)) - (\s(?P(ACR|ATM-ACR|Analysis-Module|AppNav-Compress|AppNav-UnCompress|Async|Auto-Template|BD-VIF|BDI|BVI|Bluetooth|CDMA-Ix|CEM-ACR|CEM-PG|CTunnel|Container|Dialer|EsconPhy|Ethernet-Internal|Fcpa|Filter|Filtergroup|GigabitEthernet|IMA-ACR|LongReachEthernet|Loopback|Lspvif|MFR|Multilink|NVI|Null|PROTECTION_GROUP|Port-channel|Portgroup|Pos-channel|SBC|SDH_ACR|SERIAL-ACR|SONET_ACR|SSLVPN-VIF|SYSCLOCK|Serial-PG|Service-Engine|TLS-VIF|Tunnel|VPN|Vif|Vir-cem-ACR|Virtual-PPP|Virtual-TokenRing)\S+))? - (\s(?P(?!multicast|dhcp|global|tag|track|permanent|name)\S+))? + (\s(?P(ACR|ATM-ACR|Analysis-Module|AppNav-Compress|AppNav-UnCompress|Async|Auto-Template|BD-VIF|BDI|BVI|Bluetooth|CDMA-Ix|CEM-ACR|CEM-PG|CTunnel|Container|Dialer|EsconPhy|Ethernet-Internal|Fcpa|Filter|Filtergroup|GigabitEthernet|TenGigabitEthernet|IMA-ACR|LongReachEthernet|Loopback|Lspvif|MFR|Multilink|NVI|Null|PROTECTION_GROUP|Port-channel|Portgroup|Pos-channel|SBC|SDH_ACR|SERIAL-ACR|SONET_ACR|SSLVPN-VIF|SYSCLOCK|Serial-PG|Service-Engine|TLS-VIF|Tunnel|VPN|Vif|Vir-cem-ACR|Virtual-PPP|Virtual-TokenRing)\S+))? + (\s(?P(?!multicast|dhcp|global|tag|track|permanent|name)(?!(?\d+))? (\stag\s(?P\d+))? (\s(?Ppermanent))? diff --git a/plugins/module_utils/network/ios/rm_templates/vrf_global.py b/plugins/module_utils/network/ios/rm_templates/vrf_global.py new file mode 100644 index 000000000..0563f461f --- /dev/null +++ b/plugins/module_utils/network/ios/rm_templates/vrf_global.py @@ -0,0 +1,225 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Vrf_global parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +class Vrf_globalTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Vrf_globalTemplate, self).__init__( + lines=lines, + tmplt=self, + module=module, + ) + + # fmt: off + PARSERS = [ + { + "name": "name", + "getval": re.compile( + r""" + ^vrf\sdefinition + (\s(?P\S+))? + $""", re.VERBOSE, + ), + "setval": "vrf definition {{ name }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + }, + }, + }, + "shared": True, + }, + { + "name": "description", + "getval": re.compile( + r""" + \s+description\s(?P.+$) + $""", re.VERBOSE, + ), + "setval": "description {{ description }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + 'description': '{{ description }}', + }, + }, + }, + }, + { + "name": "ipv4.multicast.multitopology", + "getval": re.compile( + r""" + \s+ipv4\smulticast\s(?Pmultitopology) + $""", re.VERBOSE, + ), + "setval": "ipv4 multicast multitopology", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + 'ipv4': { + 'multicast': { + 'multitopology': "{{ true if multitopology is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "ipv6.multicast.multitopology", + "getval": re.compile( + r""" + \s+ipv6\smulticast\s(?Pmultitopology) + $""", re.VERBOSE, + ), + "setval": "ipv6 multicast multitopology", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + 'ipv6': { + 'multicast': { + 'multitopology': "{{ true if multitopology is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "rd", + "getval": re.compile( + r""" + \s+rd\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "rd {{ rd }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "rd": "{{ rd }}", + }, + }, + }, + }, + { + "name": "route_target.export", + "getval": re.compile( + r""" + \s+route-target\sexport\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "route-target export {{ route_target.export }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "route_target": { + "export": "{{ route_target_export }}", + }, + }, + }, + }, + }, + { + "name": "route_target.import_config", + "getval": re.compile( + r""" + \s+route-target\simport\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "route-target import {{ route_target.import_config}}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "route_target": { + "import_config": "{{ route_target_import_config }}", + }, + }, + }, + }, + }, + { + "name": "route_target.both", + "getval": re.compile( + r""" + \s+route-target\sboth\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "route-target both {{ route_target.both }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "route_target": { + "both": "{{ route_target_both }}", + }, + }, + }, + }, + }, + { + "name": "vnet.tag", + "getval": re.compile( + r""" + \s+vnet\stag\s(?P\d+) + $""", re.VERBOSE, + ), + "setval": "vnet tag {{ vnet.tag }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "vnet": { + "tag": "{{ vnet_tag }}", + }, + }, + }, + }, + }, + { + "name": "vpn.id", + "getval": re.compile( + r""" + \s+vpn\sid\s(?P\S+) + $""", re.VERBOSE, + ), + "setval": "vpn id {{ vpn.id }}", + "result": { + "vrfs": { + '{{ name }}': { + 'name': '{{ name }}', + "vpn": { + "id": "{{ vpn_id }}", + }, + }, + }, + }, + }, + ] + # fmt: on diff --git a/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py b/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py new file mode 100644 index 000000000..3b11effdf --- /dev/null +++ b/plugins/module_utils/network/ios/rm_templates/vrf_interfaces.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Vrf_interfaces parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +class Vrf_interfacesTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Vrf_interfacesTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + "name": "interface", + "getval": re.compile( + r""" + ^interface\s(?P\S+)$""", + re.VERBOSE, + ), + "setval": "interface {{ name }}", + 'result': { + '{{ name }}': { + 'name': '{{ name }}', + }, + }, + "shared": True, + }, + { + "name": "vrf_name", + "getval": re.compile( + r""" + \s*vrf\sforwarding\s(?P\S+)$ + """, + re.VERBOSE, + ), + "setval": "vrf forwarding {{ vrf_name }}", + "result": { + '{{ name }}': { + 'vrf_name': '{{ vrf_name }}', + }, + }, + }, + ] + # fmt: on diff --git a/plugins/module_utils/network/ios/utils/utils.py b/plugins/module_utils/network/ios/utils/utils.py index e014903f1..361c4757e 100644 --- a/plugins/module_utils/network/ios/utils/utils.py +++ b/plugins/module_utils/network/ios/utils/utils.py @@ -324,8 +324,10 @@ def _get_number(name): if_type = "FastEthernet" elif name.lower().startswith("fo"): if_type = "FortyGigabitEthernet" - elif name.lower().startswith("fi"): + elif name.lower().startswith("fiv"): if_type = "FiveGigabitEthernet" + elif name.lower().startswith("fif"): + if_type = "FiftyGigabitEthernet" elif name.lower().startswith("long"): if_type = "LongReachEthernet" elif name.lower().startswith("et"): @@ -460,3 +462,35 @@ def sort_dict(dictionary): else: sorted_dict[key] = value return sorted_dict + + +def generate_switchport_trunk(type, add, vlans_range): + """ + Generates a list of switchport commands based on the trunk type and VLANs range. + Ensures that the length of VLANs lexeme in a command does not exceed 220 characters. + """ + + def append_command(): + command_prefix = f"switchport trunk {type} vlan " + if add or commands: + command_prefix += "add " + commands.append(command_prefix + ",".join(current_chunk)) + + commands = [] + current_chunk = [] + current_length = 0 + + for vrange in vlans_range.split(","): + next_addition = vrange if not current_chunk else "," + vrange + if current_length + len(next_addition) <= 220: + current_chunk.append(vrange) + current_length += len(next_addition) + else: + append_command() + current_chunk = [vrange] + current_length = len(vrange) + + if current_chunk: + append_command() + + return commands diff --git a/plugins/modules/ios_acls.py b/plugins/modules/ios_acls.py index 4636ecb5d..2a261cdbf 100644 --- a/plugins/modules/ios_acls.py +++ b/plugins/modules/ios_acls.py @@ -91,12 +91,6 @@ evaluate: description: Evaluate an access list type: str - fragments: - description: - - Check non-initial fragments. - - This option is DEPRECATED and is replaced with enable_fragments which - accepts bool as input this attribute will be removed after 2024-01-01. - type: str enable_fragments: description: Enable non-initial fragments. type: bool diff --git a/plugins/modules/ios_bgp_address_family.py b/plugins/modules/ios_bgp_address_family.py index e9f770631..44f51604c 100644 --- a/plugins/modules/ios_bgp_address_family.py +++ b/plugins/modules/ios_bgp_address_family.py @@ -98,6 +98,32 @@ auto_summary: description: Enable automatic network number summarization type: bool + maximum_paths: + description: Forward packets over multiple paths + type: dict + suboptions: + paths: + description: Number of paths + type: int + eibgp: + description: Both eBGP and iBGP paths as multipath + type: int + ibgp: + description: iBGP-multipath + type: int + maximum_secondary_paths: + description: Maximum secondary paths + type: dict + suboptions: + paths: + description: Number of secondary paths + type: int + eibgp: + description: Both eBGP and iBGP paths as secondary multipath + type: int + ibgp: + description: iBGP-secondary-multipath + type: int bgp: description: Configure BGP aggregate entries type: dict @@ -669,12 +695,6 @@ allpaths: description: Propagate next hop unchanged for all paths (iBGP and eBGP) to this neighbor type: bool - password: - description: - - Set a password - - This option is DEPRECATED and is replaced with password_options which - accepts dict as input, this attribute will be removed after 2024-06-01. - type: str password_options: description: Set a password with encryption type type: dict diff --git a/plugins/modules/ios_bgp_global.py b/plugins/modules/ios_bgp_global.py index 55ab21341..9ea931db7 100644 --- a/plugins/modules/ios_bgp_global.py +++ b/plugins/modules/ios_bgp_global.py @@ -35,37 +35,6 @@ as_number: description: Autonomous system number type: str - aggregate_address: - description: - - Configure BGP aggregate entry - - This option is DEPRECATED and is replaced with aggregate_addresses which - accepts list of dict as input, this attribute will be removed after 2024-06-01. - type: dict - suboptions: - address: - description: Specify aggregate address - type: str - netmask: - description: Specify aggregate mask - type: str - advertise_map: - description: Set condition to advertise attribute - type: str - as_confed_set: - description: Generate AS confed set path information - type: bool - as_set: - description: Generate AS set path information - type: bool - attribute_map: - description: Set attributes of aggregate - type: str - summary_only: - description: Filter more specific routes from updates - type: bool - suppress_map: - description: Conditionally filter more specific routes from updates - type: str aggregate_addresses: description: Configure BGP aggregate entries type: list @@ -147,40 +116,6 @@ - Change the default as plain notation - asdot notation type: bool - bestpath: - description: - - Change the default bestpath selection - - This option is DEPRECATED and replaced with bestpath_options of type dict, - this attribute will be removed after 2024-06-01. - type: list - elements: dict - suboptions: - aigp: - description: - - if both paths doesn't have aigp ignore on bestpath comparison - - ignore - type: bool - compare_routerid: - description: Compare router-id for identical EBGP paths - type: bool - cost_community: - description: cost community - type: bool - igp_metric: - description: - - igp metric - - Ignore igp metric in bestpath selection - type: bool - med: - description: MED attribute - type: dict - suboptions: - confed: - description: Compare MED among confederation paths - type: bool - missing_as_worst: - description: Treat missing MED as the least preferred one - type: bool bestpath_options: description: - Change the default bestpath selection @@ -390,22 +325,6 @@ - Set Local Preference for Gshut routes - Please refer vendor documentation for valid values type: int - inject_map: - description: - - Routemap which specifies prefixes to inject - - This option is DEPRECATED and is updated with inject_maps which is a - list of dict, this attribute will be removed after 2024-06-01. - type: dict - suboptions: - name: - description: route-map name - type: str - exist_map_name: - description: route-map name - type: str - copy_attributes: - description: Copy attributes from aggregate - type: bool inject_maps: description: Routemap which specifies prefixes to inject type: list @@ -433,18 +352,6 @@ description: Subnet network range type: dict suboptions: - ipv4_with_subnet: - description: - - IPv4 subnet range(A.B.C.D/nn) - - This option is DEPRECATED and is updated with host_with_subnet which is a - common attribute for address, this attribute will be removed after 2024-06-01. - type: str - ipv6_with_subnet: - description: - - IPv6 subnet range(X:X:X:X::X/<0-128>) - - This option is DEPRECATED and is updated with host_with_subnet which is a - common attribute for address attribute will be removed after 2024-06-01. - type: str host_with_subnet: description: - IPv4 subnet range(A.B.C.D/nn) @@ -490,36 +397,6 @@ enable: description: Enable nexthop tracking type: bool - nopeerup_delay: - description: - - Set how long BGP will wait for the first peer to come up before beginning the update delay or - graceful restart timers (in seconds) - - This option is DEPRECATED and is replaced with nopeerup_delay_options which is of type dict, - this attribute will be removed after 2024-06-01. - type: list - elements: dict - suboptions: - cold_boot: - description: - - How long to wait for the first peer to come up upon a cold boot - - Please refer vendor documentation for valid values - type: int - nsf_switchover: - description: - - How long to wait for the first peer, post NSF switchover - - Please refer vendor documentation for valid values - type: int - post_boot: - description: - - How long to wait for the first peer to come up once the system is already - booted and all peers go down - - Please refer vendor documentation for valid values - type: int - user_initiated: - description: - - How long to wait for the first peer, post a manual clear of BGP peers by the admin user - - Please refer vendor documentation for valid values - type: int nopeerup_delay_options: description: Set how long BGP will wait for the first peer to come up before beginning the update delay or @@ -805,25 +682,6 @@ interface: description: interface details type: str - distribute_list: - description: - - Filter networks in routing updates - - This option is DEPRECATED and is replaced with distributes which is of type list of dict, - this attribute will be removed after 2024-06-01. - type: dict - suboptions: - acl: - description: IP access list number/name - type: str - in: - description: Filter incoming routing updates - type: bool - out: - description: Filter outgoing routing updates - type: bool - interface: - description: interface details - type: str maximum_paths: description: Forward packets over multiple paths type: dict @@ -863,24 +721,6 @@ - Neighbor tag - Neighbor ipv6 address (X:X:X:X::X) type: str - address: - description: - - Neighbor address (A.B.C.D) - - This option is DEPRECATED and replaced with neighbor_address, - this attribute will be removed after 2024-06-01. - type: str - tag: - description: - - Neighbor tag - - This option is DEPRECATED and replaced with neighbor_address, - this attribute will be removed after 2024-06-01. - type: str - ipv6_adddress: - description: - - Neighbor ipv6 address (X:X:X:X::X) - - This option is DEPRECATED and replaced with neighbor_address, - this attribute will be removed after 2024-06-01. - type: str activate: description: Enable the Address Family for this Neighbor type: bool @@ -1194,12 +1034,6 @@ allpaths: description: Propagate next hop unchanged for all paths (iBGP and eBGP) to this neighbor type: bool - password: - description: - - Set a password - - This option is DEPRECATED and is replaced with password_options which - accepts dict as input, this attribute will be removed after 2024-06-01. - type: str password_options: description: Set a password with encryption type type: dict @@ -1287,22 +1121,6 @@ replace_as: description: Replace all private AS numbers with local AS type: bool - route_map: - description: - - Apply route map to neighbor - - This option is DEPRECATED and is replaced with route_maps which - accepts list of dict as input, this attribute will be removed after 2024-06-01. - type: dict - suboptions: - name: - description: Replace all private AS numbers with local AS - type: str - in: - description: Apply map to incoming routes - type: bool - out: - description: Apply map to outbound routes - type: bool route_maps: description: Apply a list of route maps to neighbor type: list @@ -1750,51 +1568,6 @@ global: description: global VRF type: bool - route_server_context: - description: - - Enter route server context command mode - - This option is DEPRECATED as it is out of scope of the module, - this attribute will be removed after 2024-06-01. - type: dict - suboptions: - name: - description: Name of route server context - type: str - address_family: - description: Enter address family command mode - type: dict - suboptions: - afi: - description: Address family - type: str - choices: ["ipv4", "ipv6"] - modifier: - description: Address Family modifier - type: str - choices: ["multicast", "unicast"] - import_map: - description: - - Import matching routes using a route map - - Name of route map - type: str - description: - description: Textual description of the router server context - type: str - scope: - description: - - Enter scope command mode - - This option is DEPRECATED as is not valid within the scope of module, - this attribute will be removed after 2024-06-01. - type: dict - suboptions: - global: - description: Global scope - type: bool - vrf: - description: - - VRF scope - - VPN Routing/Forwarding instance name - type: str synchronization: description: Perform IGP synchronization type: bool diff --git a/plugins/modules/ios_facts.py b/plugins/modules/ios_facts.py index 93b7b446a..2c57a06d0 100644 --- a/plugins/modules/ios_facts.py +++ b/plugins/modules/ios_facts.py @@ -177,11 +177,11 @@ returned: when hardware is configured type: dict ansible_net_memfree_mb: - description: The available free memory on the remote device in Mb + description: The available free memory on the remote device in MiB returned: when hardware is configured type: int ansible_net_memtotal_mb: - description: The total memory on the remote device in Mb + description: The total memory on the remote device in MiB returned: when hardware is configured type: int ansible_net_cpu_utilization: diff --git a/plugins/modules/ios_linkagg.py b/plugins/modules/ios_linkagg.py deleted file mode 100644 index 01267865f..000000000 --- a/plugins/modules/ios_linkagg.py +++ /dev/null @@ -1,353 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# -from __future__ import absolute_import, division, print_function - - -__metaclass__ = type - -DOCUMENTATION = r""" -module: ios_linkagg -author: Trishna Guha (@trishnaguha) -short_description: Module to configure link aggregation groups. -deprecated: - alternative: ios_lag_interfaces - why: Updated modules released with more functionality. - removed_at_date: "2024-06-01" -description: - - This module provides declarative management of link aggregation groups on Cisco - IOS network devices. -version_added: 1.0.0 -notes: - - Tested against IOS 15.2 - - This module works with connection C(network_cli). - See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) -options: - group: - description: - - Channel-group number for the port-channel Link aggregation group. Range 1-255. - type: int - mode: - description: - - Mode of the link aggregation group. - - On mode has to be quoted as 'on' or else pyyaml will convert - to True before it gets to Ansible. - choices: - - active - - "on" - - passive - - auto - - desirable - type: str - members: - description: - - List of members of the link aggregation group. - type: list - elements: str - aggregate: - description: List of link aggregation definitions. - type: list - elements: dict - suboptions: - group: - description: - - Channel-group number for the port-channel Link aggregation group. Range 1-255. - type: str - required: true - mode: - description: - - Mode of the link aggregation group. - - On mode has to be quoted as 'on' or else pyyaml will convert - to True before it gets to Ansible. - choices: - - active - - "on" - - passive - - auto - - desirable - type: str - members: - description: - - List of members of the link aggregation group. - type: list - elements: str - state: - description: - - State of the link aggregation group. - choices: - - present - - absent - type: str - state: - description: - - State of the link aggregation group. - default: present - choices: - - present - - absent - type: str - purge: - description: - - Purge links not defined in the I(aggregate) parameter. - default: false - type: bool -extends_documentation_fragment: - - cisco.ios.ios -""" - -EXAMPLES = """ -- name: Create link aggregation group - cisco.ios.ios_linkagg: - group: 10 - state: present - -- name: Delete link aggregation group - cisco.ios.ios_linkagg: - group: 10 - state: absent - -- name: Set link aggregation group to members - cisco.ios.ios_linkagg: - group: 200 - mode: active - members: - - GigabitEthernet0/0 - - GigabitEthernet0/1 - -- name: Remove link aggregation group from GigabitEthernet0/0 - cisco.ios.ios_linkagg: - group: 200 - mode: active - members: - - GigabitEthernet0/1 - -- name: Create aggregate of linkagg definitions - cisco.ios.ios_linkagg: - aggregate: - - { group: 3, mode: "on", members: [GigabitEthernet0/1] } - - { group: 100, mode: passive, members: [GigabitEthernet0/2] } -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to manage the device. - type: list - sample: - - interface port-channel 30 - - interface GigabitEthernet0/3 - - channel-group 30 mode on - - no interface port-channel 30 -""" -import re - -from copy import deepcopy - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( - CustomNetworkConfig, -) -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - remove_default_spec, -) - -from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( - get_config, - load_config, -) - - -def search_obj_in_list(group, lst): - for o in lst: - if o["group"] == group: - return o - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - purge = module.params["purge"] - for w in want: - group = w["group"] - mode = w["mode"] - members = w.get("members") or [] - state = w["state"] - del w["state"] - obj_in_have = search_obj_in_list(group, have) - if state == "absent": - if obj_in_have: - commands.append("no interface port-channel {0}".format(group)) - elif state == "present": - cmd = ["interface port-channel {0}".format(group), "end"] - if not obj_in_have: - if not group: - module.fail_json(msg="group is a required option") - commands.extend(cmd) - if members: - for m in members: - commands.append("interface {0}".format(m)) - commands.append("channel-group {0} mode {1}".format(group, mode)) - elif members: - if "members" not in obj_in_have.keys(): - for m in members: - commands.extend(cmd) - commands.append("interface {0}".format(m)) - commands.append("channel-group {0} mode {1}".format(group, mode)) - elif set(members) != set(obj_in_have["members"]): - missing_members = list(set(members) - set(obj_in_have["members"])) - for m in missing_members: - commands.extend(cmd) - commands.append("interface {0}".format(m)) - commands.append("channel-group {0} mode {1}".format(group, mode)) - superfluous_members = list(set(obj_in_have["members"]) - set(members)) - for m in superfluous_members: - commands.extend(cmd) - commands.append("interface {0}".format(m)) - commands.append("no channel-group {0} mode {1}".format(group, mode)) - if purge: - for h in have: - obj_in_want = search_obj_in_list(h["group"], want) - if not obj_in_want: - commands.append("no interface port-channel {0}".format(h["group"])) - return commands - - -def map_params_to_obj(module): - obj = [] - aggregate = module.params.get("aggregate") - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - d = item.copy() - d["group"] = str(d["group"]) - obj.append(d) - else: - obj.append( - { - "group": str(module.params["group"]), - "mode": module.params["mode"], - "members": module.params["members"], - "state": module.params["state"], - }, - ) - return obj - - -def parse_mode(module, config, group, member): - mode = None - netcfg = CustomNetworkConfig(indent=1, contents=config) - parents = ["interface {0}".format(member)] - body = netcfg.get_section(parents) - match_int = re.findall("interface {0}\\n".format(member), body, re.M) - if match_int: - match = re.search("channel-group {0} mode (\\S+)".format(group), body, re.M) - if match: - mode = match.group(1) - return mode - - -def parse_members(module, config, group): - members = [] - for line in config.strip().split("!"): - lineStrip = line.strip() - if lineStrip.startswith("interface"): - match_group = re.findall("channel-group {0} mode".format(group), lineStrip, re.M) - if match_group: - match = re.search("interface (\\S+)", lineStrip, re.M) - if match: - members.append(match.group(1)) - return members - - -def get_channel(module, config, group): - match = re.findall("^interface (\\S+)", config, re.M) - if not match: - return {} - channel = {} - for item in set(match): - member = item - channel["mode"] = parse_mode(module, config, group, member) - channel["members"] = parse_members(module, config, group) - return channel - - -def map_config_to_obj(module): - objs = list() - config = get_config(module) - for line in config.split("\n"): - lStrip = line.strip() - match = re.search("interface Port-channel(\\S+)", lStrip, re.M) - if match: - obj = {} - group = match.group(1) - obj["group"] = group - obj.update(get_channel(module, config, group)) - objs.append(obj) - return objs - - -def main(): - """main entry point for module execution""" - element_spec = dict( - group=dict(type="int"), - mode=dict(choices=["active", "on", "passive", "auto", "desirable"]), - members=dict(type="list", elements="str"), - state=dict(default="present", choices=["present", "absent"]), - ) - aggregate_spec = deepcopy(element_spec) - aggregate_spec["group"] = dict(required=True) - required_one_of = [["group", "aggregate"]] - required_together = [["members", "mode"]] - mutually_exclusive = [["group", "aggregate"]] - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - argument_spec = dict( - aggregate=dict( - type="list", - elements="dict", - options=aggregate_spec, - required_together=required_together, - ), - purge=dict(default=False, type="bool"), - ) - argument_spec.update(element_spec) - module = AnsibleModule( - argument_spec=argument_spec, - required_one_of=required_one_of, - required_together=required_together, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True, - ) - warnings = list() - result = {"changed": False} - if warnings: - result["warnings"] = warnings - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands((want, have), module) - result["commands"] = commands - if commands: - if not module.check_mode: - load_config(module, commands) - result["changed"] = True - module.exit_json(**result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/ios_lldp.py b/plugins/modules/ios_lldp.py deleted file mode 100644 index 5d3f53380..000000000 --- a/plugins/modules/ios_lldp.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# -from __future__ import absolute_import, division, print_function - - -__metaclass__ = type - -DOCUMENTATION = """ -module: ios_lldp -author: Ganesh Nalawade (@ganeshrn) -short_description: (deprecated, removed after 2024-06-01) Manage LLDP configuration on Cisco IOS network devices. -description: - - This module provides declarative management of LLDP service on Cisco IOS network - devices. -version_added: 1.0.0 -deprecated: - alternative: ios_lldp_global - why: Newer and updated modules released with more functionality. - removed_at_date: "2024-06-01" -notes: - - Tested against IOS 15.2 - - This module works with connection C(network_cli). - See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) -options: - state: - description: - - State of the LLDP configuration. If value is I(present) lldp will be enabled - else if it is I(absent) it will be disabled. - default: present - choices: - - present - - absent - - enabled - - disabled - type: str -extends_documentation_fragment: - - cisco.ios.ios -""" - -EXAMPLES = """ -- name: Enable LLDP service - cisco.ios.ios_lldp: - state: present - -- name: Disable LLDP service - cisco.ios.ios_lldp: - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to manage the device. - type: list - sample: - - lldp run -""" -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( - load_config, - run_commands, -) - - -def has_lldp(module): - output = run_commands(module, ["show lldp"]) - is_lldp_enable = False - if len(output) > 0 and "LLDP is not enabled" not in output[0]: - is_lldp_enable = True - return is_lldp_enable - - -def main(): - """main entry point for module execution""" - argument_spec = dict( - state=dict(default="present", choices=["present", "absent", "enabled", "disabled"]), - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - warnings = list() - result = {"changed": False} - if warnings: - result["warnings"] = warnings - HAS_LLDP = has_lldp(module) - commands = [] - if module.params["state"] == "absent" and HAS_LLDP: - commands.append("no lldp run") - elif module.params["state"] == "present" and not HAS_LLDP: - commands.append("lldp run") - result["commands"] = commands - if commands: - if not module.check_mode: - load_config(module, commands) - result["changed"] = True - module.exit_json(**result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/ios_logging_global.py b/plugins/modules/ios_logging_global.py index aab889772..4a78dbb5f 100644 --- a/plugins/modules/ios_logging_global.py +++ b/plugins/modules/ios_logging_global.py @@ -38,7 +38,10 @@ description: Logging buffer size type: int severity: &severity - description: Logging severity level + description: + - Logging severity level + - Idempotency is not maintained for this attribute for the value 'debugging' + since this value is not shown in running-config. type: str choices: &severity_subgroup - alerts diff --git a/plugins/modules/ios_vlans.py b/plugins/modules/ios_vlans.py index de9a0212a..f3dd3ecbc 100644 --- a/plugins/modules/ios_vlans.py +++ b/plugins/modules/ios_vlans.py @@ -75,7 +75,11 @@ type: bool shutdown: description: - - Shutdown VLAN switching. + - Specifies whether VLAN switching should be administratively enabled or disabled. + - When set to C(enabled), the VLAN interface is administratively shut down, which prevents it from forwarding traffic. + - When set to C(disabled), the VLAN interface is administratively enabled by issuing the internal C(no shutdown) command, + allowing it to forward traffic. + - The operational state of the VLAN depends on both the administrative state (C(shutdown) or C(no shutdown)) and the physical link status. type: str choices: - enabled @@ -266,100 +270,330 @@ # Before state: # ------------- # -# vios_l2#show vlan +# S1#show vlan + # VLAN Name Status Ports # ---- -------------------------------- --------- ------------------------------- -# 1 default active Gi0/1, Gi0/2 -# 10 vlan_10 active -# 20 vlan_20 act/lshut -# 30 vlan_30 sus/lshut +# 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 +# 10 Vlan_10 active +# 20 Vlan_20 active +# 30 Vlan_30 suspended +# 44 Vlan_44 suspended # 1002 fddi-default act/unsup # 1003 token-ring-default act/unsup # 1004 fddinet-default act/unsup # 1005 trnet-default act/unsup -# + # VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 # ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ # 1 enet 100001 1500 - - - - - 0 0 # 10 enet 100010 1500 - - - - - 0 0 # 20 enet 100020 610 - - - - - 0 0 # 30 enet 100030 1500 - - - - - 0 0 +# 44 enet 100044 1500 - - - - - 0 0 # 1002 fddi 101002 1500 - - - - - 0 0 # 1003 tr 101003 1500 - - - - - 0 0 # 1004 fdnet 101004 1500 - - - ieee - 0 0 # 1005 trnet 101005 1500 - - - ibm - 0 0 -# + # Remote SPAN VLANs # ------------------------------------------------------------------------------ -# 10 + + +# Primary Secondary Type Ports +# ------- --------- ----------------- ------------------------------------------ +# S1# + - name: Override device configuration of all VLANs with provided configuration cisco.ios.ios_vlans: config: - - name: Vlan_10 - vlan_id: 10 - mtu: 1000 + - name: Vlan_2020 + state: active + vlan_id: 20 + shutdown: disabled state: overridden +# Task output: +# ------------ + +# after: +# - mtu: 1500 +# name: default +# shutdown: disabled +# state: active +# vlan_id: 1 +# - mtu: 1500 +# name: Vlan_2020 +# shutdown: disabled +# state: active +# vlan_id: 20 +# - mtu: 1500 +# name: fddi-default +# shutdown: enabled +# state: active +# vlan_id: 1002 +# - mtu: 1500 +# name: token-ring-default +# shutdown: enabled +# state: active +# vlan_id: 1003 +# - mtu: 1500 +# name: fddinet-default +# shutdown: enabled +# state: active +# vlan_id: 1004 +# - mtu: 1500 +# name: trnet-default +# shutdown: enabled +# state: active +# vlan_id: 1005 + +# before: +# - mtu: 1500 +# name: default +# shutdown: disabled +# state: active +# vlan_id: 1 +# - mtu: 1500 +# name: Vlan_10 +# shutdown: disabled +# state: active +# vlan_id: 10 +# - mtu: 610 +# name: Vlan_20 +# shutdown: disabled +# state: active +# vlan_id: 20 +# - mtu: 1500 +# name: Vlan_30 +# shutdown: disabled +# state: suspend +# vlan_id: 30 +# - mtu: 1500 +# name: Vlan_44 +# shutdown: disabled +# state: suspend +# vlan_id: 44 +# - mtu: 1500 +# name: fddi-default +# shutdown: enabled +# state: active +# vlan_id: 1002 +# - mtu: 1500 +# name: token-ring-default +# shutdown: enabled +# state: active +# vlan_id: 1003 +# - mtu: 1500 +# name: fddinet-default +# shutdown: enabled +# state: active +# vlan_id: 1004 +# - mtu: 1500 +# name: trnet-default +# shutdown: enabled +# state: active +# vlan_id: 1005 + +# commands: +# - no vlan 1 +# - no vlan 10 +# - no vlan 30 +# - no vlan 44 +# - no vlan 1002 +# - no vlan 1003 +# - no vlan 1004 +# - no vlan 1005 +# - vlan 20 +# - name Vlan_2020 +# - no mtu 610 +# - no vlan configuration 1 +# - no vlan configuration 10 +# - no vlan configuration 30 +# - no vlan configuration 44 +# - no vlan configuration 1002 +# - no vlan configuration 1003 +# - no vlan configuration 1004 +# - no vlan configuration 1005 + # After state: # ------------ # # vios_l2#show vlan # VLAN Name Status Ports # ---- -------------------------------- --------- ------------------------------- -# 1 default active Gi0/1, Gi0/2 -# 10 Vlan_10 active +# 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 +# 20 Vlan_2020 active # 1002 fddi-default act/unsup # 1003 token-ring-default act/unsup # 1004 fddinet-default act/unsup # 1005 trnet-default act/unsup -# + # VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 # ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ # 1 enet 100001 1500 - - - - - 0 0 -# 10 enet 100010 1000 - - - - - 0 0 +# 20 enet 100020 1500 - - - - - 0 0 # 1002 fddi 101002 1500 - - - - - 0 0 # 1003 tr 101003 1500 - - - - - 0 0 # 1004 fdnet 101004 1500 - - - ieee - 0 0 # 1005 trnet 101005 1500 - - - ibm - 0 0 +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ -# Using overridden + +# Primary Secondary Type Ports +# ------- --------- ----------------- ------------------------------------------ + +# Using purged # Before state: # ------------- # -# Leaf-01#show run nve | sec ^vlan configuration -# vlan configuration 101 -# member evpn-instance 101 vni 10101 -# vlan configuration 102 -# member evpn-instance 102 vni 10102 -# vlan configuration 201 -# member evpn-instance 201 vni 10201 -# vlan configuration 901 -# member vni 50901 +# S1#show vlan -- name: Override device configuration of all VLANs with provided configuration +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 +# 20 Vlan_2020 active +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup + +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 20 enet 100020 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 + +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ + + +# Primary Secondary Type Ports +# ------- --------- ----------------- ------------------------------------------ + +# S1#show running-config | section ^vlan configuration .+ +# vlan configuration 20 + + +- name: Purge all vlans configuration cisco.ios.ios_vlans: config: - - vlan_id: 101 - member: - vni: 10102 - evi: 102 - - vlan_id: 102 - member: - vni: 10101 - evi: 101 - state: overridden + state: purged + +# Task output: +# ------------ + +# after: +# - mtu: 1500 +# name: default +# shutdown: disabled +# state: active +# vlan_id: 1 +# - mtu: 1500 +# name: fddi-default +# shutdown: enabled +# state: active +# vlan_id: 1002 +# - mtu: 1500 +# name: token-ring-default +# shutdown: enabled +# state: active +# vlan_id: 1003 +# - mtu: 1500 +# name: fddinet-default +# shutdown: enabled +# state: active +# vlan_id: 1004 +# - mtu: 1500 +# name: trnet-default +# shutdown: enabled +# state: active +# vlan_id: 1005 + +# before: +# - mtu: 1500 +# name: default +# shutdown: disabled +# state: active +# vlan_id: 1 +# - mtu: 1500 +# name: Vlan_2020 +# shutdown: disabled +# state: active +# vlan_id: 20 +# - mtu: 1500 +# name: fddi-default +# shutdown: enabled +# state: active +# vlan_id: 1002 +# - mtu: 1500 +# name: token-ring-default +# shutdown: enabled +# state: active +# vlan_id: 1003 +# - mtu: 1500 +# name: fddinet-default +# shutdown: enabled +# state: active +# vlan_id: 1004 +# - mtu: 1500 +# name: trnet-default +# shutdown: enabled +# state: active +# vlan_id: 1005 + +# commands: +# - no vlan 1 +# - no vlan 20 +# - no vlan 1002 +# - no vlan 1003 +# - no vlan 1004 +# - no vlan 1005 +# - no vlan configuration 1 +# - no vlan configuration 20 +# - no vlan configuration 1002 +# - no vlan configuration 1003 +# - no vlan configuration 1004 +# - no vlan configuration 1005 # After state: # ------------ # -# Leaf-01#show run nve | sec ^vlan configuration -# vlan configuration 101 -# member evpn-instance 102 vni 10102 -# vlan configuration 102 -# member evpn-instance 101 vni 10101 +# S1#show vlan + +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup + +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 + +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ + + +# Primary Secondary Type Ports +# ------- --------- ----------------- ------------------------------------------ + +# S1#show running-config | section ^vlan configuration .+ +# S1# + # Using replaced diff --git a/plugins/modules/ios_vrf_global.py b/plugins/modules/ios_vrf_global.py new file mode 100644 index 000000000..5b737c6f1 --- /dev/null +++ b/plugins/modules/ios_vrf_global.py @@ -0,0 +1,892 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2024 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_vrf_global +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_vrf_global +short_description: Resource module to configure global VRF definitions. +description: This module provides declarative management of VRF definitions on Cisco IOS. +version_added: 8.0.0 +author: Ruchi Pakhle (@Ruchip16) +notes: + - Tested against Cisco IOS-XE version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) + - The module examples uses callback plugin (stdout_callback = yaml) to generate task + output in yaml format. +options: + config: + description: A dictionary containing device configurations for VRF, including a list of VRF definitions. + type: dict + suboptions: + vrfs: + description: List of VRF definitions. + type: list + elements: dict + suboptions: + name: + description: Name of the VRF. + type: str + required: true + description: + description: VRF specific description + type: str + ipv4: + description: VRF IPv4 configuration + type: dict + suboptions: + multicast: + description: IPv4 multicast configuration + type: dict + suboptions: + multitopology: + description: Enable multicast-specific topology + type: bool + ipv6: + description: VRF IPv6 configuration + type: dict + suboptions: + multicast: + description: IPv6 multicast configuration + type: dict + suboptions: + multitopology: + description: Enable multicast-specific topology + type: bool + rd: + description: Specify route distinguisher (RD). + type: str + route_target: + description: Specify target VPN extended configurations. + type: dict + suboptions: + export: + description: Export target-VPN configuration. + type: str + import_config: + description: Import target-VPN configuration. + type: str + both: + description: Both export and import target-VPN configuration. + type: str + vnet: + description: Virtual networking configuration. + type: dict + suboptions: + tag: + description: Identifier used to tag packets associated with this VNET. + type: int + vpn: + description: Configure vpn-id for the VRF as specified in RFC 2685. + type: dict + suboptions: + id: + description: Configure vpn-id in RFC 2685 format. + type: str + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by + executing the command B(show running-config | section ^vrf). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + choices: [parsed, gathered, deleted, merged, replaced, rendered, overridden, purged] + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config | section vrf). + connection to remote host is not required. + - The state I(deleted) only removes the VRF attributes that this module + manages and does not negate the VRF completely. Thereby, preserving + address-family related configurations under VRF context. + - The state I(purged) removes all the VRF definitions from the + target device. Use caution with this state. + - Refer to examples for more details. + type: str +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# admin#show running-config | section ^vrf + +- name: Merge provided configuration with device configuration + cisco.ios.ios_vrf_global: + config: + vrfs: + - name: VRF2 + description: This is a test VRF for merged state + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "2:3" + route_target: + export: "192.0.2.0:100" + import_config: "192.0.2.3:200" + vpn: + id: "2:45" + vnet: + tag: 200 + state: merged + +# Task output +# ------------- +# +# before: {} +# +# commands: +# - vrf definition VRF2 +# - description This is a test VRF for merged state +# - ipv4 multicast multitopology +# - ipv6 multicast multitopology +# - rd 2:3 +# - route-target export 192.0.2.0:100 +# - route-target import 192.0.2.3:200 +# - vnet tag 200 +# - vpn id 2:45 +# +# after: +# - name: VRF2 +# description: This is a test VRF for merged state +# ipv4: +# multicast: +# multitopology: true +# ipv6: +# multicast: +# multitopology: true +# rd: "2:3" +# route_target: +# export: "192.0.2.0:100" +# import_config: "192.0.2.3:200" +# vnet: +# tag: 200 +# vpn: +# id: "2:45" + +# After state: +# ------------- +# +# admin#show running-config | section ^vrf +# vrf definition VRF2 +# vnet tag 200 +# description This is a test VRF for merged state +# ipv4 multicast multitopology +# ipv6 multicast multitopology +# rd 2:3 +# vpn id 2:45 +# route-target export 192.0.2.0:100 +# route-target import 192.0.2.3:200 + +# Using replaced + +# Before state: +# ------------- +# +# admin#show running-config | section ^vrf +# vrf definition VRF2 +# vnet tag 200 +# description This is a test VRF for merged state +# ipv4 multicast multitopology +# ipv6 multicast multitopology +# rd 2:3 +# vpn id 2:45 +# route-target export 192.0.2.0:100 +# route-target import 192.0.2.3:200 + +- name: Replace the provided configuration with the existing running configuration + cisco.ios.ios_vrf_global: + config: + vrfs: + - name: VRF7 + description: VRF7 description + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "7:8" + route_target: + export: "198.51.100.112:500" + import_config: "192.0.2.4:400" + vpn: + id: "5:45" + vnet: + tag: 300 + state: replaced + +# Task Output: +# ------------ +# +# before: +# - name: VRF2 +# description: This is a test VRF for merged state +# ipv4: +# multicast: +# multitopology: true +# ipv6: +# multicast: +# multitopology: true +# rd: "2:3" +# route_target: +# export: "192.0.2.0:100" +# import_config: "192.0.2.3:200" +# vnet: +# tag: 200 +# vpn: +# id: "2:45" +# +# commands: +# - vrf definition VRF7 +# - description VRF7 description +# - ipv4 multicast multitopology +# - ipv6 multicast multitopology +# - rd 7:8 +# - route-target export 198.51.100.112:500 +# - route-target import 192.0.2.4:400 +# - vnet tag 300 +# - vpn id 5:45 +# +# after: +# - name: VRF2 +# description: This is a test VRF for merged state +# ipv4: +# multicast: +# multitopology: true +# ipv6: +# multicast: +# multitopology: true +# rd: "2:3" +# route_target: +# export: "192.0.2.0:100" +# import_config: "192.0.2.3:200" +# vnet: +# tag: 200 +# vpn: +# id: "2:45 +# - name: VRF7 +# description: VRF7 description +# ipv4: +# multicast: +# multitopology: true +# ipv6: +# multicast: +# multitopology: true +# rd: "7:8" +# route_target: +# export: "198.51.100.112:500" +# import_config: "192.0.2.4:400" +# vnet: +# tag: 300 +# vpn: +# id: "5:45" +# +# After state: +# ------------- +# +# admin#show running-config | section ^vrf +# vrf definition VRF2 +# vnet tag 200 +# description This is a test VRF for merged state +# ipv4 multicast multitopology +# ipv6 multicast multitopology +# rd 2:3 +# vpn id 2:45 +# route-target export 192.0.2.0:100 +# route-target import 192.0.2.3:200 +# vrf definition VRF7 +# vnet tag 300 +# description VRF7 description +# ipv4 multicast multitopology +# ipv6 multicast multitopology +# rd 7:8 +# route-target export 198.51.100.112:500 +# route-target import 192.0.2.4:400 +# vpn id 5:45 + +# Using Overridden + +# Before state: +# ------------- +# +# admin#show running-config | section ^vrf +# vrf definition VRF2 +# vnet tag 200 +# description This is a test VRF for merged state +# ipv4 multicast multitopology +# ipv6 multicast multitopology +# rd 2:3 +# vpn id 2:45 +# route-target export 192.0.2.0:100 +# route-target import 192.0.2.3:200 +# vrf definition VRF7 +# vnet tag 300 +# description VRF7 description +# ipv4 multicast multitopology +# ipv6 multicast multitopology +# rd 7:8 +# route-target export 198.51.100.112:500 +# route-target import 192.0.2.4:400 +# vpn id 5:45 + +- name: Override the provided configuration with the existing running configuration + cisco.ios.ios_vrf_global: + config: + vrfs: + - name: VRF6 + description: VRF6 description + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "6:7" + route_target: + export: "198.51.0.2:400" + import_config: "198.51.0.5:200" + vpn: + id: "4:5" + vnet: + tag: 500 + state: overridden + +# Task Output: +# ------------ +# +# before: +# - name: VRF2 +# description: This is a test VRF for merged state +# ipv4: +# multicast: +# multitopology: true +# ipv6: +# multicast: +# multitopology: true +# rd: "2:3" +# route_target: +# export: "192.0.2.0:100" +# import_config: "192.0.2.3:200" +# vnet: +# tag: 200 +# vpn: +# id: "2:45 +# - name: VRF7 +# description: VRF7 description +# ipv4: +# multicast: +# multitopology: true +# ipv6: +# multicast: +# multitopology: true +# rd: "7:8" +# route_target: +# export: "198.51.100.112:500" +# import_config: "192.0.2.4:400" +# vnet: +# tag: 300 +# vpn: +# id: "5:45" +# +# commands: +# - vrf definition VRF2 +# - no description This is a test VRF for merged state +# - no ipv4 multicast multitopology +# - no ipv6 multicast multitopology +# - no rd 2:3 +# - no route-target export 192.0.2.0:100 +# - no route-target import 192.0.2.3:200 +# - no vnet tag 200 +# - no vpn id 2:45 +# - vrf definition VRF7 +# - no description VRF7 description +# - no ipv4 multicast multitopology +# - no ipv6 multicast multitopology +# - no rd 7:8 +# - no route-target export 198.51.100.112:500 +# - no route-target import 192.0.2.4:400 +# - no vnet tag 300 +# - no vpn id 5:45 +# - vrf definition VRF6 +# - description VRF6 description +# - ipv4 multicast multitopology +# - ipv6 multicast multitopology +# - rd 6:7 +# - route-target export 198.51.0.2:400 +# - route-target import 198.51.0.5:200 +# - vnet tag 500 +# - vpn id 4:5 +# +# after: +# - name: VRF2 +# - name: VRF6 +# description: VRF6 description +# ipv4: +# multicast: +# multitopology: true +# ipv6: +# multicast: +# multitopology: true +# rd: "6:7" +# route_target: +# export: "198.51.0.2:400" +# import_config: "198.51.0.5:200" +# vnet: +# tag: 500 +# vpn: +# id: "4:5 +# - name: VRF7 + +# After state: +# ------------ +# +# admin#show running-config | section ^vrf +# vrf definition VRF2 +# vrf definition VRF6 +# vnet tag 500 +# description VRF6 description +# ipv4 multicast multitopology +# ipv6 multicast multitopology +# rd 6:7 +# vpn id 4:5 +# route-target export 198.51.0.2:400 +# route-target import 198.51.0.5:200 +# vrf definition VRF7 + +# Using Deleted + +# Before state: +# ------------- +# +# admin#show running-config | section ^vrf +# vrf definition VRF2 +# vrf definition VRF6 +# vnet tag 500 +# description VRF6 description +# ipv4 multicast multitopology +# ipv6 multicast multitopology +# rd 6:7 +# vpn id 4:5 +# route-target export 198.51.0.2:400 +# route-target import 198.51.0.5:200 +# vrf definition VRF7 + +- name: Delete the provided configuration when config is given + cisco.ios.ios_vrf_global: + config: + vrfs: + - name: VRF2 + - name: VRF6 + - name: VRF7 + state: deleted + +# Task Output: +# ------------ +# +# before: +# - name: VRF2 +# - name: VRF6 +# description: VRF6 description +# ipv4: +# multicast: +# multitopology: true +# ipv6: +# multicast: +# multitopology: true +# rd: "6:7" +# route_target: +# export: "198.51.0.2:400" +# import_config: "198.51.0.5:200" +# vnet: +# tag: 500 +# vpn: +# id: "4:5" +# - name: VRF7 +# +# commands: +# - vrf definition VRF2 +# - vrf definition VRF6 +# - no description VRF6 description +# - no ipv4 multicast multitopology +# - no ipv6 multicast multitopology +# - no rd 6:7 +# - no route-target export 198.51.0.2:400 +# - no route-target import 198.51.0.5:200 +# - no vnet tag 500 +# - no vpn id 4:5 +# - vrf definition VRF7 +# +# after: +# - name: VRF2 +# - name: VRF6 +# - name: VRF7 + +# After state: +# ------------- +# admin#show running-config | section ^vrf +# vrf definition VRF2 +# vrf definition VRF6 +# vrf definition VRF7 + +# Using Deleted with empty config + +# Before state: +# ------------- +# +# admin#show running-config | section ^vrf +# vrf definition VRF2 +# vrf definition VRF6 +# vnet tag 500 +# description VRF6 description +# ipv4 multicast multitopology +# ipv6 multicast multitopology +# rd 6:7 +# vpn id 4:5 +# route-target export 198.51.0.2:400 +# route-target import 198.51.0.5:200 +# vrf definition VRF7 + +- name: Delete the provided configuration when config is empty + cisco.ios.ios_vrf_global: + config: + state: deleted + +# Task Output: +# ------------ +# +# before: +# - name: VRF2 +# - name: VRF6 +# description: VRF6 description +# ipv4: +# multicast: +# multitopology: true +# ipv6: +# multicast: +# multitopology: true +# rd: "6:7" +# route_target: +# export: "198.51.0.2:400" +# import_config: "198.51.0.5:200" +# vnet: +# tag: 500 +# vpn: +# id: "4:5" +# - name: VRF7 + +# commands: +# - vrf definition VRF2 +# - vrf definition VRF6 +# - no description VRF6 description +# - no ipv4 multicast multitopology +# - no ipv6 multicast multitopology +# - no rd 6:7 +# - no route-target export 198.51.0.2:400 +# - no route-target import 198.51.0.5:200 +# - no vnet tag 500 +# - no vpn id 4:5 +# - vrf definition VRF7 +# +# after: +# - name: VRF2 +# - name: VRF6 +# - name: VRF7 + +# After state: +# ------------- +# +# admin#show running-config | section ^vrf +# vrf definition VRF2 +# vrf definition VRF6 +# vrf definition VRF7 + +# Using purged - would delete all the VRF definitions + +# Before state: +# ------------- +# +# admin#show running-config | section ^vrf +# vrf definition VRF2 +# vrf definition VRF6 +# vrf definition VRF7 + +- name: Purge all the configuration from the device + cisco.ios.ios_vrf_global: + state: purged + +# Task Output: +# ------------ +# +# before: +# - name: VRF2 +# - name: VRF6 +# - name: VRF7 +# commands: +# - no vrf definition VRF2 +# - no vrf definition VRF6 +# - no vrf definition VRF7 +# after: {} + +# After state: +# ------------- +# +# admin#show running-config | section ^vrf + +# Using Rendered + +- name: Render provided configuration with device configuration + cisco.ios.ios_vrf_global: + config: + vrfs: + - name: VRF2 + description: This is a test VRF for merged state + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "2:3" + route_target: + export: "192.0.2.0:100" + import_config: "192.0.2.3:200" + vpn: + id: "2:45" + vnet: + tag: 200 + state: rendered + +# Task Output: +# ------------ +# +# rendered: +# - vrf definition VRF2 +# - description This is a test VRF for merged state +# - ipv4 multicast multitopology +# - ipv6 multicast multitopology +# - rd 2:3 +# - route-target export 192.0.2.0:100 +# - route-target import 192.0.2.3:200 +# - vnet tag 200 +# - vpn id 2:45 + +# Using Gathered + +# Before state: +# ------------- +# +# admin#show running-config | section ^vrf +# vrf definition VRF2 +# vnet tag 200 +# description This is a test VRF for merged state +# ipv4 multicast multitopology +# ipv6 multicast multitopology +# rd 2:3 +# vpn id 2:45 +# route-target export 192.0.2.0:100 +# route-target import 192.0.2.3:200 + +- name: Gather existing running configuration + cisco.ios.ios_vrf_global: + config: + state: gathered + +# Task Output: +# ------------ +# +# gathered: +# vrfs: +# - name: VRF2 +# description: This is a test VRF for merged state +# ipv4: +# multicast: +# multitopology: true +# ipv6: +# multicast: +# multitopology: true +# rd: "2:3" +# route_target: +# export: "192.0.2.0:100" +# import_config: "192.0.2.3:200" +# vnet: +# tag: 200 +# vpn: +# id: "2:45" + +# Using parsed + +# File: parsed.cfg +# ---------------- +# +# vrf definition test +# vnet tag 34 +# description This is test VRF +# ipv4 multicast multitopology +# ipv6 multicast multitopology +# rd 192.0.2.0:300 +# vpn id 3:4 +# route-target export 192.0.2.0:100 +# route-target import 192.0.2.2:300 +# vrf definition test2 +# vnet tag 35 +# description This is test VRF +# ipv4 multicast multitopology +# ipv6 multicast multitopology +# rd 192.0.2.3:300 + +- name: Parse the provided configuration + cisco.ios.ios_vrf_global: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Task Output: +# ------------ +# +# parsed: +# vrfs: +# - name: test +# description: This is test VRF +# ipv4: +# multicast: +# multitopology: true +# ipv6: +# multicast: +# multitopology: true +# rd: "192.0.2.0:300" +# route_target: +# export: "192.0.2.0:100" +# import_config: "192.0.2.2:300" +# vnet: +# tag: 34 +# vpn: +# id: "3:4" +# - name: test2 +# description: This is test VRF +# ipv4: +# multicast: +# multitopology: true +# ipv6: +# multicast: +# multitopology: true +# rd: "192.0.2.3:300" +# vnet: +# tag: 35 +""" + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - "vrf definition test" + - "description This is a test VRF" + - "rd: 2:3" +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - "vrf definition management" + - "description This is a test VRF" + - "rd: 2:3" + - "route-target export 190.0.2.3:400" + - "route-target import 190.0.2.1:300" +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vrf_global.vrf_global import ( + Vrf_globalArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.vrf_global.vrf_global import ( + Vrf_global, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Vrf_globalArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Vrf_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/ios_vrf_interfaces.py b/plugins/modules/ios_vrf_interfaces.py new file mode 100644 index 000000000..5a9fdca3c --- /dev/null +++ b/plugins/modules/ios_vrf_interfaces.py @@ -0,0 +1,617 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2024 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_vrf_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: ios_vrf_interfaces +extends_documentation_fragment: + - cisco.ios.ios +short_description: Manages VRF configuration on interfaces. +description: + - Manages Virtual Routing and Forwarding (VRF) configuration on interfaces of Cisco IOS devices. +version_added: "1.0.0" +author: "AAYUSH ANAND (@AAYUSH2091)" +notes: + - Tested against Cisco IOS XE Version 17.13.01a + - VRF must exist before assigning to an interface + - When removing VRF from interface, associated IP addresses will be removed +options: + config: + description: A list of interface VRF configurations. + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface to be configured. + - Example - GigabitEthernet1, Loopback24 + type: str + required: true + vrf_name: + description: + - Name of the VRF to be configured on the interface. + - When configured, applies 'vrf forwarding ' under the interface. + type: str + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by executing + the command B(show running-config | section ^interface). + - The state I(parsed) reads the configuration from C(running_config) option and transforms + it into Ansible structured data as per the resource module's argspec and the value + is then returned in the I(parsed) key within the result. + type: str + state: + description: + - The state the configuration should be left in. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: Merge provided configuration with device configuration + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_D + - name: GigabitEthernet3 + - name: GigabitEthernet4 + state: merged + +# Task Output: +# ------------ +# +# before: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# - name: "GigabitEthernet2" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" +# +# commands: +# - interface GigabitEthernet2 +# - vrf forwarding vrf_D +# +# after: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_D" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# vrf forwarding vrf_D +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +# Using overridden + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# vrf forwarding vrf_B +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: Override device configuration with provided configuration + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + - name: GigabitEthernet3 + - name: GigabitEthernet4 + state: overridden + +# Task Output: +# ------------ +# +# before: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_B" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" +# +# commands: +# - interface GigabitEthernet2 +# - no vrf forwarding vrf_B +# +# after: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# - name: "GigabitEthernet2" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +# Using gathered + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# vrf forwarding vrf_B +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: Gather listed VRF interfaces + cisco.ios.ios_vrf_interfaces: + state: gathered + +# Task Output: +# ------------ +# +# gathered: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_B" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" + +# Using rendered + +- name: Render VRF configuration + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_D + - name: GigabitEthernet3 + - name: GigabitEthernet4 + state: rendered + +# Task Output: +# ------------ +# +# rendered: +# - interface GigabitEthernet2 +# - vrf forwarding vrf_D + +# Using parsed + +# File: parsed.cfg +# --------------- +# +# interface GigabitEthernet1 +# vrf vrf_C +# shutdown +# ! +# interface GigabitEthernet2 +# vrf vrf_D +# shutdown +# ! +# interface GigabitEthernet3 +# shutdown +# ! +# interface GigabitEthernet4 +# shutdown +# ! + +- name: Parse configuration from device running config + cisco.ios.ios_vrf_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Task Output: +# ------------ +# +# parsed: +# - name: "GigabitEthernet1" +# vrf_name: "vrf_C" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_D" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" + +# Using replaced + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# vrf forwarding vrf_A +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# vrf forwarding vrf_B +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# vrf forwarding vrf_C +# no ip address +# shutdown +# negotiation auto + +- name: Replace device configuration of listed VRF interfaces with provided configuration + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + vrf_name: vrf_D + - name: GigabitEthernet2 + vrf_name: vrf_E + state: replaced + +# Task Output: +# ------------ +# +# before: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# vrf_name: "vrf_A" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_B" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" +# vrf_name: "vrf_C" +# +# commands: +# - interface GigabitEthernet1 +# - no vrf forwarding vrf_A +# - vrf forwarding vrf_D +# - interface GigabitEthernet2 +# - no vrf forwarding vrf_B +# - vrf forwarding vrf_E +# +# after: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# vrf_name: "vrf_D" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_E" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" +# vrf_name: "vrf_C" + +# Using deleted + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# vrf forwarding vrf_A +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# vrf forwarding vrf_B +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# vrf forwarding vrf_C +# no ip address +# shutdown +# negotiation auto + +- name: Delete VRF configuration of specified interfaces + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + state: deleted + +# Task Output: +# ------------ +# +# before: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# vrf_name: "vrf_A" +# - name: "GigabitEthernet2" +# vrf_name: "vrf_B" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" +# vrf_name: "vrf_C" +# +# commands: +# - interface GigabitEthernet1 +# - no vrf forwarding vrf_A +# - interface GigabitEthernet2 +# - no vrf forwarding vrf_B +# +# after: +# - name: "Loopback24" +# - name: "GigabitEthernet1" +# - name: "GigabitEthernet2" +# - name: "GigabitEthernet3" +# - name: "GigabitEthernet4" +# vrf_name: "vrf_C" + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Loopback24 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# no ip address +# shutdown +# negotiation auto +# interface GigabitEthernet3 +# no ip address +# negotiation auto +# interface GigabitEthernet4 +# vrf forwarding vrf_C +# no ip address +# shutdown +# negotiation auto +""" + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) + type: list + sample: > + [ + { + "name": "Loopback24" + }, + { + "name": "GigabitEthernet1" + }, + { + "name": "GigabitEthernet2", + "vrf_name": "vrf_B" + }, + { + "name": "GigabitEthernet3" + }, + { + "name": "GigabitEthernet4" + } + ] + +after: + description: The resulting configuration after module execution. + returned: when changed + type: list + sample: > + [ + { + "name": "Loopback24" + }, + { + "name": "GigabitEthernet1" + }, + { + "name": "GigabitEthernet2", + "vrf_name": "vrf_D" + }, + { + "name": "GigabitEthernet3" + }, + { + "name": "GigabitEthernet4" + } + ] + +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) + type: list + sample: + - "interface GigabitEthernet2" + - "vrf forwarding vrf_D" + - "no vrf forwarding vrf_B" + +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - "interface GigabitEthernet1" + - "vrf forwarding vrf_C" + - "interface GigabitEthernet2" + - "vrf forwarding vrf_D" + +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + [ + { + "name": "Loopback24" + }, + { + "name": "GigabitEthernet1" + }, + { + "name": "GigabitEthernet2", + "vrf_name": "vrf_B" + }, + { + "name": "GigabitEthernet3" + }, + { + "name": "GigabitEthernet4" + } + ] + +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + [ + { + "name": "GigabitEthernet1", + "vrf_name": "vrf_C" + }, + { + "name": "GigabitEthernet2", + "vrf_name": "vrf_D" + }, + { + "name": "GigabitEthernet3" + }, + { + "name": "GigabitEthernet4" + } + ] +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vrf_interfaces.vrf_interfaces import ( + Vrf_interfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.vrf_interfaces.vrf_interfaces import ( + Vrf_interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Vrf_interfacesArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Vrf_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/plugins/terminal/ios.py b/plugins/terminal/ios.py index 2ef4f2d7c..dba3022b0 100644 --- a/plugins/terminal/ios.py +++ b/plugins/terminal/ios.py @@ -34,7 +34,9 @@ class TerminalModule(TerminalBase): - terminal_stdout_re = [re.compile(rb"[\r\n]?[\w\+\-\.:\/\[\]]+(?:\([^\)]+\)){0,3}(?:[>#]) ?$")] + terminal_stdout_re = [ + re.compile(rb"[\r\n]?[\w\+\-\.:\/\[\]]+(?:\([^\)]+\)){0,3}(?:[>#]) ?$"), + ] privilege_level_re = re.compile(r"Current privilege level is (\d+)$") @@ -55,10 +57,17 @@ class TerminalModule(TerminalBase): re.compile(rb"% ?(\S+) ?Informational: ?[\s]+", re.I), re.compile(rb"Command authorization failed"), re.compile(rb"Command Rejected: ?[\s]+", re.I), - re.compile(rb"% General session commands not allowed under the address family", re.I), + re.compile( + rb"% General session commands not allowed under the address family", + re.I, + ), re.compile(rb"% BGP: Error initializing topology", re.I), re.compile(rb"%SNMP agent not enabled", re.I), re.compile(rb"% Invalid", re.I), + re.compile( + rb"%You must disable VTPv1 and VTPv2 or switch to VTPv3 before configuring a VLAN name longer than 32 characters", + re.I, + ), ] terminal_config_prompt = re.compile(r"^.+\(config(-.*)?\)#$") @@ -66,7 +75,9 @@ class TerminalModule(TerminalBase): def get_privilege_level(self): try: cmd = {"command": "show privilege"} - result = self._exec_cli_command(to_bytes(json.dumps(cmd), errors="surrogate_or_strict")) + result = self._exec_cli_command( + to_bytes(json.dumps(cmd), errors="surrogate_or_strict"), + ) except AnsibleConnectionFailure as e: raise AnsibleConnectionFailure( "unable to fetch privilege, with error: %s" % (e.message), @@ -74,7 +85,9 @@ def get_privilege_level(self): prompt = self.privilege_level_re.search(result) if not prompt: - raise AnsibleConnectionFailure("unable to check privilege level [%s]" % result) + raise AnsibleConnectionFailure( + "unable to check privilege level [%s]" % result, + ) return int(prompt.group(1)) @@ -110,11 +123,16 @@ def on_become(self, passwd=None): if passwd: # Note: python-3.5 cannot combine u"" and r"" together. Thus make # an r string and use to_text to ensure it's text on both py2 and py3. - cmd["prompt"] = to_text(r"[\r\n]?(?:.*)?[Pp]assword: ?$", errors="surrogate_or_strict") + cmd["prompt"] = to_text( + r"[\r\n]?(?:.*)?[Pp]assword: ?$", + errors="surrogate_or_strict", + ) cmd["answer"] = passwd cmd["prompt_retry_check"] = True try: - self._exec_cli_command(to_bytes(json.dumps(cmd), errors="surrogate_or_strict")) + self._exec_cli_command( + to_bytes(json.dumps(cmd), errors="surrogate_or_strict"), + ) prompt = self._get_prompt() privilege_level = self.get_privilege_level() except AnsibleConnectionFailure as e: diff --git a/tests/integration/targets/ios_bgp_address_family/tests/cli/merged.yaml b/tests/integration/targets/ios_bgp_address_family/tests/cli/merged.yaml index e0d2a452c..e6d70b297 100644 --- a/tests/integration/targets/ios_bgp_address_family/tests/cli/merged.yaml +++ b/tests/integration/targets/ios_bgp_address_family/tests/cli/merged.yaml @@ -29,6 +29,10 @@ # afi: l2vpn # safi: evpn - afi: ipv4 + maximum_paths: + paths: 12 + maximum_secondary_paths: + eibgp: 2 redistribute: - connected: metric: 10 diff --git a/tests/integration/targets/ios_bgp_address_family/tests/cli/rendered.yaml b/tests/integration/targets/ios_bgp_address_family/tests/cli/rendered.yaml index 77a5cd204..f6fd007d4 100644 --- a/tests/integration/targets/ios_bgp_address_family/tests/cli/rendered.yaml +++ b/tests/integration/targets/ios_bgp_address_family/tests/cli/rendered.yaml @@ -3,79 +3,97 @@ msg: Start ios_bgp_address_family rendered integration tests ansible_connection={{ ansible_connection }} - block: - - name: Render the commands for provided configuration + - name: Render config lines for the provided configuration register: result - cisco.ios.ios_bgp_address_family: + cisco.ios.ios_bgp_address_family: &id001 config: - as_number: 65000 address_family: - afi: ipv4 - safi: multicast - # vrf: blue - aggregate_address: - - address: 192.0.2.1 - netmask: 255.255.255.255 - as_confed_set: true - bgp: - aggregate_timer: 10 - dampening: - penalty_half_time: 1 - reuse_route_val: 1 - suppress_route_val: 1 - max_suppress: 1 - slow_peer: - - detection: - threshold: 150 - neighbor: - - address: 198.51.100.1 - aigp: - send: - cost_community: - id: 100 - poi: - igp_cost: true - transitive: true - slow_peer: - - detection: - threshold: 150 - remote_as: 10 - route_maps: - - name: test-route - out: true - network: - - address: 198.51.110.10 - mask: 255.255.255.255 - backdoor: true - - afi: ipv4 - safi: mdt - bgp: - dmzlink_bw: true - dampening: - penalty_half_time: 1 - reuse_route_val: 10 - suppress_route_val: 100 - max_suppress: 5 - soft_reconfig_backup: true + maximum_paths: + paths: 12 + maximum_secondary_paths: + eibgp: 2 + redistribute: + - connected: + metric: 10 + - ospf: + process_id: 124 + - ospf: + process_id: 123 + match: + internal: true + externals: + type_1: true + type_2: true + nssa_externals: + type_1: true + type_2: true + metric: 10 + route_map: bar + - ospfv3: + process_id: 123 + match: + internal: true + externals: + type_1: true + type_2: true + nssa_externals: + type_1: true + type_2: true + metric: 10 + route_map: bar - afi: ipv4 - safi: multicast - aggregate_address: + aggregate_addresses: - address: 192.0.3.1 - netmask: 255.255.255.255 as_confed_set: true + netmask: 255.255.255.255 default_metric: 12 distance: external: 10 internal: 10 local: 100 - network: + networks: - address: 198.51.111.11 mask: 255.255.255.255 route_map: test + safi: multicast table_map: - name: test_tableMap filter: true + name: test_tableMap + - afi: ipv4 + bgp: + dampening: + max_suppress: 5 + penalty_half_time: 1 + reuse_route_val: 10 + suppress_route_val: 100 + dmzlink_bw: true + soft_reconfig_backup: true + safi: mdt + - afi: ipv6 + redistribute: + - ospf: + process_id: 124 + match: + internal: true + externals: + type_1: true + type_2: true + nssa_externals: + type_1: true + type_2: true + metric: 10 + route_map: bar + include_connected: true + as_number: "65000" state: rendered - - ansible.builtin.assert: + - name: Assert that change was false + ansible.builtin.assert: that: - result.changed == false + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ rendered['commands'] | symmetric_difference(result['rendered']) | length == 0 }}" diff --git a/tests/integration/targets/ios_bgp_address_family/vars/main.yaml b/tests/integration/targets/ios_bgp_address_family/vars/main.yaml index 9198405ac..de5ea3c15 100644 --- a/tests/integration/targets/ios_bgp_address_family/vars/main.yaml +++ b/tests/integration/targets/ios_bgp_address_family/vars/main.yaml @@ -4,29 +4,24 @@ merged: commands: - router bgp 65000 - # - address-family l2vpn evpn - # - neighbor 198.51.100.1 activate - # - neighbor 198.51.100.1 inherit peer-policy LEAF-EVPN-PEER-POLICY - # - address-family ipv4 vrf green - # - advertise l2vpn evpn - # - redistribute connected - # - redistribute static - address-family ipv4 multicast - default-metric 12 - distance bgp 10 10 100 - table-map test_tableMap filter - network 198.51.111.11 mask 255.255.255.255 route-map test - aggregate-address 192.0.3.1 255.255.255.255 as-confed-set + - address-family ipv6 + - redistribute ospf 124 metric 10 match internal external 1 external 2 nssa-external 1 nssa-external 2 route-map bar include-connected - address-family ipv4 mdt - bgp dmzlink-bw - bgp soft-reconfig-backup - bgp dampening 1 10 100 5 - - address-family ipv6 - - redistribute ospf 124 metric 10 match internal external 1 external 2 nssa-external 1 nssa-external 2 route-map bar include-connected - address-family ipv4 + - maximum-paths 12 + - maximum-secondary-paths eibgp 2 - redistribute connected metric 10 - - redistribute ospf 123 metric 10 match internal external 1 external 2 nssa-external 1 nssa-external 2 route-map bar - redistribute ospf 124 + - redistribute ospf 123 metric 10 match internal external 1 external 2 nssa-external 1 nssa-external 2 route-map bar - redistribute ospfv3 123 metric 10 match internal external 1 external 2 nssa-external 1 nssa-external 2 route-map bar after: @@ -315,6 +310,29 @@ deleted: - no address-family ipv4 - no address-family ipv6 +rendered: + commands: + - router bgp 65000 + - address-family ipv4 + - maximum-paths 12 + - maximum-secondary-paths eibgp 2 + - redistribute connected metric 10 + - redistribute ospf 124 + - redistribute ospf 123 metric 10 match internal external 1 external 2 nssa-external 1 nssa-external 2 route-map bar + - redistribute ospfv3 123 metric 10 match internal external 1 external 2 nssa-external 1 nssa-external 2 route-map bar + - address-family ipv4 multicast + - default-metric 12 + - distance bgp 10 10 100 + - table-map test_tableMap filter + - network 198.51.111.11 mask 255.255.255.255 route-map test + - aggregate-address 192.0.3.1 255.255.255.255 as-confed-set + - address-family ipv4 mdt + - bgp dmzlink-bw + - bgp soft-reconfig-backup + - bgp dampening 1 10 100 5 + - address-family ipv6 + - redistribute ospf 124 metric 10 match internal external 1 external 2 nssa-external 1 nssa-external 2 route-map bar include-connected + deleted_all: after: as_number: "65000" diff --git a/tests/integration/targets/ios_bgp_global/tests/cli/_populate_config.yaml b/tests/integration/targets/ios_bgp_global/tests/cli/_populate_config.yaml index 7c216edb0..3f18c6213 100644 --- a/tests/integration/targets/ios_bgp_global/tests/cli/_populate_config.yaml +++ b/tests/integration/targets/ios_bgp_global/tests/cli/_populate_config.yaml @@ -18,10 +18,10 @@ time: 50 community: 100 local_preference: 100 - nopeerup_delay: - - post_boot: 10 + nopeerup_delay_options: + post_boot: 10 neighbor: - - address: 192.0.2.1 + - neighbor_address: 192.0.2.1 description: merge neighbor remote_as: 100 aigp: @@ -31,9 +31,6 @@ poi: igp_cost: true transitive: true - route_map: - name: test-route - out: true redistribute: - connected: metric: 10 diff --git a/tests/integration/targets/ios_bgp_global/tests/cli/merged.yaml b/tests/integration/targets/ios_bgp_global/tests/cli/merged.yaml index 6c126ccfd..f7fd02ba4 100644 --- a/tests/integration/targets/ios_bgp_global/tests/cli/merged.yaml +++ b/tests/integration/targets/ios_bgp_global/tests/cli/merged.yaml @@ -11,8 +11,6 @@ config: as_number: 65000 bgp: - bestpath: - - compare_routerid: true default: ipv4_unicast: false route_target: @@ -23,10 +21,8 @@ community: 100 local_preference: 100 log_neighbor_changes: true - nopeerup_delay: - - post_boot: 10 neighbor: - - address: 198.0.2.1 + - neighbor_address: 198.0.2.1 description: merge neighbor remote_as: 100 shutdown: diff --git a/tests/integration/targets/ios_bgp_global/tests/cli/rendered.yaml b/tests/integration/targets/ios_bgp_global/tests/cli/rendered.yaml index eace1e838..b1c27f820 100644 --- a/tests/integration/targets/ios_bgp_global/tests/cli/rendered.yaml +++ b/tests/integration/targets/ios_bgp_global/tests/cli/rendered.yaml @@ -10,8 +10,6 @@ as_number: 65000 bgp: advertise_best_external: true - bestpath: - - compare_routerid: true dampening: penalty_half_time: 1 reuse_route_val: 1 @@ -26,7 +24,7 @@ nopeerup_delay_options: post_boot: 10 neighbor: - - address: 192.0.2.1 + - neighbor_address: 192.0.2.1 description: merge neighbor remote_as: 100 aigp: @@ -36,9 +34,6 @@ poi: igp_cost: true transitive: true - route_map: - name: test-route - out: true redistribute: - connected: metric: 10 diff --git a/tests/integration/targets/ios_bgp_global/tests/cli/replaced.yaml b/tests/integration/targets/ios_bgp_global/tests/cli/replaced.yaml index 0fddfe10e..5606d8ef4 100644 --- a/tests/integration/targets/ios_bgp_global/tests/cli/replaced.yaml +++ b/tests/integration/targets/ios_bgp_global/tests/cli/replaced.yaml @@ -21,7 +21,7 @@ post_boot: 10 cold_boot: 20 neighbor: - - address: 192.0.2.2 + - neighbor_address: 192.0.2.2 description: replace neighbor remote_as: 100 slow_peer: diff --git a/tests/integration/targets/ios_bgp_global/tests/cli/rtt.yaml b/tests/integration/targets/ios_bgp_global/tests/cli/rtt.yaml index e35cf7914..029c2dec0 100644 --- a/tests/integration/targets/ios_bgp_global/tests/cli/rtt.yaml +++ b/tests/integration/targets/ios_bgp_global/tests/cli/rtt.yaml @@ -20,7 +20,7 @@ post_boot: 10 cold_boot: 20 neighbor: - - address: 192.0.2.2 + - neighbor_address: 192.0.2.2 description: replace neighbor remote_as: 100 slow_peer: diff --git a/tests/integration/targets/ios_bgp_global/vars/main.yaml b/tests/integration/targets/ios_bgp_global/vars/main.yaml index 93fadcf29..71dd2788f 100644 --- a/tests/integration/targets/ios_bgp_global/vars/main.yaml +++ b/tests/integration/targets/ios_bgp_global/vars/main.yaml @@ -5,20 +5,16 @@ merged: commands: - router bgp 65000 - timers bgp 100 200 150 - - bgp bestpath compare-routerid - no bgp default ipv4-unicast - no bgp default route-target filter - bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 - bgp log-neighbor-changes - - bgp nopeerup-delay post-boot 10 - neighbor 198.0.2.1 remote-as 100 - neighbor 198.0.2.1 description merge neighbor after: as_number: "65000" bgp: - bestpath_options: - compare_routerid: true default: ipv4_unicast: false route_target: @@ -29,8 +25,6 @@ merged: neighbors: time: 50 log_neighbor_changes: true - nopeerup_delay_options: - post_boot: 10 neighbors: - description: merge neighbor neighbor_address: 198.0.2.1 @@ -75,9 +69,6 @@ gathered: description: merge neighbor neighbor_address: 192.0.2.1 remote_as: "100" - route_maps: - - name: test-route - out: true redistribute: - connected: metric: 10 @@ -180,7 +171,6 @@ rendered: - router bgp 65000 - timers bgp 100 200 150 - bgp advertise-best-external - - bgp bestpath compare-routerid - bgp dampening 1 1 1 1 - bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 - bgp log-neighbor-changes @@ -188,7 +178,6 @@ rendered: - neighbor 192.0.2.1 remote-as 100 - neighbor 192.0.2.1 description merge neighbor - neighbor 192.0.2.1 aigp send cost-community 100 poi igp-cost transitive - - neighbor 192.0.2.1 route-map test-route out - redistribute connected metric 10 deleted: diff --git a/tests/integration/targets/ios_facts/tests/cli/all_facts.yaml b/tests/integration/targets/ios_facts/tests/cli/all_facts.yaml index e01263fe4..dc4854f2e 100644 --- a/tests/integration/targets/ios_facts/tests/cli/all_facts.yaml +++ b/tests/integration/targets/ios_facts/tests/cli/all_facts.yaml @@ -55,6 +55,7 @@ - ntp_global - service - hostname + - vrf_global - name: Collect list of available network resources for IOS register: result diff --git a/tests/integration/targets/ios_linkagg/meta/main.yaml b/tests/integration/targets/ios_linkagg/meta/main.yaml deleted file mode 100644 index ab3ad91bc..000000000 --- a/tests/integration/targets/ios_linkagg/meta/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -dependencies: - - prepare_ios_tests diff --git a/tests/integration/targets/ios_linkagg/tasks/main.yaml b/tests/integration/targets/ios_linkagg/tasks/main.yaml deleted file mode 100644 index 3360559bd..000000000 --- a/tests/integration/targets/ios_linkagg/tasks/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -- name: Main task for linkagg module - ansible.builtin.include_tasks: cli.yaml diff --git a/tests/integration/targets/ios_linkagg/tests/cli/basic.yaml b/tests/integration/targets/ios_linkagg/tests/cli/basic.yaml deleted file mode 100644 index 079c1f8fe..000000000 --- a/tests/integration/targets/ios_linkagg/tests/cli/basic.yaml +++ /dev/null @@ -1,157 +0,0 @@ ---- -- ansible.builtin.debug: msg="START cli/basic.yaml on connection={{ ansible_connection }}" - -- ansible.builtin.set_fact: switch_type="{{ switch_type }}" - -- block: - - name: Setup - remove configuration used in test(part1) - ignore_errors: true - cisco.ios.ios_config: - lines: - - no interface port-channel 20 - - - name: Setup - remove configuration used in test(part2) - ignore_errors: true - cisco.ios.ios_config: - lines: - - no interface port-channel 5 - - - name: Setup - remove configuration used in test(part3) - loop: - - interface GigabitEthernet2 - - interface GigabitEthernet3 - cisco.ios.ios_config: - lines: - - no channel-group 20 mode active - parents: "{{ item }}" - - - name: Create linkagg - register: result - cisco.ios.ios_linkagg: &id001 - group: 20 - state: present - - - ansible.builtin.assert: - that: - - result.changed == true - - "'interface port-channel 20' in result.commands" - - - name: Create linkagg (idempotent) - register: result - cisco.ios.ios_linkagg: *id001 - - ansible.builtin.assert: - that: - - result.changed == false - - - name: Set link aggregation group to members - register: result - cisco.ios.ios_linkagg: &id002 - group: 20 - mode: active - members: - - GigabitEthernet2 - - GigabitEthernet3 - - - ansible.builtin.assert: - that: - - result.changed == true - - "'interface GigabitEthernet2' in result.commands" - - "'channel-group 20 mode active' in result.commands" - - "'interface GigabitEthernet3' in result.commands" - - "'channel-group 20 mode active' in result.commands" - - - name: Set link aggregation group to members (idempotent) - register: result - cisco.ios.ios_linkagg: *id002 - - ansible.builtin.assert: - that: - - result.changed == false - - - name: Remove link aggregation group from member - register: result - cisco.ios.ios_linkagg: &id003 - group: 20 - mode: active - members: - - GigabitEthernet3 - - - ansible.builtin.assert: - that: - - result.changed == true - - "'interface GigabitEthernet2' in result.commands" - - "'no channel-group 20 mode active' in result.commands" - - - name: Remove link aggregation group from member (idempotent) - register: result - cisco.ios.ios_linkagg: *id003 - - ansible.builtin.assert: - that: - - result.changed == false - - - name: Remove linkagg - register: result - cisco.ios.ios_linkagg: &id004 - group: 20 - state: absent - - - ansible.builtin.assert: - that: - - result.changed == true - - "'no interface port-channel 20' in result.commands" - - - name: Remove linkagg (idempotent) - register: result - cisco.ios.ios_linkagg: *id004 - - ansible.builtin.assert: - that: - - result.changed == false - - - name: Create aggregate of linkagg definitions - register: result - cisco.ios.ios_linkagg: &id005 - aggregate: - - group: 5 - - - group: 20 - mode: active - members: - - GigabitEthernet2 - - - ansible.builtin.assert: - that: - - result.changed == true - - "'interface port-channel 5' in result.commands" - - "'interface port-channel 20' in result.commands" - - "'interface GigabitEthernet2' in result.commands" - - "'channel-group 20 mode active' in result.commands" - - - name: Create aggregate of linkagg definitions (idempotent) - register: result - cisco.ios.ios_linkagg: *id005 - - ansible.builtin.assert: - that: - - result.changed == false - - - name: Teardown(part1) - ignore_errors: true - cisco.ios.ios_config: - lines: - - no interface port-channel 20 - - - name: Teardown(part2) - ignore_errors: true - cisco.ios.ios_config: - lines: - - no interface port-channel 5 - - - name: Teardown(part3) - loop: - - interface GigabitEthernet2 - - interface GigabitEthernet3 - cisco.ios.ios_config: - lines: - - no channel-group 20 mode active - parents: "{{ item }}" - when: switch_type == 'L2' - -- ansible.builtin.debug: msg="END cli/basic.yaml on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ios_lldp/defaults/main.yaml b/tests/integration/targets/ios_lldp/defaults/main.yaml deleted file mode 100644 index 5f709c5aa..000000000 --- a/tests/integration/targets/ios_lldp/defaults/main.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -testcase: "*" diff --git a/tests/integration/targets/ios_lldp/meta/main.yaml b/tests/integration/targets/ios_lldp/meta/main.yaml deleted file mode 100644 index ab3ad91bc..000000000 --- a/tests/integration/targets/ios_lldp/meta/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -dependencies: - - prepare_ios_tests diff --git a/tests/integration/targets/ios_lldp/tasks/main.yaml b/tests/integration/targets/ios_lldp/tasks/main.yaml deleted file mode 100644 index e2d4a7b2d..000000000 --- a/tests/integration/targets/ios_lldp/tasks/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -- name: Main task for lldp module - ansible.builtin.include_tasks: cli.yaml diff --git a/tests/integration/targets/ios_lldp/tests/cli/basic.yaml b/tests/integration/targets/ios_lldp/tests/cli/basic.yaml deleted file mode 100644 index 8fb6e07e7..000000000 --- a/tests/integration/targets/ios_lldp/tests/cli/basic.yaml +++ /dev/null @@ -1,46 +0,0 @@ ---- -- ansible.builtin.debug: msg="START ios_lldp cli/basic.yaml on connection={{ ansible_connection }}" - -- name: Make sure lldp is not running before tests - cisco.ios.ios_config: - lines: no lldp run - -- name: Enable lldp service - register: result - cisco.ios.ios_lldp: - state: present - -- ansible.builtin.assert: - that: - - result.changed == true - - '"lldp run" in result.commands' - -- name: Enable lldp service again (idempotent) - register: result - cisco.ios.ios_lldp: - state: present - -- ansible.builtin.assert: - that: - - result.changed == false - -- name: Disable lldp service - register: result - cisco.ios.ios_lldp: - state: absent - -- ansible.builtin.assert: - that: - - result.changed == true - - '"no lldp run" in result.commands' - -- name: Disable lldp service (idempotent) - register: result - cisco.ios.ios_lldp: - state: absent - -- ansible.builtin.assert: - that: - - result.changed == false - -- ansible.builtin.debug: msg="END ios_lldp cli/basic.yaml on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ios_logging_global/tests/cli/merged.yaml b/tests/integration/targets/ios_logging_global/tests/cli/merged.yaml index 7c3a84b8e..72fd5af2a 100644 --- a/tests/integration/targets/ios_logging_global/tests/cli/merged.yaml +++ b/tests/integration/targets/ios_logging_global/tests/cli/merged.yaml @@ -57,5 +57,24 @@ that: - merged['after'] == result['after'] + - name: Test - [merged] merge buffered severity with device configuration (idempotent) + cisco.ios.ios_logging_global: &id001 + config: + buffered: + severity: debugging + size: 5099 + xml: true + + - name: Test - [merged] merge console severity with device configuration (idempotent) + register: resultdeb + cisco.ios.ios_logging_global: *id001 + + - name: Test - [merged] assert that debugging value is not idempotent + ansible.builtin.assert: + that: + - resultdeb['changed'] == true + - "'logging buffered xml 5099 debugging' in resultdeb['commands']" + - resultdeb["before"]["buffered"] == resultdeb["after"]["buffered"] + always: - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_smoke/tests/cli/common_config.yaml b/tests/integration/targets/ios_smoke/tests/cli/common_config.yaml index 80e251c54..54a30e103 100644 --- a/tests/integration/targets/ios_smoke/tests/cli/common_config.yaml +++ b/tests/integration/targets/ios_smoke/tests/cli/common_config.yaml @@ -1,5 +1,4 @@ --- -# ios_linkagg -> CustomNetworkConfig # cisco.ios.ios_config -> dumps, NetworkConfig - ansible.builtin.debug: msg="START cli/common_config.yaml on connection={{ ansible_connection }}" @@ -110,77 +109,6 @@ - interface GigabitEthernet2 - interface GigabitEthernet3 - - name: Create linkagg - ios_linkagg: - group: 20 - state: present - register: result - - - ansible.builtin.assert: - that: - - "result.changed == true" - - "'interface port-channel 20' in result.commands" - - - name: Set link aggregation group to members - ios_linkagg: - group: 20 - mode: active - members: - - GigabitEthernet2 - - GigabitEthernet3 - register: result - - - ansible.builtin.assert: - that: - - "result.changed == true" - - "'interface GigabitEthernet2' in result.commands" - - "'channel-group 20 mode active' in result.commands" - - "'interface GigabitEthernet3' in result.commands" - - "'channel-group 20 mode active' in result.commands" - - - name: Remove link aggregation group from member - ios_linkagg: - group: 20 - mode: active - members: - - GigabitEthernet3 - register: result - - - ansible.builtin.assert: - that: - - "result.changed == true" - - "'interface GigabitEthernet2' in result.commands" - - "'no channel-group 20 mode active' in result.commands" - - - name: Remove linkagg - ios_linkagg: - group: 20 - state: absent - register: result - - - ansible.builtin.assert: - that: - - "result.changed == true" - - "'no interface port-channel 20' in result.commands" - - - name: Create aggregate of linkagg definitions - ios_linkagg: - aggregate: - - group: 5 - - group: 20 - mode: active - members: - - "GigabitEthernet2" - register: result - - - ansible.builtin.assert: - that: - - "result.changed == true" - - "'interface port-channel 5' in result.commands" - - "'interface port-channel 20' in result.commands" - - "'interface GigabitEthernet2' in result.commands" - - "'channel-group 20 mode active' in result.commands" - - name: Teardown(part1) cisco.ios.ios_config: lines: diff --git a/tests/integration/targets/ios_snmp_server/tests/cli/merged.yaml b/tests/integration/targets/ios_snmp_server/tests/cli/merged.yaml index f520971d0..5efbcc54c 100644 --- a/tests/integration/targets/ios_snmp_server/tests/cli/merged.yaml +++ b/tests/integration/targets/ios_snmp_server/tests/cli/merged.yaml @@ -11,6 +11,16 @@ register: result cisco.ios.ios_snmp_server: &id001 config: + location: "in the racks" + contact: "john@doe.org" + packet_size: 900 + communities: + - acl_v4: ADMIN-SUP + name: community_name + ro: true + - acl_v4: test_Acl + name: integrationComm + rw: true hosts: - community_string: integrationComm host: 192.0.2.10 diff --git a/tests/integration/targets/ios_snmp_server/vars/main.yaml b/tests/integration/targets/ios_snmp_server/vars/main.yaml index 8d3e4739b..df1d64573 100644 --- a/tests/integration/targets/ios_snmp_server/vars/main.yaml +++ b/tests/integration/targets/ios_snmp_server/vars/main.yaml @@ -1,20 +1,20 @@ --- merged: commands: + - snmp-server contact john@doe.org + - snmp-server location in the racks + - snmp-server packetsize 900 - snmp-server host 192.0.2.10 informs version 2c integrationComm isis + - snmp-server community community_name ro ADMIN-SUP after: communities: + - acl_v4: ADMIN-SUP + name: community_name + ro: true - acl_v4: test_Acl name: integrationComm rw: true - contact: "contact updated using play" - inform: - pending: 2 - ip: - dscp: 2 - location: "location entry for snmp" - packet_size: 500 - system_shutdown: true + contact: john@doe.org engine_id: - id: AB0C5342FF0F remote: @@ -27,6 +27,13 @@ merged: traps: - isis version: 2c + inform: + pending: 2 + ip: + dscp: 2 + location: in the racks + packet_size: 900 + system_shutdown: true overridden: before: diff --git a/tests/integration/targets/ios_static_routes/tests/cli/_parsed.cfg b/tests/integration/targets/ios_static_routes/tests/cli/_parsed.cfg new file mode 100644 index 000000000..acb3fc45b --- /dev/null +++ b/tests/integration/targets/ios_static_routes/tests/cli/_parsed.cfg @@ -0,0 +1,2 @@ +ip route 198.51.100.0 255.255.255.0 198.51.101.1 175 tag 70 name replaced_route multicast +ip route 192.168.1.0 255.255.255.0 GigabitEthernet0/1.22 10.0.0.1 tag 30 diff --git a/tests/integration/targets/ios_static_routes/tests/cli/parsed.yaml b/tests/integration/targets/ios_static_routes/tests/cli/parsed.yaml new file mode 100644 index 000000000..c35cc0d8c --- /dev/null +++ b/tests/integration/targets/ios_static_routes/tests/cli/parsed.yaml @@ -0,0 +1,14 @@ +--- +- ansible.builtin.debug: + msg: START ios_static_routes parsed integration test + +- name: Ios_static_routes parsed - play + register: result + cisco.ios.ios_static_routes: + running_config: "{{ lookup('file', '_parsed.cfg') }}" + state: parsed + +- ansible.builtin.assert: + that: + - result.changed == false + - parsed_data == result['parsed'] diff --git a/tests/integration/targets/ios_static_routes/vars/main.yaml b/tests/integration/targets/ios_static_routes/vars/main.yaml index b682a6eea..801e7336a 100644 --- a/tests/integration/targets/ios_static_routes/vars/main.yaml +++ b/tests/integration/targets/ios_static_routes/vars/main.yaml @@ -81,6 +81,22 @@ gathered: name: route_2 - forward_router_address: 198.51.101.3 name: route_3 +parsed_data: + - address_families: + - afi: ipv4 + routes: + - dest: 198.51.100.0/24 + next_hops: + - distance_metric: 175 + forward_router_address: 198.51.101.1 + multicast: true + name: replaced_route + tag: 70 + - dest: 192.168.1.0/24 + next_hops: + - forward_router_address: 10.0.0.1 + interface: GigabitEthernet0/1.22 + tag: 30 rtt: override_commands: - ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.12 tag 10 name new_rtt_vrf track 150 diff --git a/tests/integration/targets/ios_linkagg/defaults/main.yaml b/tests/integration/targets/ios_vrf_global/defaults/main.yaml similarity index 50% rename from tests/integration/targets/ios_linkagg/defaults/main.yaml rename to tests/integration/targets/ios_vrf_global/defaults/main.yaml index 9ef5ba516..164afead2 100644 --- a/tests/integration/targets/ios_linkagg/defaults/main.yaml +++ b/tests/integration/targets/ios_vrf_global/defaults/main.yaml @@ -1,3 +1,3 @@ --- -testcase: "*" +testcase: "[^_].*" test_items: [] diff --git a/tests/integration/targets/ios_vrf_global/meta/main.yaml b/tests/integration/targets/ios_vrf_global/meta/main.yaml new file mode 100644 index 000000000..23d65c7ef --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/meta/main.yaml @@ -0,0 +1,2 @@ +--- +dependencies: [] diff --git a/tests/integration/targets/ios_linkagg/tasks/cli.yaml b/tests/integration/targets/ios_vrf_global/tasks/cli.yaml similarity index 72% rename from tests/integration/targets/ios_linkagg/tasks/cli.yaml rename to tests/integration/targets/ios_vrf_global/tasks/cli.yaml index cd8c04990..6f505600c 100644 --- a/tests/integration/targets/ios_linkagg/tasks/cli.yaml +++ b/tests/integration/targets/ios_vrf_global/tasks/cli.yaml @@ -3,16 +3,19 @@ ansible.builtin.find: paths: "{{ role_path }}/tests/cli" patterns: "{{ testcase }}.yaml" + use_regex: true register: test_cases delegate_to: localhost - name: Set test_items ansible.builtin.set_fact: test_items: "{{ test_cases.files | map(attribute='path') | list }}" + delegate_to: localhost -- name: Run test cases (connection=ansible.netcommon.network_cli) +- name: Run test case (connection=ansible.netcommon.network_cli) ansible.builtin.include_tasks: "{{ test_case_to_run }}" + vars: + ansible_connection: ansible.netcommon.network_cli with_items: "{{ test_items }}" loop_control: loop_var: test_case_to_run - tags: network_cli diff --git a/tests/integration/targets/ios_vrf_global/tasks/main.yaml b/tests/integration/targets/ios_vrf_global/tasks/main.yaml new file mode 100644 index 000000000..adc4c6075 --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tasks/main.yaml @@ -0,0 +1,5 @@ +--- +- name: Main task for vrf_global module + ansible.builtin.include_tasks: cli.yaml + tags: + - network_cli diff --git a/tests/integration/targets/ios_vrf_global/tests/cli/_parsed.cfg b/tests/integration/targets/ios_vrf_global/tests/cli/_parsed.cfg new file mode 100644 index 000000000..5e237fe62 --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tests/cli/_parsed.cfg @@ -0,0 +1,11 @@ +vrf definition test + vnet tag 34 + description This is test VRF + ipv4 multicast multitopology + ipv6 multicast multitopology + rd 2:3 + vpn id 3:4 + route-target export 192.0.2.0:100 + route-target import 192.0.2.3:300 +! +end diff --git a/tests/integration/targets/ios_vrf_global/tests/cli/_populate_config.yaml b/tests/integration/targets/ios_vrf_global/tests/cli/_populate_config.yaml new file mode 100644 index 000000000..0d0a17388 --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tests/cli/_populate_config.yaml @@ -0,0 +1,15 @@ +--- +- name: Merge provided configuration with device configuration + register: result + cisco.ios.ios_config: + lines: + - vrf definition VRF2 + - description This is a test VRF for merged state + - ipv4 multicast multitopology + - ipv6 multicast multitopology + - rd 2:3 + - route-target export 192.0.2.1:400 + - route-target import 192.0.2.6:400 + - vnet tag 200 + - vpn id 2:45 + save_when: always diff --git a/tests/integration/targets/ios_vrf_global/tests/cli/_remove_config.yaml b/tests/integration/targets/ios_vrf_global/tests/cli/_remove_config.yaml new file mode 100644 index 000000000..f662e3363 --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tests/cli/_remove_config.yaml @@ -0,0 +1,10 @@ +--- +- name: Remove VRF global configurations + cisco.ios.ios_config: + lines: + - no vrf definition VRF2 + - no vrf definition VRF7 + - no vrf definition VRF6 + - no vrf definition test + save_when: always + register: result diff --git a/tests/integration/targets/ios_vrf_global/tests/cli/deleted.yaml b/tests/integration/targets/ios_vrf_global/tests/cli/deleted.yaml new file mode 100644 index 000000000..68ac217b0 --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tests/cli/deleted.yaml @@ -0,0 +1,45 @@ +--- +- ansible.builtin.debug: + msg: Start Deleted integration state for ios_vrf_global ansible_connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Delete given vrf configuration + register: result + cisco.ios.ios_vrf_global: &id001 + config: + vrfs: + - name: VRF2 + state: deleted + + - ansible.builtin.assert: + that: + - result.changed == true + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ deleted['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Assert that before dicts are correctly generated + ansible.builtin.assert: + that: + - "{{ merged['after']['vrfs'] | symmetric_difference(result['before']['vrfs']) | length == 0 }}" + + - name: Assert that after dicts are correctly generated + ansible.builtin.assert: + that: + - deleted['after'] == result['after'] + + - name: Delete provided VRF global (idempotent) + register: result + cisco.ios.ios_vrf_global: *id001 + - name: Assert that the previous task was idempotent + ansible.builtin.assert: + that: + - result.changed == true + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_global/tests/cli/empty_config.yaml b/tests/integration/targets/ios_vrf_global/tests/cli/empty_config.yaml new file mode 100644 index 000000000..7c8474844 --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tests/cli/empty_config.yaml @@ -0,0 +1,68 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_global empty_config.yaml integration tests on connection={{ ansible_connection }} + +- name: Merged with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_global: + config: + state: merged + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state merged' + +- name: Replaced with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_global: + config: + state: replaced + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state replaced' + +- name: Overridden with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_global: + config: + state: overridden + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state overridden' + +- name: Rendered with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_global: + config: + state: rendered + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state rendered' + +- name: Parsed with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_global: + running_config: + state: parsed + +- ansible.builtin.assert: + that: + - result.msg == 'value of running_config parameter must not be empty for state parsed' + +- name: Purged with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_global: + config: + state: purged + +- ansible.builtin.debug: + msg: END ios_vrf_global empty_config integration tests on connection={{ ansible_connection }} diff --git a/tests/integration/targets/ios_vrf_global/tests/cli/gathered.yaml b/tests/integration/targets/ios_vrf_global/tests/cli/gathered.yaml new file mode 100644 index 000000000..cf2f2972b --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tests/cli/gathered.yaml @@ -0,0 +1,22 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_global gathered integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Gather the provided configuration with the existing running configuration + register: result + cisco.ios.ios_vrf_global: + config: + state: gathered + + - name: Assert + ansible.builtin.assert: + that: + - result.changed == false + - gathered['after'] == result['gathered'] + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_global/tests/cli/merged.yaml b/tests/integration/targets/ios_vrf_global/tests/cli/merged.yaml new file mode 100644 index 000000000..f705627f6 --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tests/cli/merged.yaml @@ -0,0 +1,54 @@ +--- +- ansible.builtin.debug: + msg: START Merged ios_vrf_global state for integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- block: + - name: Merge provided configuration with device configuration + register: result + cisco.ios.ios_vrf_global: &id001 + config: + vrfs: + - name: VRF2 + description: This is a test VRF for merged state + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "2:3" + route_target: + export: "192.0.2.1:400" + import_config: "192.0.2.6:400" + vpn: + id: "2:45" + vnet: + tag: 200 + state: merged + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ merged['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Assert that before dicts are correctly generated + ansible.builtin.assert: + that: + - merged['before'] == {} + + - name: Assert that after dict is correctly generated + ansible.builtin.assert: + that: + - merged['after'] == result['after'] + + - name: Merge provided configuration with device configuration (idempotent) + register: result + cisco.ios.ios_vrf_global: *id001 + - name: Assert that the previous task was idempotent + ansible.builtin.assert: + that: + - result['changed'] == false + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_global/tests/cli/overridden.yaml b/tests/integration/targets/ios_vrf_global/tests/cli/overridden.yaml new file mode 100644 index 000000000..664971e3c --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tests/cli/overridden.yaml @@ -0,0 +1,63 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_global overridden integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Override the provided configuration with the existing running configuration + cisco.ios.ios_vrf_global: &overridden + config: + vrfs: + - name: VRF2 + - name: VRF6 + description: VRF6 description + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "6:7" + route_target: + export: "192.0.2.2:300" + import_config: "192.0.2.3:400" + vpn: + id: "4:5" + vnet: + tag: 500 + state: overridden + register: result + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ overridden['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Assert that before dicts are correctly generated + ansible.builtin.assert: + that: + - "{{ overridden['before'] == result['before'] }}" + + - name: Assert that after dict is correctly generated + ansible.builtin.assert: + that: + - "{{ overridden['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Idempotency check + cisco.ios.ios_vrf_global: *overridden + register: result + + - name: Assert that no changes were made + ansible.builtin.assert: + that: + - result['changed'] == false + - result.commands|length == 0 + + - name: Assert that before dict is correctly generated + ansible.builtin.assert: + that: + - "{{ overridden['after'] | symmetric_difference(result['before']) |length == 0 }}" + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_global/tests/cli/parsed.yaml b/tests/integration/targets/ios_vrf_global/tests/cli/parsed.yaml new file mode 100644 index 000000000..0d1008d1a --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tests/cli/parsed.yaml @@ -0,0 +1,14 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_global parsed integration tests on connection={{ ansible_connection }} + +- name: Parse the commands for provided configuration + register: result + cisco.ios.ios_vrf_global: + running_config: "{{ lookup('file', '_parsed.cfg') }}" + state: parsed + +- ansible.builtin.assert: + that: + - result.changed == false + - parsed['after'] == result['parsed'] diff --git a/tests/integration/targets/ios_vrf_global/tests/cli/purged.yaml b/tests/integration/targets/ios_vrf_global/tests/cli/purged.yaml new file mode 100644 index 000000000..aa016c607 --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tests/cli/purged.yaml @@ -0,0 +1,29 @@ +--- +- ansible.builtin.debug: + msg: Start Deleted integration state for ios_vrf_global ansible_connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Purge provided VRF global + register: result + cisco.ios.ios_vrf_global: &id001 + state: purged + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "'no vrf definition VRF2' in result.commands" + - result.commands|length == 1 + + - name: Purge provided VRF global (idempotent) + register: result + cisco.ios.ios_vrf_global: *id001 + - name: Assert that the previous task was idempotent + ansible.builtin.assert: + that: + - result.changed == false + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_global/tests/cli/rendered.yaml b/tests/integration/targets/ios_vrf_global/tests/cli/rendered.yaml new file mode 100644 index 000000000..3eebab3b5 --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tests/cli/rendered.yaml @@ -0,0 +1,34 @@ +--- +- ansible.builtin.debug: + msg: Start ios_vrf_global rendered integration tests ansible_connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- block: + - name: Render provided configuration with device configuration + register: result + cisco.ios.ios_vrf_global: + config: + vrfs: + - name: VRF2 + description: This is a test VRF for merged state + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "2:3" + route_target: + export: "192.0.2.1:400" + import_config: "192.0.2.6:400" + vpn: + id: "2:45" + vnet: + tag: 200 + state: rendered + + - ansible.builtin.assert: + that: + - result.changed == false + - result.rendered|symmetric_difference(merged.commands) == [] diff --git a/tests/integration/targets/ios_vrf_global/tests/cli/replaced.yaml b/tests/integration/targets/ios_vrf_global/tests/cli/replaced.yaml new file mode 100644 index 000000000..69842bd71 --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tests/cli/replaced.yaml @@ -0,0 +1,55 @@ +--- +- ansible.builtin.debug: + msg: START replaced ios_vrf_global state for integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Replace the provided configuration with the existing running configuration + register: result + cisco.ios.ios_vrf_global: &id001 + config: + vrfs: + - name: VRF7 + description: VRF7 description + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "7:8" + route_target: + export: "198.51.100.2:500" + import_config: "198.51.100.5:400" + vpn: + id: "5:45" + vnet: + tag: 300 + state: replaced + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ replaced['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Assert that before dict is correctly generated + ansible.builtin.assert: + that: + - "{{ replaced['before'] == result['before'] }}" + + - name: Assert that after dict is correctly generated + ansible.builtin.assert: + that: + - replaced['after'] == result['after'] + + - name: Replaced provided VRF global configuration (idempotent) + register: result + cisco.ios.ios_vrf_global: *id001 + - name: Assert that task was idempotent + ansible.builtin.assert: + that: + - result['changed'] == false + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_global/tests/cli/rtt.yaml b/tests/integration/targets/ios_vrf_global/tests/cli/rtt.yaml new file mode 100644 index 000000000..2273b8bf3 --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/tests/cli/rtt.yaml @@ -0,0 +1,77 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_global round trip integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- block: + - name: Merge provided configuration with device configuration + register: result + cisco.ios.ios_vrf_global: + config: + vrfs: + - name: VRF2 + description: This is a test VRF for merged state + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "2:3" + route_target: + export: "192.0.2.1:400" + import_config: "192.0.2.6:400" + vpn: + id: "2:45" + vnet: + tag: 200 + state: merged + + - name: Ios_vrf_global RTT - gather vrf_global facts + cisco.ios.ios_facts: + gather_network_resources: + - vrf_global + + - name: Apply the provided configuration (config to be reverted) + register: result + cisco.ios.ios_vrf_global: + config: + vrfs: + - name: VRF6 + description: VRF6 description + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "6:7" + route_target: + export: "192.0.2.2:300" + import_config: "192.0.2.3:400" + vpn: + id: "4:5" + vnet: + tag: 500 + state: overridden + + - ansible.builtin.assert: + that: + - result.commands|length == 18 + - result.changed == true + - result.commands|symmetric_difference(rtt.commands) == [] + + - name: Revert back to base configuration using facts round trip + register: revert + cisco.ios.ios_vrf_global: + config: "{{ ansible_facts['network_resources']['vrf_global'] }}" + state: replaced + + - ansible.builtin.assert: + that: + - revert.changed == true + - revert['commands'] == merged['commands'] + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_global/vars/main.yaml b/tests/integration/targets/ios_vrf_global/vars/main.yaml new file mode 100644 index 000000000..7573f6484 --- /dev/null +++ b/tests/integration/targets/ios_vrf_global/vars/main.yaml @@ -0,0 +1,233 @@ +--- +merged: + before: {} + + commands: + - vrf definition VRF2 + - description This is a test VRF for merged state + - ipv4 multicast multitopology + - ipv6 multicast multitopology + - rd 2:3 + - route-target export 192.0.2.1:400 + - route-target import 192.0.2.6:400 + - vnet tag 200 + - vpn id 2:45 + + after: + vrfs: + - name: VRF2 + description: This is a test VRF for merged state + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "2:3" + route_target: + export: "192.0.2.1:400" + import_config: "192.0.2.6:400" + vpn: + id: "2:45" + vnet: + tag: 200 + +replaced: + before: + vrfs: + - name: VRF2 + description: This is a test VRF for merged state + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "2:3" + route_target: + export: "192.0.2.1:400" + import_config: "192.0.2.6:400" + vpn: + id: "2:45" + vnet: + tag: 200 + commands: + - vrf definition VRF7 + - description VRF7 description + - ipv4 multicast multitopology + - ipv6 multicast multitopology + - rd 7:8 + - route-target export 198.51.100.2:500 + - route-target import 198.51.100.5:400 + - vnet tag 300 + - vpn id 5:45 + after: + vrfs: + - name: VRF2 + description: This is a test VRF for merged state + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "2:3" + route_target: + export: "192.0.2.1:400" + import_config: "192.0.2.6:400" + vpn: + id: "2:45" + vnet: + tag: 200 + - name: VRF7 + description: VRF7 description + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "7:8" + route_target: + export: "198.51.100.2:500" + import_config: "198.51.100.5:400" + vpn: + id: "5:45" + vnet: + tag: 300 + +overridden: + before: + vrfs: + - name: VRF2 + description: This is a test VRF for merged state + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "2:3" + route_target: + export: "192.0.2.1:400" + import_config: "192.0.2.6:400" + vpn: + id: "2:45" + vnet: + tag: 200 + + commands: + - vrf definition VRF2 + - no description This is a test VRF for merged state + - no ipv4 multicast multitopology + - no ipv6 multicast multitopology + - no rd 2:3 + - no route-target export 192.0.2.1:400 + - no route-target import 192.0.2.6:400 + - no vnet tag 200 + - no vpn id 2:45 + - vrf definition VRF6 + - description VRF6 description + - ipv4 multicast multitopology + - ipv6 multicast multitopology + - rd 6:7 + - route-target export 192.0.2.2:300 + - route-target import 192.0.2.3:400 + - vnet tag 500 + - vpn id 4:5 + after: + vrfs: + - name: VRF2 + - name: VRF6 + description: VRF6 description + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "6:7" + route_target: + export: "192.0.2.2:300" + import_config: "192.0.2.3:400" + vpn: + id: "4:5" + vnet: + tag: 500 + +gathered: + after: + vrfs: + - name: VRF2 + description: This is a test VRF for merged state + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "2:3" + route_target: + export: "192.0.2.1:400" + import_config: "192.0.2.6:400" + vpn: + id: "2:45" + vnet: + tag: 200 + +parsed: + after: + vrfs: + - name: test + description: This is test VRF + ipv4: + multicast: + multitopology: true + ipv6: + multicast: + multitopology: true + rd: "2:3" + route_target: + export: "192.0.2.0:100" + import_config: "192.0.2.3:300" + vpn: + id: "3:4" + vnet: + tag: 34 + +deleted: + commands: + - vrf definition VRF2 + - no description This is a test VRF for merged state + - no ipv4 multicast multitopology + - no ipv6 multicast multitopology + - no rd 2:3 + - no route-target export 192.0.2.1:400 + - no route-target import 192.0.2.6:400 + - no vnet tag 200 + - no vpn id 2:45 + + after: + vrfs: + - name: VRF2 + +rtt: + commands: + - vrf definition VRF2 + - no description This is a test VRF for merged state + - no ipv4 multicast multitopology + - no ipv6 multicast multitopology + - no rd 2:3 + - no route-target export 192.0.2.1:400 + - no route-target import 192.0.2.6:400 + - no vnet tag 200 + - no vpn id 2:45 + - vrf definition VRF6 + - description VRF6 description + - ipv4 multicast multitopology + - ipv6 multicast multitopology + - rd 6:7 + - route-target export 192.0.2.2:300 + - route-target import 192.0.2.3:400 + - vnet tag 500 + - vpn id 4:5 diff --git a/tests/integration/targets/ios_vrf_interfaces/defaults/main.yaml b/tests/integration/targets/ios_vrf_interfaces/defaults/main.yaml new file mode 100644 index 000000000..164afead2 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: "[^_].*" +test_items: [] diff --git a/tests/integration/targets/ios_lldp/tasks/cli.yaml b/tests/integration/targets/ios_vrf_interfaces/tasks/cli.yaml similarity index 65% rename from tests/integration/targets/ios_lldp/tasks/cli.yaml rename to tests/integration/targets/ios_vrf_interfaces/tasks/cli.yaml index cd8c04990..39471d59f 100644 --- a/tests/integration/targets/ios_lldp/tasks/cli.yaml +++ b/tests/integration/targets/ios_vrf_interfaces/tasks/cli.yaml @@ -1,8 +1,9 @@ --- - name: Collect all CLI test cases ansible.builtin.find: - paths: "{{ role_path }}/tests/cli" + paths: "{{ role_path }}/tests/common" patterns: "{{ testcase }}.yaml" + use_regex: true register: test_cases delegate_to: localhost @@ -10,9 +11,12 @@ ansible.builtin.set_fact: test_items: "{{ test_cases.files | map(attribute='path') | list }}" -- name: Run test cases (connection=ansible.netcommon.network_cli) +- name: Run test case (connection=ansible.netcommon.network_cli) + vars: + ansible_connection: ansible.netcommon.network_cli ansible.builtin.include_tasks: "{{ test_case_to_run }}" with_items: "{{ test_items }}" loop_control: loop_var: test_case_to_run - tags: network_cli + tags: + - network_cli diff --git a/tests/integration/targets/ios_vrf_interfaces/tasks/main.yaml b/tests/integration/targets/ios_vrf_interfaces/tasks/main.yaml new file mode 100644 index 000000000..f75f2f031 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tasks/main.yaml @@ -0,0 +1,5 @@ +--- +- name: Include the CLI tasks + ansible.builtin.include_tasks: cli.yaml + tags: + - cli diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/_parsed.cfg b/tests/integration/targets/ios_vrf_interfaces/tests/common/_parsed.cfg new file mode 100644 index 000000000..4d06b79dd --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/_parsed.cfg @@ -0,0 +1,15 @@ + +interface GigabitEthernet1 + vrf forwarding vrf_C + shutdown +! +interface GigabitEthernet2 + vrf forwarding vrf_D + shutdown +! +interface GigabitEthernet3 + shutdown +! +interface GigabitEthernet4 + shutdown +! diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/_populate_config.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/_populate_config.yaml new file mode 100644 index 000000000..9c676f568 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/_populate_config.yaml @@ -0,0 +1,12 @@ +--- +- name: Populate with simple overridden + cisco.ios.ios_vrf_interfaces: + state: overridden + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_B + - name: GigabitEthernet3 + - name: GigabitEthernet4 + vars: + ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/_remove_config.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/_remove_config.yaml new file mode 100644 index 000000000..5e0cc0491 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/_remove_config.yaml @@ -0,0 +1,11 @@ +--- +- name: Populate with simple overridden + cisco.ios.ios_vrf_interfaces: + state: overridden + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + - name: GigabitEthernet3 + - name: GigabitEthernet4 + vars: + ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/deleted.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/deleted.yaml new file mode 100644 index 000000000..4f9abef79 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/deleted.yaml @@ -0,0 +1,50 @@ +--- +- ansible.builtin.debug: + msg: Start Deleted integration state for ios_vrf_interfaces ansible_connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Delete provided VRF interfaces + register: result + cisco.ios.ios_vrf_interfaces: &id001 + config: + - name: GigabitEthernet2 + state: deleted + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ deleted['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Delete provided VRF interfaces (idempotent) + register: result + cisco.ios.ios_vrf_interfaces: *id001 + - name: Assert that the previous task was idempotent + ansible.builtin.assert: + that: + - result.changed == false + + - ansible.builtin.include_tasks: _populate_config.yaml + + - name: Delete provided VRF interfaces without any configuration + register: result + cisco.ios.ios_vrf_interfaces: &id002 + state: deleted + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ deleted['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Delete provided VRF interfaces without any configuration (idempotent) + register: result + cisco.ios.ios_vrf_interfaces: *id002 + - name: Assert that the delete task was idempotent + ansible.builtin.assert: + that: + - result.changed == false + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/empty_config.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/empty_config.yaml new file mode 100644 index 000000000..2506c7e50 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/empty_config.yaml @@ -0,0 +1,61 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_interfaces empty_config integration tests on connection={{ ansible_connection }} + +- name: Merged with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_interfaces: + config: + state: merged + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state merged' + +- name: Replaced with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_interfaces: + config: + state: replaced + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state replaced' + +- name: Overridden with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_interfaces: + config: + state: overridden + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state overridden' + +- name: Rendered with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_interfaces: + config: + state: rendered + +- ansible.builtin.assert: + that: + - result.msg == 'value of config parameter must not be empty for state rendered' + +- name: Parsed with empty configuration should give appropriate error message + register: result + ignore_errors: true + cisco.ios.ios_vrf_interfaces: + running_config: + state: parsed + +- ansible.builtin.assert: + that: + - result.msg == 'value of running_config parameter must not be empty for state parsed' + +- ansible.builtin.debug: + msg: END ios_vrf_interfaces empty_config integration tests on connection={{ ansible_connection }} diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/gathered.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/gathered.yaml new file mode 100644 index 000000000..616cf616c --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/gathered.yaml @@ -0,0 +1,22 @@ +--- +- name: START ios_vrf_interfaces gathered integration tests + ansible.builtin.debug: + msg: START ios_vrf_interfaces gathered integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Gather the provided configuration + register: result + cisco.ios.ios_vrf_interfaces: + config: + state: gathered + + - name: Assert + ansible.builtin.assert: + that: + - not result.changed + - "{{ gathered['config'] | symmetric_difference(result['gathered']) |length == 0 }}" + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/merged.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/merged.yaml new file mode 100644 index 000000000..9d169d135 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/merged.yaml @@ -0,0 +1,44 @@ +--- +- ansible.builtin.debug: + msg: START Merged ios_vrf_interfaces state for integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- block: + - name: Merge provided configuration with device configuration + register: result + cisco.ios.ios_vrf_interfaces: &id001 + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_D + - name: GigabitEthernet3 + - name: GigabitEthernet4 + state: merged + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ merged['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Assert that before dicts are correctly generated + ansible.builtin.assert: + that: + - "{{ merged['before'] | symmetric_difference(result['before']) | length == 0 }}" + + - name: Assert that after dict is correctly generated + ansible.builtin.assert: + that: + - "{{ merged['after'] | symmetric_difference(result['after']) | length == 0 }}" + + - name: Merge provided configuration with device configuration (idempotent) + register: result + cisco.ios.ios_vrf_interfaces: *id001 + + - name: Assert that the previous task was idempotent + ansible.builtin.assert: + that: + - result['changed'] == false + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/overridden.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/overridden.yaml new file mode 100644 index 000000000..5fa53c611 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/overridden.yaml @@ -0,0 +1,44 @@ +--- +- ansible.builtin.debug: + msg: START Overridden ios_vrf_interfaces state for integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Override provided configuration with device configuration + register: result + cisco.ios.ios_vrf_interfaces: &id001 + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + - name: GigabitEthernet3 + - name: GigabitEthernet4 + vrf_name: vrf_C + state: overridden + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ overridden['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Assert that before dicts are correctly generated + ansible.builtin.assert: + that: + - overridden['before'] == result['before'] + + - name: Assert that after dict is correctly generated + ansible.builtin.assert: + that: + - overridden['after'] == result['after'] + + - name: Override provided configuration with device configuration (idempotent) + register: result + cisco.ios.ios_vrf_interfaces: *id001 + + - name: Assert that the previous task was idempotent + ansible.builtin.assert: + that: + - result['changed'] == false + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/parsed.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/parsed.yaml new file mode 100644 index 000000000..a08968c71 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/parsed.yaml @@ -0,0 +1,14 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_interfaces parsed integration tests on connection={{ ansible_connection }} + +- name: Parse externally provided route-policy configuration + register: result + cisco.ios.ios_vrf_interfaces: + running_config: "{{ lookup('file', '_parsed.cfg') }}" + state: parsed + +- name: Assert that configuration was correctly parsed + ansible.builtin.assert: + that: + - parsed['after'] == result['parsed'] diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/rendered.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/rendered.yaml new file mode 100644 index 000000000..84ac4f670 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/rendered.yaml @@ -0,0 +1,21 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_interfaces rendered integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- name: Render route-policy configuration + register: result + cisco.ios.ios_vrf_interfaces: + state: rendered + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_D + - name: GigabitEthernet3 + - name: GigabitEthernet4 + +- name: Assert that correct set of commands were rendered + ansible.builtin.assert: + that: + - merged['commands'] == result['rendered'] diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/replaced.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/replaced.yaml new file mode 100644 index 000000000..0b8089093 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/replaced.yaml @@ -0,0 +1,42 @@ +--- +- ansible.builtin.debug: + msg: START Replaced ios_vrf_interfaces state for integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml +- ansible.builtin.include_tasks: _populate_config.yaml + +- block: + - name: Replace provided VRF interfaces configuration + register: result + cisco.ios.ios_vrf_interfaces: &id001 + config: + - name: GigabitEthernet2 + vrf_name: vrf_D + state: replaced + + - name: Assert that correct set of commands were generated + ansible.builtin.assert: + that: + - "{{ replaced['commands'] | symmetric_difference(result['commands']) | length == 0 }}" + + - name: Assert that before dicts are correctly generated + ansible.builtin.assert: + that: + - replaced['before'] == result['before'] + + - name: Assert that after dict is correctly generated + ansible.builtin.assert: + that: + - replaced['after'] == result['after'] + + - name: Replace provided VRF interfaces configuration (idempotent) + register: result + cisco.ios.ios_vrf_interfaces: *id001 + + - name: Assert that task was idempotent + ansible.builtin.assert: + that: + - result['changed'] == false + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_interfaces/tests/common/rtt.yaml b/tests/integration/targets/ios_vrf_interfaces/tests/common/rtt.yaml new file mode 100644 index 000000000..7dfed1d48 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/tests/common/rtt.yaml @@ -0,0 +1,55 @@ +--- +- ansible.builtin.debug: + msg: START ios_vrf_interfaces round trip integration tests on connection={{ ansible_connection }} + +- ansible.builtin.include_tasks: _remove_config.yaml + +- block: + - name: Apply the provided configuration (base config) + register: base_config + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + vrf_name: vrf_B + - name: GigabitEthernet3 + - name: GigabitEthernet4 + state: merged + + - name: Gather VRF interfaces facts + cisco.ios.ios_facts: + gather_subset: + - "!all" + - "!min" + gather_network_resources: + - vrf_interfaces + + - name: Apply the provided configuration (config to be reverted) + register: result + cisco.ios.ios_vrf_interfaces: + config: + - name: GigabitEthernet1 + - name: GigabitEthernet2 + - name: GigabitEthernet3 + - name: GigabitEthernet4 + vrf_name: vrf_C + state: overridden + + - ansible.builtin.assert: + that: + - result.changed == true + - result.commands|symmetric_difference(overridden.commands) == [] + + - name: Revert back to base configuration using facts round trip + register: revert + cisco.ios.ios_vrf_interfaces: + config: "{{ ansible_facts['network_resources']['vrf_interfaces'] }}" + state: overridden + + - ansible.builtin.assert: + that: + - revert.changed == true + - revert.commands|symmetric_difference(rtt.commands) == [] + + always: + - ansible.builtin.include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/ios_vrf_interfaces/vars/main.yaml b/tests/integration/targets/ios_vrf_interfaces/vars/main.yaml new file mode 100644 index 000000000..20d971bc3 --- /dev/null +++ b/tests/integration/targets/ios_vrf_interfaces/vars/main.yaml @@ -0,0 +1,101 @@ +--- +gathered: + config: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + vrf_name: "vrf_B" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + +merged: + commands: + - interface GigabitEthernet2 + - vrf forwarding vrf_D + after: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + vrf_name: "vrf_D" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + before: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + +overridden: + commands: + - interface GigabitEthernet2 + - no vrf forwarding vrf_B + - interface GigabitEthernet4 + - vrf forwarding vrf_C + after: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + vrf_name: "vrf_C" + before: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + vrf_name: "vrf_B" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + +parsed: + after: + - name: "GigabitEthernet1" + vrf_name: "vrf_C" + - name: "GigabitEthernet2" + vrf_name: "vrf_D" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + +replaced: + commands: + - interface GigabitEthernet2 + - vrf forwarding vrf_D + before: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + vrf_name: "vrf_B" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + after: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + vrf_name: "vrf_D" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + +deleted: + commands: + - interface GigabitEthernet2 + - no vrf forwarding vrf_B + before: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + vrf_name: "vrf_B" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + after: + - name: "Loopback24" + - name: "GigabitEthernet1" + - name: "GigabitEthernet2" + - name: "GigabitEthernet3" + - name: "GigabitEthernet4" + +rtt: + commands: + - interface GigabitEthernet2 + - vrf forwarding vrf_B + - interface GigabitEthernet4 + - no vrf forwarding vrf_C diff --git a/tests/sanity/ignore-2.19.txt b/tests/sanity/ignore-2.19.txt new file mode 100644 index 000000000..d7ff17ca8 --- /dev/null +++ b/tests/sanity/ignore-2.19.txt @@ -0,0 +1 @@ +plugins/action/ios.py action-plugin-docs # base class for deprecated network platform modules using `connection: local` diff --git a/tests/unit/modules/network/ios/test_ios_bgp_address_family.py b/tests/unit/modules/network/ios/test_ios_bgp_address_family.py index ea5d279f4..d020d45a6 100644 --- a/tests/unit/modules/network/ios/test_ios_bgp_address_family.py +++ b/tests/unit/modules/network/ios/test_ios_bgp_address_family.py @@ -211,6 +211,8 @@ def test_ios_bgp_address_family_merged_idempotent(self): neighbor TEST-PEER-GROUP send-community neighbor TEST-PEER-GROUP next-hop-self all neighbor 2001:db8::1 activate + maximum-secondary-paths eibgp 2 + maximum-paths 12 ! address-family ipv4 multicast table-map test_tableMap filter @@ -251,6 +253,8 @@ def test_ios_bgp_address_family_merged_idempotent(self): { "afi": "ipv4", "bgp": {"redistribute_internal": True}, + "maximum_paths": {"paths": 12}, + "maximum_secondary_paths": {"eibgp": 2}, "neighbors": [ { "neighbor_address": "TEST-PEER-GROUP", @@ -938,6 +942,8 @@ def test_ios_bgp_address_family_rendered(self): afi="ipv4", safi="multicast", vrf="blue", + maximum_paths=dict(paths=12), + maximum_secondary_paths=dict(eibgp=2), aggregate_address=[ dict( address="192.0.2.1", @@ -1023,6 +1029,8 @@ def test_ios_bgp_address_family_rendered(self): "router bgp 65000", "address-family ipv4 multicast vrf blue", "bgp dampening 1 1 1 1", + "maximum-secondary-paths eibgp 2", + "maximum-paths 12", "neighbor 198.51.100.1 remote-as 10", "neighbor 198.51.100.1 activate", "neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive", @@ -1061,6 +1069,8 @@ def test_ios_bgp_address_family_parsed(self): neighbor TEST-PEER-GROUP send-community neighbor TEST-PEER-GROUP next-hop-self all neighbor 2001:db8::1 activate + maximum-secondary-paths eibgp 2 + maximum-paths 12 ! address-family ipv4 multicast table-map test_tableMap filter @@ -1120,6 +1130,8 @@ def test_ios_bgp_address_family_parsed(self): }, }, ], + "maximum_paths": {"paths": 12}, + "maximum_secondary_paths": {"eibgp": 2}, "neighbors": [ { "send_community": {"set": True}, diff --git a/tests/unit/modules/network/ios/test_ios_bgp_global.py b/tests/unit/modules/network/ios/test_ios_bgp_global.py index 16a4c9b5c..947adf2ef 100644 --- a/tests/unit/modules/network/ios/test_ios_bgp_global.py +++ b/tests/unit/modules/network/ios/test_ios_bgp_global.py @@ -286,7 +286,6 @@ def test_ios_bgp_global_merged_idempotent(self): timers bgp 100 200 150 redistribute connected metric 10 neighbor 192.0.2.1 remote-as 100 - neighbor 192.0.2.1 route-map test-route out address-family ipv4 neighbor 192.0.2.28 activate neighbor 172.31.35.140 activate @@ -304,9 +303,8 @@ def test_ios_bgp_global_merged_idempotent(self): redistribute=[dict(connected=dict(set=True, metric=10))], neighbors=[ dict( - address="192.0.2.1", + neighbor_address="192.0.2.1", remote_as=100, - route_map=dict(name="test-route", out=True), ), ], timers=dict(keepalive=100, holdtime=200, min_holdtime=150), @@ -316,6 +314,34 @@ def test_ios_bgp_global_merged_idempotent(self): ) self.execute_module(changed=False, commands=[]) + def test_ios_bgp_global_ebgp_multihop(self): + self.execute_show_command.return_value = dedent( + """\ + router bgp 65000 + neighbor 192.0.2.1 remote-as 100 + neighbor 192.0.2.1 ebgp-multihop 255 + """, + ) + set_module_args( + { + "config": { + "as_number": "65000", + "neighbors": [ + { + "neighbor_address": "192.0.2.1", + "remote_as": "100", + "ebgp_multihop": { + "enable": True, + "hop_count": 255, + }, + }, + ], + }, + "state": "merged", + }, + ) + self.execute_module(changed=False, commands=[]) + def test_ios_bgp_global_merged_fail_msg(self): self.execute_show_command.return_value = dedent( """\ @@ -326,7 +352,6 @@ def test_ios_bgp_global_merged_fail_msg(self): timers bgp 100 200 150 redistribute connected metric 10 neighbor 192.0.2.1 remote-as 100 - neighbor 192.0.2.1 route-map test-route out address-family ipv4 neighbor 192.0.2.28 activate neighbor 172.31.35.140 activate @@ -344,9 +369,8 @@ def test_ios_bgp_global_merged_fail_msg(self): redistribute=[dict(connected=dict(set=True, metric=10))], neighbors=[ dict( - address="192.0.2.1", + neighbor_address="192.0.2.1", remote_as=100, - route_map=dict(name="test-route", out=True), ), ], timers=dict(keepalive=100, holdtime=200, min_holdtime=150), @@ -368,7 +392,6 @@ def test_ios_bgp_global_overridden(self): bgp nopeerup-delay post-boot 10 bgp bestpath compare-routerid bgp advertise-best-external - aggregate-address 192.168.0.11 255.255.0.0 attribute-map map1 timers bgp 100 200 150 redistribute connected metric 10 neighbor 192.0.2.2 remote-as 100 @@ -382,13 +405,6 @@ def test_ios_bgp_global_overridden(self): dict( config=dict( as_number="65000", - aggregate_address=dict( - dict( - address="192.168.0.11", - attribute_map="map1", - netmask="255.255.0.0", - ), - ), aggregate_addresses=[ dict( address="192.168.0.1", @@ -410,7 +426,7 @@ def test_ios_bgp_global_overridden(self): redistribute=[dict(connected=dict(set=True, metric=10))], neighbors=[ dict( - address="192.0.2.1", + neighbor_address="192.0.2.1", remote_as=200, description="replace neighbor", ), @@ -440,7 +456,6 @@ def test_ios_bgp_global_replaced(self): bgp nopeerup-delay post-boot 10 bgp bestpath compare-routerid bgp advertise-best-external - aggregate-address 192.168.0.11 255.255.0.0 attribute-map map1 timers bgp 100 200 150 redistribute connected metric 10 neighbor 192.0.2.2 remote-as 100 @@ -454,13 +469,6 @@ def test_ios_bgp_global_replaced(self): dict( config=dict( as_number="65000", - aggregate_address=dict( - dict( - address="192.168.0.11", - attribute_map="map1", - netmask="255.255.0.0", - ), - ), aggregate_addresses=[ dict( address="192.168.0.1", @@ -486,7 +494,7 @@ def test_ios_bgp_global_replaced(self): redistribute=[dict(connected=dict(set=True, metric=10))], neighbors=[ dict( - address="192.0.2.1", + neighbor_address="192.0.2.1", remote_as=200, description="replace neighbor", ), @@ -520,7 +528,6 @@ def test_ios_bgp_global_replaced_idempotent(self): timers bgp 100 200 150 redistribute connected metric 10 neighbor 192.0.2.1 remote-as 100 - neighbor 192.0.2.1 route-map test-route out address-family ipv4 neighbor 192.0.2.28 activate neighbor 172.31.35.140 activate @@ -538,9 +545,8 @@ def test_ios_bgp_global_replaced_idempotent(self): redistribute=[dict(connected=dict(set=True, metric=10))], neighbors=[ dict( - address="192.0.2.1", + neighbor_address="192.0.2.1", remote_as=100, - route_map=dict(name="test-route", out=True), ), ], timers=dict(keepalive=100, holdtime=200, min_holdtime=150), @@ -610,220 +616,6 @@ def test_ios_bgp_global_purged(self): commands = ["no router bgp 65000"] self.execute_module(changed=True, commands=commands) - def test_deprecated_attributes_rendered(self): - set_module_args( - { - "config": { - "aggregate_address": { - "address": "192.0.2.3", - "attribute_map": "ma", - "netmask": "255.255.0.0", - "summary_only": True, - }, - "as_number": "65000", - "auto_summary": True, - "bgp": { - "additional_paths": {"install": True, "receive": True}, - "bestpath": [ - {"aigp": True}, - {"compare_routerid": True}, - {"med": {"confed": True, "missing_as_worst": True}}, - ], - "inject_map": { - "copy_attributes": True, - "exist_map_name": "mp2", - "name": "map1", - }, - "listen": { - "limit": 200, - "range": { - "ipv4_with_subnet": "192.0.2.9/24", - "peer_group": "mygrp", - }, - }, - "log_neighbor_changes": True, - "nopeerup_delay": [ - { - "cold_boot": 2, - "nsf_switchover": 10, - "post_boot": 22, - "user_initiated": 22, - }, - ], - }, - "bmp": {"buffer_size": 22, "server": 2}, - "distance": { - "bgp": { - "routes_external": 2, - "routes_internal": 3, - "routes_local": 4, - }, - "mbgp": { - "routes_external": 2, - "routes_internal": 3, - "routes_local": 5, - }, - }, - "distribute_list": { - "out": True, - "acl": "5000", - "interface": "Loopback0", - }, - "maximum_paths": {"ibgp": 2, "paths": 2}, - "maximum_secondary_paths": {"ibgp": 22, "paths": 22}, - "neighbors": [ - { - "advertise": {"diverse_path": {"backup": True}}, - "neighbor_address": "192.1.1.1", - "route_reflector_client": True, - }, - { - "neighbor_address": "192.5.5.5", - "remote_as": 64500, - "update_source": "Loopback0", - "route_map": {"name": "rmp1", "in": True}, - }, - { - "neighbor_address": "192.6.6.6", - "remote_as": 64500, - "update_source": "Loop", - }, - { - "neighbor_address": "192.1.1.2", - "local_as": { - "no_prepend": {"replace_as": True, "set": True}, - "number": 56, - "set": True, - }, - }, - { - "activate": True, - "address": "192.0.1.2", - "remote_as": 45000, - "send_community": {"extended": True}, - "password": "new password", - }, - { - "activate": True, - "neighbor_address": "172.21.1.2", - "remote_as": 45000, - }, - {"neighbor_address": "192.0.2.3", "remote_as": 300}, - { - "neighbor_address": "192.0.2.4", - "remote_as": 6553601, - "shutdown": {"set": True, "graceful": 10, "community": 20}, - }, - { - "activate": True, - "advertise": {"additional_paths": {"group_best": True}}, - "ipv6_adddress": "2001:DB8::1037", - }, - { - "tag": "tagged", - "peer_group": "5", - "soft_reconfiguration": True, - "version": 4, - }, - ], - "networks": [ - { - "address": "192.0.2.1", - "backdoor": True, - "netmask": "55.255.0.0", - "route_map": "mp1", - }, - { - "address": "192.0.2.3", - "backdoor": True, - "netmask": "255.255.0.0", - "route_map": "mp2", - }, - { - "address": "192.0.2.0", - "backdoor": True, - "netmask": "255.255.0.0", - "route_map": "mp2", - }, - ], - "redistribute": [ - {"static": {"metric": 33, "route_map": "mp1"}}, - {"application": {"metric": 22, "name": "ap1"}}, - { - "application": { - "metric": 33, - "name": "ap112", - "route_map": "mp1", - }, - }, - {"connected": {"metric": 22}}, - {"mobile": {"metric": 211}}, - ], - "route_server_context": {"description": "good smn server BMP"}, - }, - "state": "rendered", - }, - ) - commands = [ - "router bgp 65000", - "auto-summary", - "bmp buffer-size 22", - "bmp server 2", - "distance bgp 2 3 4", - "distance mbgp 2 3 5", - "maximum-paths 2", - "maximum-paths ibgp 2", - "maximum-secondary-paths 22", - "maximum-secondary-paths ibgp 22", - "description good smn server BMP", - "bgp additional-paths install receive", - "bgp bestpath aigp ignore", - "bgp bestpath compare-routerid", - "bgp bestpath med confed missing-as-worst", - "bgp listen limit 200", - "bgp listen range 192.0.2.9/24 peer-group mygrp", - "bgp log-neighbor-changes", - "bgp nopeerup-delay cold-boot 2", - "bgp nopeerup-delay post-boot 22", - "bgp nopeerup-delay nsf-switchover 10", - "bgp nopeerup-delay user-initiated 22", - "bgp inject-map map1 exist-map mp2 copy-attributes", - "distribute-list 5000 out Loopback0", - "aggregate-address 192.0.2.3 255.255.0.0 summary-only attribute-map ma", - "network 192.0.2.1 mask 55.255.0.0 route-map mp1 backdoor", - "network 192.0.2.3 mask 255.255.0.0 route-map mp2 backdoor", - "network 192.0.2.0 mask 255.255.0.0 route-map mp2 backdoor", - "neighbor 192.1.1.1 advertise diverse-path backup", - "neighbor 192.1.1.1 route-reflector-client", - "neighbor 192.5.5.5 remote-as 64500", - "neighbor 192.5.5.5 update-source Loopback0", - "neighbor 192.5.5.5 route-map rmp1 in", - "neighbor 192.6.6.6 remote-as 64500", - "neighbor 192.6.6.6 update-source Loop", - "neighbor 192.1.1.2 local-as 56 no-prepend replace-as", - "neighbor 192.0.1.2 remote-as 45000", - "neighbor 192.0.1.2 password new password", - "neighbor 192.0.1.2 activate", - "neighbor 192.0.1.2 send-community extended", - "neighbor 172.21.1.2 remote-as 45000", - "neighbor 172.21.1.2 activate", - "neighbor 192.0.2.3 remote-as 300", - "neighbor 192.0.2.4 remote-as 6553601", - "neighbor 192.0.2.4 shutdown graceful 10 community 20", - "neighbor 2001:DB8::1037 activate", - "neighbor 2001:DB8::1037 advertise additional-paths group-best", - "neighbor tagged peer-group 5", - "neighbor tagged soft-reconfiguration inbound", - "neighbor tagged version 4", - "redistribute static metric 33 route-map mp1", - "redistribute application ap1 metric 22", - "redistribute application ap112 metric 33 route-map mp1", - "redistribute connected metric 22", - "redistribute mobile metric 211", - ] - result = self.execute_module(changed=False) - self.assertEqual(sorted(result["rendered"]), sorted(commands)) - def test_ios_bgp_global_parsed(self): set_module_args( dict( @@ -836,6 +628,7 @@ def test_ios_bgp_global_parsed(self): timers bgp 100 200 150 redistribute connected metric 10 neighbor 192.0.2.1 remote-as 100 + neighbor 192.0.2.1 password 7 DEQPITOP101395 neighbor 192.0.2.1 route-map test-route out address-family ipv4 neighbor 192.0.2.28 activate @@ -860,7 +653,13 @@ def test_ios_bgp_global_parsed(self): { "remote_as": "100", "neighbor_address": "192.0.2.1", - "route_maps": [{"name": "test-route", "out": True}], + "route_maps": [ + {"name": "test-route", "out": True}, + ], + "password_options": { + "encryption": 7, + "pass_key": "DEQPITOP101395", + }, }, ], } diff --git a/tests/unit/modules/network/ios/test_ios_interfaces.py b/tests/unit/modules/network/ios/test_ios_interfaces.py index 63ee2341b..eeafc72f4 100644 --- a/tests/unit/modules/network/ios/test_ios_interfaces.py +++ b/tests/unit/modules/network/ios/test_ios_interfaces.py @@ -678,6 +678,10 @@ def test_ios_interfaces_rendered(self): "name": "fiveGigabitEthernet", "description": "Ansible UT FiveGigabitEthernet", }, + { + "name": "fiftyGigabitEthernet", + "description": "Ansible UT for fiftyGigabitEthernet", + }, { "name": "ethernet1", "description": "Ansible UT Ethernet", @@ -750,6 +754,9 @@ def test_ios_interfaces_rendered(self): "interface FiveGigabitEthernet", "description Ansible UT FiveGigabitEthernet", "no shutdown", + "interface FiftyGigabitEthernet", + "description Ansible UT for fiftyGigabitEthernet", + "no shutdown", "interface Ethernet1", "description Ansible UT Ethernet", "no shutdown", diff --git a/tests/unit/modules/network/ios/test_ios_l2_interfaces.py b/tests/unit/modules/network/ios/test_ios_l2_interfaces.py index e84846aeb..a1cee74ad 100644 --- a/tests/unit/modules/network/ios/test_ios_l2_interfaces.py +++ b/tests/unit/modules/network/ios/test_ios_l2_interfaces.py @@ -793,3 +793,188 @@ def test_ios_l2_interfaces_fiveGibBit(self): result = self.execute_module(changed=True) self.maxDiff = None self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_ios_l2_interfaces_trunk_multiline_merge(self): + self.execute_show_command.return_value = dedent( + """\ + interface GigabitEthernet0/2 + switchport trunk allowed vlan 10-20,40 + switchport trunk encapsulation dot1q + switchport trunk native vlan 10 + switchport trunk pruning vlan 10,20 + switchport mode trunk + """, + ) + set_module_args( + dict( + config=[ + dict( + mode="trunk", + name="GigabitEthernet0/2", + trunk=dict( + allowed_vlans=["60-70"] + [str(vlan) for vlan in range(101, 500, 2)], + encapsulation="isl", + native_vlan=20, + pruning_vlans=["10", "20", "30-40"], + ), + ), + ], + state="merged", + ), + ) + commands = [ + "interface GigabitEthernet0/2", + "switchport trunk encapsulation isl", + "switchport trunk native vlan 20", + "switchport trunk allowed vlan add 60-70,101,103,105,107,109,111,113,115,117,119,121,123,125,127,129,131,133,135,137,139,141,143,145,147," + + "149,151,153,155,157,159,161,163,165,167,169,171,173,175,177,179,181,183,185,187,189,191,193,195,197,199,201,203,205", + "switchport trunk allowed vlan add 207,209,211,213,215,217,219,221,223,225,227,229,231,233,235,237,239,241,243,245,247,249,251,253,255,257," + + "259,261,263,265,267,269,271,273,275,277,279,281,283,285,287,289,291,293,295,297,299,301,303,305,307,309,311,313,315", + "switchport trunk allowed vlan add 317,319,321,323,325,327,329,331,333,335,337,339,341,343,345,347,349,351,353,355,357,359,361,363,365,367," + + "369,371,373,375,377,379,381,383,385,387,389,391,393,395,397,399,401,403,405,407,409,411,413,415,417,419,421,423,425", + "switchport trunk allowed vlan add 427,429,431,433,435,437,439,441,443,445,447,449,451,453,455,457,459,461,463,465,467,469,471,473,475,477," + + "479,481,483,485,487,489,491,493,495,497,499", + "switchport trunk pruning vlan add 30-40", + ] + result = self.execute_module(changed=True) + self.maxDiff = None + self.assertEqual(result["commands"], commands) + + def test_ios_l2_interfaces_trunk_multiline_replace(self): + self.execute_show_command.return_value = dedent( + """\ + interface GigabitEthernet0/2 + switchport trunk allowed vlan 10-20,40 + switchport trunk encapsulation dot1q + switchport trunk native vlan 10 + switchport trunk pruning vlan 10,20 + switchport mode trunk + """, + ) + set_module_args( + dict( + config=[ + dict( + mode="trunk", + name="GigabitEthernet0/2", + trunk=dict( + allowed_vlans=["60-70"] + [str(vlan) for vlan in range(101, 500, 2)], + encapsulation="isl", + native_vlan=20, + pruning_vlans=["10", "20", "30-40"], + ), + ), + ], + state="replaced", + ), + ) + commands = [ + "interface GigabitEthernet0/2", + "switchport trunk encapsulation isl", + "switchport trunk native vlan 20", + "switchport trunk allowed vlan remove 10-20,40", + "switchport trunk allowed vlan add 60-70,101,103,105,107,109,111,113,115,117,119,121,123,125,127,129,131,133,135,137,139,141,143,145,147," + + "149,151,153,155,157,159,161,163,165,167,169,171,173,175,177,179,181,183,185,187,189,191,193,195,197,199,201,203,205", + "switchport trunk allowed vlan add 207,209,211,213,215,217,219,221,223,225,227,229,231,233,235,237,239,241,243,245,247,249,251,253,255,257," + + "259,261,263,265,267,269,271,273,275,277,279,281,283,285,287,289,291,293,295,297,299,301,303,305,307,309,311,313,315", + "switchport trunk allowed vlan add 317,319,321,323,325,327,329,331,333,335,337,339,341,343,345,347,349,351,353,355,357,359,361,363,365,367," + + "369,371,373,375,377,379,381,383,385,387,389,391,393,395,397,399,401,403,405,407,409,411,413,415,417,419,421,423,425", + "switchport trunk allowed vlan add 427,429,431,433,435,437,439,441,443,445,447,449,451,453,455,457,459,461,463,465,467,469,471,473,475,477," + + "479,481,483,485,487,489,491,493,495,497,499", + "switchport trunk pruning vlan add 30-40", + ] + result = self.execute_module(changed=True) + self.maxDiff = None + self.assertEqual(result["commands"], commands) + + def test_ios_l2_interfaces_trunk_multiline_replace_init(self): + self.execute_show_command.return_value = dedent( + """\ + interface GigabitEthernet0/2 + switchport trunk encapsulation dot1q + switchport trunk native vlan 10 + switchport trunk pruning vlan 10,20 + switchport mode trunk + """, + ) + set_module_args( + dict( + config=[ + dict( + mode="trunk", + name="GigabitEthernet0/2", + trunk=dict( + allowed_vlans=["60-70"] + [str(vlan) for vlan in range(101, 500, 2)], + encapsulation="isl", + native_vlan=20, + pruning_vlans=["10", "20", "30-40"], + ), + ), + ], + state="replaced", + ), + ) + commands = [ + "interface GigabitEthernet0/2", + "switchport trunk encapsulation isl", + "switchport trunk native vlan 20", + "switchport trunk allowed vlan 60-70,101,103,105,107,109,111,113,115,117,119,121,123,125,127,129,131,133,135,137,139,141,143,145,147,149,151,153," + + "155,157,159,161,163,165,167,169,171,173,175,177,179,181,183,185,187,189,191,193,195,197,199,201,203,205", + "switchport trunk allowed vlan add 207,209,211,213,215,217,219,221,223,225,227,229,231,233,235,237,239,241,243,245,247,249,251,253,255,257,259," + + "261,263,265,267,269,271,273,275,277,279,281,283,285,287,289,291,293,295,297,299,301,303,305,307,309,311,313,315", + "switchport trunk allowed vlan add 317,319,321,323,325,327,329,331,333,335,337,339,341,343,345,347,349,351,353,355,357,359,361,363,365,367,369," + + "371,373,375,377,379,381,383,385,387,389,391,393,395,397,399,401,403,405,407,409,411,413,415,417,419,421,423,425", + "switchport trunk allowed vlan add 427,429,431,433,435,437,439,441,443,445,447,449,451,453,455,457,459,461,463,465,467,469,471,473,475,477,479," + + "481,483,485,487,489,491,493,495,497,499", + "switchport trunk pruning vlan add 30-40", + ] + result = self.execute_module(changed=True) + self.maxDiff = None + self.assertEqual(result["commands"], commands) + + def test_ios_l2_interfaces_trunk_multiline_overridden(self): + self.execute_show_command.return_value = dedent( + """\ + interface GigabitEthernet0/2 + switchport trunk allowed vlan 10-20,40 + switchport trunk encapsulation dot1q + switchport trunk native vlan 10 + switchport trunk pruning vlan 10,20 + switchport mode trunk + """, + ) + set_module_args( + dict( + config=[ + dict( + mode="trunk", + name="GigabitEthernet0/2", + trunk=dict( + allowed_vlans=["60-70"] + [str(vlan) for vlan in range(101, 500, 2)], + encapsulation="isl", + native_vlan=20, + pruning_vlans=["10", "20", "30-40"], + ), + ), + ], + state="overridden", + ), + ) + commands = [ + "interface GigabitEthernet0/2", + "switchport trunk encapsulation isl", + "switchport trunk native vlan 20", + "switchport trunk allowed vlan remove 10-20,40", + "switchport trunk allowed vlan add 60-70,101,103,105,107,109,111,113,115,117,119,121,123,125,127,129,131,133,135,137,139,141,143,145,147,149,151," + + "153,155,157,159,161,163,165,167,169,171,173,175,177,179,181,183,185,187,189,191,193,195,197,199,201,203,205", + "switchport trunk allowed vlan add 207,209,211,213,215,217,219,221,223,225,227,229,231,233,235,237,239,241,243,245,247,249,251,253,255,257,259," + + "261,263,265,267,269,271,273,275,277,279,281,283,285,287,289,291,293,295,297,299,301,303,305,307,309,311,313,315", + "switchport trunk allowed vlan add 317,319,321,323,325,327,329,331,333,335,337,339,341,343,345,347,349,351,353,355,357,359,361,363,365,367,369," + + "371,373,375,377,379,381,383,385,387,389,391,393,395,397,399,401,403,405,407,409,411,413,415,417,419,421,423,425", + "switchport trunk allowed vlan add 427,429,431,433,435,437,439,441,443,445,447,449,451,453,455,457,459,461,463,465,467,469,471,473,475,477,479," + + "481,483,485,487,489,491,493,495,497,499", + "switchport trunk pruning vlan add 30-40", + ] + result = self.execute_module(changed=True) + self.maxDiff = None + self.assertEqual(result["commands"], commands) diff --git a/tests/unit/modules/network/ios/test_ios_service.py b/tests/unit/modules/network/ios/test_ios_service.py index 69af87eda..150c04e19 100644 --- a/tests/unit/modules/network/ios/test_ios_service.py +++ b/tests/unit/modules/network/ios/test_ios_service.py @@ -129,8 +129,8 @@ def test_ios_service_merged(self): } merged = [ "service password-encryption", - "service timestamps debug uptime mesc", - "service timestamps log datetime mesc localtime show-timezone year", + "service timestamps debug uptime msec", + "service timestamps log datetime msec localtime show-timezone year", ] playbook["state"] = "merged" set_module_args(playbook) @@ -217,7 +217,7 @@ def test_ios_service_overridden(self): "service password-encryption", "service tcp-keepalives-in", "service tcp-keepalives-out", - "service timestamps log datetime mesc localtime show-timezone year", + "service timestamps log datetime msec localtime show-timezone year", "service timestamps debug datetime", ] playbook["state"] = "overridden" @@ -276,8 +276,8 @@ def test_ios_service_replaced(self): "service password-encryption", "service tcp-keepalives-in", "service tcp-keepalives-out", - "service timestamps log datetime mesc localtime show-timezone year", - "service timestamps debug datetime mesc localtime", + "service timestamps log datetime msec localtime show-timezone year", + "service timestamps debug datetime msec localtime", ] playbook["state"] = "replaced" set_module_args(playbook) @@ -500,7 +500,7 @@ def test_ios_service_rendered(self): "service tcp-keepalives-in", "service tcp-keepalives-out", "service timestamps debug uptime localtime", - "service timestamps log datetime mesc localtime show-timezone year", + "service timestamps log datetime msec localtime show-timezone year", ] result = self.execute_module(changed=False) self.maxDiff = None diff --git a/tests/unit/modules/network/ios/test_ios_snmp_server.py b/tests/unit/modules/network/ios/test_ios_snmp_server.py index 1c60a6628..a3e3a2c21 100644 --- a/tests/unit/modules/network/ios/test_ios_snmp_server.py +++ b/tests/unit/modules/network/ios/test_ios_snmp_server.py @@ -2105,3 +2105,68 @@ def test_ios_snmpv3_user_server_overridden(self): set_module_args(playbook) result = self.execute_module(changed=True) self.assertEqual(sorted(result["commands"]), sorted(overridden)) + + def test_ios_snmp_server_configuration_overridden(self): + self.execute_show_command.return_value = dedent( + """\ + snmp-server community community_name RO + snmp-server packetsize 400 + snmp-server location in the server room + """, + ) + self.execute_show_command_user.return_value = "" + playbook = { + "config": { + "location": "in the racks", + "contact": "john@doe.org", + "packet_size": 500, + "communities": [ + { + "acl_v4": "ADMIN-SUP", + "name": "community_name", + "ro": True, + }, + ], + }, + } + expected_commands = [ + "no snmp-server community community_name ro", + "snmp-server community community_name ro ADMIN-SUP", + "snmp-server contact john@doe.org", + "snmp-server location in the racks", + "snmp-server packetsize 500", + ] + playbook["state"] = "overridden" + set_module_args(playbook) + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(expected_commands)) + + def test_ios_snmp_server_configuration_idempotence(self): + self.execute_show_command.return_value = dedent( + """\ + snmp-server community community_name RO ADMIN-SUP + snmp-server packetsize 500 + snmp-server location in the racks + snmp-server contact john@doe.org + """, + ) + self.execute_show_command_user.return_value = "" + playbook = { + "config": { + "location": "in the racks", + "contact": "john@doe.org", + "packet_size": 500, + "communities": [ + { + "acl_v4": "ADMIN-SUP", + "name": "community_name", + "ro": True, + }, + ], + }, + "state": "merged", + } + set_module_args(playbook) + result = self.execute_module() + self.assertEqual(result["changed"], False) + self.assertEqual(result["commands"], []) diff --git a/tests/unit/modules/network/ios/test_ios_static_routes.py b/tests/unit/modules/network/ios/test_ios_static_routes.py index 4bb89cc79..c33e1c42c 100644 --- a/tests/unit/modules/network/ios/test_ios_static_routes.py +++ b/tests/unit/modules/network/ios/test_ios_static_routes.py @@ -2214,3 +2214,95 @@ def test_ios_static_route_gathered(self): self.maxDiff = None print(result["gathered"]) self.assertEqual(sorted(result["gathered"]), sorted(gathered)) + + def test_ios_static_route_gathered_2(self): + self.execute_show_command.return_value = dedent( + """\ + ip route 198.51.100.0 255.255.255.0 198.51.101.1 175 tag 70 name replaced_route multicast + ip route 192.168.1.0 255.255.255.0 GigabitEthernet0/1.22 10.0.0.1 tag 30 + """, + ) + set_module_args(dict(state="gathered")) + gathered = [ + { + "address_families": [ + { + "afi": "ipv4", + "routes": [ + { + "next_hops": [ + { + "forward_router_address": "198.51.101.1", + "distance_metric": 175, + "tag": 70, + "name": "replaced_route", + "multicast": True, + }, + ], + "dest": "198.51.100.0/24", + }, + { + "next_hops": [ + { + "interface": "GigabitEthernet0/1.22", + "forward_router_address": "10.0.0.1", + "tag": 30, + }, + ], + "dest": "192.168.1.0/24", + }, + ], + }, + ], + }, + ] + result = self.execute_module(changed=False) + self.assertEqual(sorted(result["gathered"]), sorted(gathered)) + + def test_ios_static_route_overridden_2(self): + self.execute_show_command.return_value = dedent( + """\ + ip route 198.51.100.0 255.255.255.0 198.51.101.1 175 tag 70 name replaced_route multicast + ip route 192.168.1.0 255.255.255.0 GigabitEthernet0/1.22 10.0.0.1 tag 30 + """, + ) + set_module_args( + dict( + config=[ + { + "address_families": [ + { + "afi": "ipv4", + "routes": [ + { + "next_hops": [ + { + "forward_router_address": "198.51.101.20", + "distance_metric": 175, + "tag": 70, + "name": "replaced_route", + "track": 150, + }, + { + "forward_router_address": "198.51.101.3", + "name": "merged_route_3", + }, + ], + "dest": "198.51.100.0/24", + }, + ], + }, + ], + }, + ], + state="overridden", + ), + ) + commands = [ + "ip route 198.51.100.0 255.255.255.0 198.51.101.20 175 tag 70 name replaced_route track 150", + "ip route 198.51.100.0 255.255.255.0 198.51.101.3 name merged_route_3", + "no ip route 198.51.100.0 255.255.255.0 198.51.101.1 175 tag 70 name replaced_route multicast", + "no ip route 192.168.1.0 255.255.255.0 GigabitEthernet0/1.22 10.0.0.1 tag 30", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) diff --git a/tests/unit/modules/network/ios/test_ios_vlans.py b/tests/unit/modules/network/ios/test_ios_vlans.py index 10b30e140..a48a6fd16 100644 --- a/tests/unit/modules/network/ios/test_ios_vlans.py +++ b/tests/unit/modules/network/ios/test_ios_vlans.py @@ -511,42 +511,13 @@ def test_ios_vlans_overridden(self): ) result = self.execute_module(changed=True) commands = [ - "vlan 1", - "no name default", - "no state active", - "no mtu 1500", - "shutdown", - "vlan 150", - "no name VLAN0150", - "no state active", - "no mtu 1500", - "no remote-span", - "shutdown", - "vlan 888", - "no name a_very_long_vlan_name_a_very_long_vlan_name", - "no state active", - "no mtu 1500", - "shutdown", - "vlan 1002", - "no name fddi-default", - "no state active", - "no mtu 1500", - "shutdown", - "vlan 1003", - "no name trcrf-default", - "no state active", - "no mtu 4472", - "shutdown", - "vlan 1004", - "no name fddinet-default", - "no state active", - "no mtu 1500", - "shutdown", - "vlan 1005", - "no name trbrf-default", - "no state active", - "no mtu 4472", - "shutdown", + "no vlan 1", + "no vlan 150", + "no vlan 888", + "no vlan 1002", + "no vlan 1003", + "no vlan 1004", + "no vlan 1005", "vlan 200", "name test_vlan_200", "state active", @@ -558,6 +529,13 @@ def test_ios_vlans_overridden(self): "no mtu 610", "remote-span", "shutdown", + "no vlan configuration 1", + "no vlan configuration 150", + "no vlan configuration 888", + "no vlan configuration 1002", + "no vlan configuration 1003", + "no vlan configuration 1004", + "no vlan configuration 1005", ] self.assertEqual(result["commands"], commands) @@ -1225,14 +1203,14 @@ def test_ios_vlans_config_overridden(self): ) result = self.execute_module(changed=True) commands = [ - "vlan configuration 201", - "no member evpn-instance 201 vni 10201", - "vlan configuration 202", - "no member evpn-instance 202 vni 10202", - "vlan configuration 901", - "no member vni 50901", - "vlan configuration 902", - "no member vni 50902", + "no vlan 201", + "no vlan 202", + "no vlan 901", + "no vlan 902", + "no vlan configuration 201", + "no vlan configuration 202", + "no vlan configuration 901", + "no vlan configuration 902", "vlan configuration 101", "member evpn-instance 102 vni 10102", "vlan configuration 102", @@ -1299,7 +1277,7 @@ def test_ios_purged_vlans_config(self): ), ) result = self.execute_module(changed=True) - commands = ["no vlan configuration 101"] + commands = ["no vlan 101", "no vlan configuration 101"] self.assertEqual(result["commands"], commands) def test_ios_vlans_config_rendered(self): diff --git a/tests/unit/modules/network/ios/test_ios_vrf_global.py b/tests/unit/modules/network/ios/test_ios_vrf_global.py new file mode 100644 index 000000000..4b04d6b85 --- /dev/null +++ b/tests/unit/modules/network/ios/test_ios_vrf_global.py @@ -0,0 +1,485 @@ +# (c) 2024, Ansible by Red Hat, inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type +from textwrap import dedent +from unittest.mock import patch + +from ansible_collections.cisco.ios.plugins.modules import ios_vrf_global +from ansible_collections.cisco.ios.tests.unit.modules.utils import set_module_args + +from .ios_module import TestIosModule + + +class TestIosVrfGlobalModule(TestIosModule): + """Test the ios_vrf_global module.""" + + module = ios_vrf_global + + def setUp(self): + """Set up for ios_vrf_global module tests.""" + super(TestIosVrfGlobalModule, self).setUp() + + self.mock_get_resource_connection_facts = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base." + "get_resource_connection", + ) + self.get_resource_connection_facts = self.mock_get_resource_connection_facts.start() + + self.mock_execute_show_command = patch( + "ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vrf_global.vrf_global." + "Vrf_globalFacts.get_config", + ) + self.execute_show_command = self.mock_execute_show_command.start() + + def tearDown(self): + super(TestIosVrfGlobalModule, self).tearDown() + self.mock_get_resource_connection_facts.stop() + self.mock_execute_show_command.stop() + + def test_ios_vrf_global_merged(self): + self.execute_show_command.return_value = dedent( + """\ + vrf definition test + description This is test VRF + ipv4 multicast multitopology + ipv6 multicast multitopology + rd 2:3 + route-target export 192.0.2.0:100 + route-target import 192.0.2.3:300 + vnet tag 34 + vpn id 3:4 + """, + ) + + set_module_args( + dict( + config=dict( + vrfs=[ + dict( + name="VRF2", + description="This is a test VRF for merged state", + ipv4=dict(multicast=dict(multitopology=True)), + ipv6=dict(multicast=dict(multitopology=True)), + rd="2:3", + route_target=dict( + export="192.0.2.1:400", + import_config="192.0.2.6:400", + ), + vnet=dict(tag=200), + vpn=dict(id="2:45"), + ), + ], + ), + state="merged", + ), + ) + commands = [ + "vrf definition VRF2", + "description This is a test VRF for merged state", + "ipv4 multicast multitopology", + "ipv6 multicast multitopology", + "rd 2:3", + "route-target export 192.0.2.1:400", + "route-target import 192.0.2.6:400", + "vnet tag 200", + "vpn id 2:45", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_ios_vrf_global_merged_idempotent(self): + self.execute_show_command.return_value = dedent( + """\ + vrf definition VRF2 + description This is a test VRF for merged state + ipv4 multicast multitopology + ipv6 multicast multitopology + rd 2:3 + route-target export 192.0.2.1:400 + route-target import 192.0.2.6:400 + vnet tag 200 + vpn id 2:45 + """, + ) + set_module_args( + dict( + config=dict( + vrfs=[ + dict( + name="VRF2", + description="This is a test VRF for merged state", + ipv4=dict(multicast=dict(multitopology=True)), + ipv6=dict(multicast=dict(multitopology=True)), + rd="2:3", + route_target=dict( + export="192.0.2.1:400", + import_config="192.0.2.6:400", + ), + vnet=dict(tag=200), + vpn=dict(id="2:45"), + ), + ], + ), + state="merged", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_ios_vrf_global_overridden(self): + self.execute_show_command.return_value = dedent( + """\ + vrf definition VRF2 + description This is a test VRF for merged state + ipv4 multicast multitopology + ipv6 multicast multitopology + rd 2:3 + route-target export 192.0.2.1:400 + route-target import 192.0.2.6:400 + vnet tag 200 + vpn id 2:45 + """, + ) + set_module_args( + dict( + config=dict( + vrfs=[ + dict( + name="VRF2", + ), + dict( + name="VRF6", + description="VRF6 description", + ipv4=dict(multicast=dict(multitopology=True)), + ipv6=dict(multicast=dict(multitopology=True)), + rd="6:7", + route_target=dict( + export="192.0.2.2:300", + import_config="192.0.2.3:400", + ), + vnet=dict(tag=500), + vpn=dict(id="4:5"), + ), + ], + ), + state="overridden", + ), + ) + commands = [ + "vrf definition VRF2", + "no description This is a test VRF for merged state", + "no ipv4 multicast multitopology", + "no ipv6 multicast multitopology", + "no rd 2:3", + "no route-target export 192.0.2.1:400", + "no route-target import 192.0.2.6:400", + "no vnet tag 200", + "no vpn id 2:45", + "vrf definition VRF6", + "description VRF6 description", + "ipv4 multicast multitopology", + "ipv6 multicast multitopology", + "rd 6:7", + "route-target export 192.0.2.2:300", + "route-target import 192.0.2.3:400", + "vnet tag 500", + "vpn id 4:5", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_ios_vrf_global_replaced(self): + self.execute_show_command.return_value = dedent( + """\ + vrf definition VRF2 + vrf definition VRF6 + description VRF6 description + ipv4 multicast multitopology + ipv6 multicast multitopology + rd 6:7 + route-target export 192.0.2.2:300 + route-target import 192.0.2.3:400 + vnet tag 500 + vpn id 4:5 + """, + ) + set_module_args( + dict( + config=dict( + vrfs=[ + dict( + name="VRF2", + ), + dict( + name="VRF6", + description="VRF6 description", + ipv4=dict(multicast=dict(multitopology=True)), + ipv6=dict(multicast=dict(multitopology=True)), + rd="6:7", + route_target=dict( + export="192.0.2.2:300", + import_config="192.0.2.3:400", + ), + vnet=dict(tag=500), + vpn=dict(id="4:5"), + ), + dict( + name="VRF7", + description="VRF7 description", + ipv4=dict(multicast=dict(multitopology=True)), + ipv6=dict(multicast=dict(multitopology=True)), + rd="7:8", + route_target=dict( + export="198.51.100.2:500", + import_config="198.51.100.5:400", + ), + vnet=dict(tag=300), + vpn=dict(id="2:45"), + ), + ], + ), + state="replaced", + ), + ) + commands = [ + "vrf definition VRF7", + "description VRF7 description", + "ipv4 multicast multitopology", + "ipv6 multicast multitopology", + "rd 7:8", + "route-target export 198.51.100.2:500", + "route-target import 198.51.100.5:400", + "vnet tag 300", + "vpn id 2:45", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_ios_vrf_global_replaced_idempotent(self): + self.execute_show_command.return_value = dedent( + """\ + vrf definition VRF2 + vrf definition VRF6 + vnet tag 500 + description VRF6 description + ipv4 multicast multitopology + ipv6 multicast multitopology + rd 6:7 + vpn id 4:5 + route-target export 192.0.2.2:300 + route-target import 192.0.2.3:400 + vrf definition VRF7 + vnet tag 300 + description VRF7 description + ipv4 multicast multitopology + ipv6 multicast multitopology + rd 7:8 + vpn id 2:45 + route-target export 198.51.100.2:500 + route-target import 198.51.100.5:400 + """, + ) + set_module_args( + dict( + config=dict( + vrfs=[ + dict( + name="VRF2", + ), + dict( + name="VRF6", + description="VRF6 description", + ipv4=dict(multicast=dict(multitopology=True)), + ipv6=dict(multicast=dict(multitopology=True)), + rd="6:7", + route_target=dict( + export="192.0.2.2:300", + import_config="192.0.2.3:400", + ), + vnet=dict(tag=500), + vpn=dict(id="4:5"), + ), + dict( + name="VRF7", + description="VRF7 description", + ipv4=dict(multicast=dict(multitopology=True)), + ipv6=dict(multicast=dict(multitopology=True)), + rd="7:8", + route_target=dict( + export="198.51.100.2:500", + import_config="198.51.100.5:400", + ), + vnet=dict(tag=300), + vpn=dict(id="2:45"), + ), + ], + ), + state="replaced", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_ios_vrf_global_deleted(self): + self.execute_show_command.return_value = dedent( + """\ + vrf definition VRF2 + vrf definition VRF6 + vnet tag 500 + description VRF6 description + ipv4 multicast multitopology + ipv6 multicast multitopology + rd 6:7 + vpn id 4:5 + route-target export 192.0.2.2:300 + route-target import 192.0.2.3:400 + vrf definition VRF7 + vnet tag 300 + description VRF7 description + ipv4 multicast multitopology + ipv6 multicast multitopology + rd 7:8 + vpn id 2:45 + route-target export 198.51.100.2:500 + route-target import 198.51.100.5:400 + """, + ) + set_module_args( + dict( + config=dict( + vrfs=[ + { + "name": "VRF6", + }, + { + "name": "VRF7", + }, + ], + ), + state="deleted", + ), + ) + commands = [ + "vrf definition VRF6", + "no description VRF6 description", + "no ipv4 multicast multitopology", + "no ipv6 multicast multitopology", + "no rd 6:7", + "no route-target export 192.0.2.2:300", + "no route-target import 192.0.2.3:400", + "no vnet tag 500", + "no vpn id 4:5", + "vrf definition VRF7", + "no description VRF7 description", + "no ipv4 multicast multitopology", + "no ipv6 multicast multitopology", + "no rd 7:8", + "no route-target export 198.51.100.2:500", + "no route-target import 198.51.100.5:400", + "no vnet tag 300", + "no vpn id 2:45", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_ios_vrf_global_deleted_empty(self): + self.execute_show_command.return_value = dedent( + """\ + """, + ) + set_module_args(dict(config=dict(), state="deleted")) + result = self.execute_module(changed=False) + self.assertEqual(result["commands"], []) + + def test_ios_vrf_global_purged(self): + self.execute_show_command.return_value = dedent( + """\ + vrf definition VRF7 + description VRF7 description + ipv4 multicast multitopology + ipv6 multicast multitopology + rd 7:8 + route-target export 198.51.100.2:500 + route-target import 198.51.100.5:400 + vnet tag 300 + vpn id 2:45 + """, + ) + set_module_args(dict(state="purged")) + commands = ["no vrf definition VRF7"] + self.execute_module(changed=True, commands=commands) + + def test_ios_vrf_global_rendered(self): + set_module_args( + dict( + config={ + "vrfs": [ + dict( + name="VRF2", + description="This is a test VRF for rendered state", + ipv4=dict(multicast=dict(multitopology=True)), + ipv6=dict(multicast=dict(multitopology=True)), + rd="2:3", + route_target=dict( + export="192.0.2.1:400", + import_config="192.0.2.6:400", + ), + vnet=dict(tag=200), + vpn=dict(id="2:45"), + ), + ], + }, + state="rendered", + ), + ) + commands = [ + "vrf definition VRF2", + "description This is a test VRF for rendered state", + "ipv4 multicast multitopology", + "ipv6 multicast multitopology", + "rd 2:3", + "route-target export 192.0.2.1:400", + "route-target import 192.0.2.6:400", + "vnet tag 200", + "vpn id 2:45", + ] + result = self.execute_module(changed=False) + self.assertEqual(sorted(result["rendered"]), sorted(commands)) + + def test_ios_vrf_global_parsed(self): + set_module_args( + dict( + running_config=dedent( + """\ + vrf definition test + description This is test VRF + ipv4 multicast multitopology + ipv6 multicast multitopology + rd 2:3 + route-target export 192.0.2.0:100 + route-target import 192.0.2.3:300 + vnet tag 34 + vpn id 3:4 + """, + ), + state="parsed", + ), + ) + result = self.execute_module(changed=False) + parsed_list = { + "vrfs": [ + { + "name": "test", + "description": "This is test VRF", + "ipv4": {"multicast": {"multitopology": True}}, + "ipv6": {"multicast": {"multitopology": True}}, + "rd": "2:3", + "route_target": {"export": "192.0.2.0:100", "import_config": "192.0.2.3:300"}, + "vnet": {"tag": 34}, + "vpn": {"id": "3:4"}, + }, + ], + } + self.assertEqual(parsed_list, result["parsed"]) diff --git a/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py new file mode 100644 index 000000000..c35f820ce --- /dev/null +++ b/tests/unit/modules/network/ios/test_ios_vrf_interfaces.py @@ -0,0 +1,461 @@ +# (c) 2024 Red Hat Inc. +# +# 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 . + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from textwrap import dedent +from unittest.mock import patch + +from ansible_collections.cisco.ios.plugins.modules import ios_vrf_interfaces +from ansible_collections.cisco.ios.tests.unit.modules.utils import set_module_args + +from .ios_module import TestIosModule + + +class TestIosVrfInterfacesModule(TestIosModule): + module = ios_vrf_interfaces + + def setUp(self): + super(TestIosVrfInterfacesModule, self).setUp() + + self.mock_get_resource_connection = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base." + "get_resource_connection", + ) + self.get_resource_connection = self.mock_get_resource_connection.start() + + self.mock_get_config = patch( + "ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vrf_interfaces.vrf_interfaces." + "Vrf_interfacesFacts.get_vrf_interfaces_data", + ) + self.get_config = self.mock_get_config.start() + + def tearDown(self): + super(TestIosVrfInterfacesModule, self).tearDown() + self.get_resource_connection.stop() + self.get_config.stop() + + def test_ios_vrf_interfaces_merged_idempotent(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + no ip address + shutdown + ! + + interface GigabitEthernet3 + no ip address + shutdown + ! + + interface GigabitEthernet4 + no ip address + shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3", "vrf_name": "testvrf2"}, + {"name": "GigabitEthernet3", "vrf_name": "testvrf2"}, + {"name": "GigabitEthernet4", "vrf_name": "testvrf1"}, + ], + state="merged", + ), + ) + commands = [ + "interface GigabitEthernet4", + "vrf forwarding testvrf1", + "interface GigabitEthernet3", + "vrf forwarding testvrf2", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_ios_vrf_interfaces_merged(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + no ip address + shutdown + ! + + interface GigabitEthernet3 + no ip address + shutdown + ! + + interface GigabitEthernet4 + no ip address + shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3"}, + {"name": "GigabitEthernet4", "vrf_name": "testvrf1"}, + ], + state="merged", + ), + ) + commands = [ + "interface GigabitEthernet4", + "vrf forwarding testvrf1", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_ios_vrf_interfaces_replaced(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + no ip address + shutdown + ! + + interface GigabitEthernet3 + no ip address + shutdown + ! + + interface GigabitEthernet4 + vrf forwarding vrf_2 + no shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3"}, + {"name": "GigabitEthernet4", "vrf_name": "testvrf1_replaced"}, + ], + state="replaced", + ), + ) + commands = [ + "interface GigabitEthernet4", + "vrf forwarding testvrf1_replaced", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_ios_vrf_interfaces_deleted(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + no ip address + shutdown + ! + + interface GigabitEthernet3 + vrf forwarding vrf_3 + no shutdown + ! + + interface GigabitEthernet4 + vrf forwarding vrf_2 + no shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3"}, + {"name": "GigabitEthernet4"}, + ], + state="replaced", + ), + ) + commands = [ + "interface GigabitEthernet4", + "no vrf forwarding vrf_2", + "interface GigabitEthernet3", + "no vrf forwarding vrf_3", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_ios_vrf_interfaces_parsed(self): + set_module_args( + dict( + running_config=dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + vrf forwarding vrf_1 + shutdown + ! + + interface GigabitEthernet3 + no shutdown + ! + + interface GigabitEthernet4 + vrf forwarding vrf_2 + no shutdown + ! + """, + ), + state="parsed", + ), + ) + parsed_list = [ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2", "vrf_name": "vrf_1"}, + {"name": "GigabitEthernet3"}, + {"name": "GigabitEthernet4", "vrf_name": "vrf_2"}, + ] + result = self.execute_module(changed=False) + + self.assertEqual(result["parsed"], parsed_list) + + def test_ios_vrf_interfaces_overridden(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + vrf forwarding vrf_1 + shutdown + ! + + interface GigabitEthernet3 + vrf forwarding vrf_2 + shutdown + ! + + interface GigabitEthernet4 + no shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3"}, + {"name": "GigabitEthernet4", "vrf_name": "vrf_5"}, + ], + state="replaced", + ), + ) + commands = [ + "interface GigabitEthernet2", + "no vrf forwarding vrf_1", + "interface GigabitEthernet3", + "no vrf forwarding vrf_2", + "interface GigabitEthernet4", + "vrf forwarding vrf_5", + ] + result = self.execute_module(changed=True) + self.assertEqual(sorted(result["commands"]), sorted(commands)) + + def test_ios_vrf_interfaces_replaced_idempotent(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + no ip address + shutdown + ! + + interface GigabitEthernet3 + vrf forwarding testvrf2 + no ip address + shutdown + ! + + interface GigabitEthernet4 + vrf forwarding testvrf1 + no ip address + shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3", "vrf_name": "testvrf2"}, + {"name": "GigabitEthernet4", "vrf_name": "testvrf1"}, + ], + state="replaced", + ), + ) + result = self.execute_module(changed=False) + self.assertEqual(result["commands"], []) + + def test_ios_vrf_interfaces_overridden_idempotent(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + no ip address + shutdown + ! + + interface GigabitEthernet3 + vrf forwarding testvrf2 + no ip address + shutdown + ! + + interface GigabitEthernet4 + vrf forwarding testvrf1 + no ip address + shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2"}, + {"name": "GigabitEthernet3", "vrf_name": "testvrf2"}, + {"name": "GigabitEthernet4", "vrf_name": "testvrf1"}, + ], + state="overridden", + ), + ) + result = self.execute_module(changed=False) + self.assertEqual(result["commands"], []) + + def test_ios_vrf_interfaces_gathered(self): + self.maxDiff = None + run_cfg = dedent( + """\ + interface GigabitEthernet1 + ip address dhcp + no shutdown + ! + + interface GigabitEthernet2 + vrf forwarding vrf_1 + shutdown + ! + + interface GigabitEthernet3 + vrf forwarding vrf_2 + no ip address + shutdown + ! + + interface GigabitEthernet4 + no shutdown + ! + """, + ) + self.get_config.return_value = run_cfg + set_module_args(dict(state="gathered")) + gathered_list = [ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2", "vrf_name": "vrf_1"}, + {"name": "GigabitEthernet3", "vrf_name": "vrf_2"}, + {"name": "GigabitEthernet4"}, + ] + result = self.execute_module(changed=False) + self.assertEqual(result["gathered"], gathered_list) + + def test_ios_vrf_interfaces_rendered(self): + self.maxDiff = None + set_module_args( + dict( + config=[ + {"name": "GigabitEthernet1"}, + {"name": "GigabitEthernet2", "vrf_name": "vrf_1"}, + {"name": "GigabitEthernet3", "vrf_name": "vrf_2"}, + {"name": "GigabitEthernet4"}, + ], + state="rendered", + ), + ) + commands = [ + "interface GigabitEthernet2", + "vrf forwarding vrf_1", + "interface GigabitEthernet3", + "vrf forwarding vrf_2", + ] + result = self.execute_module(changed=False) + self.assertEqual(sorted(result["rendered"]), sorted(commands)) diff --git a/tox-ansible.ini b/tox-ansible.ini index 5e1f4b36a..b49a359f9 100644 --- a/tox-ansible.ini +++ b/tox-ansible.ini @@ -1,10 +1,3 @@ [ansible] -skip = - py3.7 - py3.8 - 2.9 - 2.10 - 2.11 - 2.12 - 2.13 +skip = ""