diff options
Diffstat (limited to 'tests/unit/modules')
22 files changed, 2489 insertions, 192 deletions
diff --git a/tests/unit/modules/conftest.py b/tests/unit/modules/conftest.py index 41465c30..0f36839e 100644 --- a/tests/unit/modules/conftest.py +++ b/tests/unit/modules/conftest.py @@ -11,12 +11,11 @@ import pytest from ansible.module_utils._text import to_bytes from ansible.module_utils.common._collections_compat import MutableMapping -from ansible.module_utils.six import string_types @pytest.fixture def patch_ansible_module(request, mocker): - if isinstance(request.param, string_types): + if isinstance(request.param, str): args = request.param elif isinstance(request.param, MutableMapping): if "ANSIBLE_MODULE_ARGS" not in request.param: diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_global_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_global_config.cfg index f54a03dc..464f132f 100644 --- a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_global_config.cfg +++ b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_global_config.cfg @@ -2,6 +2,8 @@ set firewall group address-group RND-HOSTS address 192.0.2.1 set firewall group address-group RND-HOSTS address 192.0.2.3 set firewall group address-group RND-HOSTS address 192.0.2.5 set firewall group address-group RND-HOSTS description 'This group has the Management hosts address lists' +set firewall group address-group DELETE-HOSTS address 1.2.3.4 +set firewall group address-group DELETE-HOSTS description 'The (single) last address from this group will be deleted in the tests' set firewall group ipv6-address-group LOCAL-v6 address ::1 set firewall group ipv6-address-group LOCAL-v6 address fdec:2503:89d6:59b3::1 set firewall group ipv6-address-group LOCAL-v6 description 'This group has the hosts address lists of this machine' diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_global_config_v14.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_global_config_v14.cfg index 0a1247dd..c883ca78 100644 --- a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_global_config_v14.cfg +++ b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_global_config_v14.cfg @@ -1,7 +1,10 @@ +set firewall group address-group A-EMPTY set firewall group address-group RND-HOSTS address 192.0.2.1 set firewall group address-group RND-HOSTS address 192.0.2.3 set firewall group address-group RND-HOSTS address 192.0.2.5 set firewall group address-group RND-HOSTS description 'This group has the Management hosts address lists' +set firewall group address-group DELETE-HOSTS address 1.2.3.4 +set firewall group address-group DELETE-HOSTS description 'The (single) last address from this group will be deleted in the tests' set firewall group ipv6-address-group LOCAL-v6 address ::1 set firewall group ipv6-address-group LOCAL-v6 address fdec:2503:89d6:59b3::1 set firewall group ipv6-address-group LOCAL-v6 description 'This group has the hosts address lists of this machine' @@ -16,3 +19,6 @@ set firewall global-options state-policy related action 'accept' set firewall global-options state-policy related log-level 'alert' set firewall global-options ipv6-src-route 'enable' set firewall global-options send-redirects 'enable' +set firewall zone ZONE-TEST interface 'eth0.1234' +set firewall zone ZONE-TEST description 'zone-test test description' +set firewall group address-group ZONE-TEST address '1.2.3.4' diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config.cfg index 3ad6ec97..9665f86b 100644 --- a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config.cfg +++ b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config.cfg @@ -1,3 +1,5 @@ +set firewall flowtable test interface 'eth1' +set firewall flowtable test offload 'software' set firewall name V4-INGRESS default-action 'accept' set firewall ipv6-name V6-INGRESS default-action 'accept' set firewall name V4-INGRESS description 'This is IPv4 V4-INGRESS rule set' @@ -18,4 +20,6 @@ set firewall name MULTIPLE-RULE default-action 'drop' set firewall name MULTIPLE-RULE rule 1 action 'accept' set firewall name MULTIPLE-RULE rule 1 protocol 'all' set firewall name MULTIPLE-RULE rule 2 action 'drop' -set firewall name MULTIPLE-RULE rule 2 protocol 'all'
\ No newline at end of file +set firewall name MULTIPLE-RULE rule 2 protocol 'all' +set firewall name V4-OFFLOAD rule 1 action 'offload' +set firewall name V4-OFFLOAD rule 1 offload-target 'test' diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config_v14.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config_v14.cfg index 7f63dd78..6f69211a 100644 --- a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config_v14.cfg +++ b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config_v14.cfg @@ -1,3 +1,5 @@ +set firewall flowtable test interface 'eth1' +set firewall flowtable test offload 'software' set firewall ipv4 name V4-INGRESS default-action 'accept' set firewall ipv6 name V6-INGRESS default-action 'accept' set firewall ipv4 name V4-INGRESS description 'This is IPv4 V4-INGRESS rule set' @@ -36,4 +38,7 @@ set firewall ipv4 name MULTIPLE-RULE default-action 'drop' set firewall ipv4 name MULTIPLE-RULE rule 1 action 'accept' set firewall ipv4 name MULTIPLE-RULE rule 1 protocol 'all' set firewall ipv4 name MULTIPLE-RULE rule 2 action 'drop' -set firewall ipv4 name MULTIPLE-RULE rule 2 protocol 'all'
\ No newline at end of file +set firewall ipv4 name MULTIPLE-RULE rule 2 protocol 'all' +set firewall ipv4 name V4-OFFLOAD rule 105 +set firewall ipv4 name V4-OFFLOAD rule 105 action 'offload' +set firewall ipv4 name V4-OFFLOAD rule 105 offload-target 'test' diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_route_maps_config_v14.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_route_maps_config_v14.cfg new file mode 100644 index 00000000..1e84e5eb --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_route_maps_config_v14.cfg @@ -0,0 +1,18 @@ +set policy route-map test3 rule 1 action 'permit' +set policy route-map test3 rule 1 match interface 'eth2' +set policy route-map test3 rule 1 match ipv6 nexthop 'fdda:5cc1:23:4::1f' +set policy route-map test3 rule 1 match metric '1' +set policy route-map test3 rule 1 match peer '1.1.1.2' +set policy route-map test3 rule 1 match rpki 'invalid' +set policy route-map test3 rule 1 set bgp-extcommunity-rt '22:11' +set policy route-map test3 rule 1 set community replace 'internet' +set policy route-map test3 rule 1 set ipv6-next-hop global 'fdda:5cc1:23:4::1f' +set policy route-map test3 rule 1 set ip-next-hop '10.20.10.20' +set policy route-map test3 rule 1 set local-preference '4' +set policy route-map test3 rule 1 set metric '5' +set policy route-map test3 rule 1 set metric-type 'type-1' +set policy route-map test3 rule 1 set origin 'egp' +set policy route-map test3 rule 1 set originator-id '10.0.2.3' +set policy route-map test3 rule 1 set src '10.0.2.15' +set policy route-map test3 rule 1 set tag '5' +set policy route-map test3 rule 1 set weight '4' diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces.cfg new file mode 100644 index 00000000..b877a4b6 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces.cfg @@ -0,0 +1,10 @@ +Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down +Interface IP Address S/L Description +--------- ---------- --- ----------- +eth0 10.0.2.15/24 u/u +eth0.100 - u/u vlan-100 +eth1 - u/u +eth1.200 192.0.2.1/24 u/u vlan-200 +eth2 - u/u +lo 127.0.0.1/8 u/u + ::1/128 diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces_empty.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces_empty.cfg new file mode 100644 index 00000000..06ae56a0 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces_empty.cfg @@ -0,0 +1,7 @@ +Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down +Interface IP Address S/L Description +--------- ---------- --- ----------- +eth0 10.0.2.15/24 u/u +eth1 - u/u +eth2 - u/u +lo 127.0.0.1/8 u/u diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_vrf_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_vrf_config.cfg new file mode 100644 index 00000000..094b7cf3 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_vrf_config.cfg @@ -0,0 +1,15 @@ +set vrf bind-to-all +set vrf name vrf-blue description 'blue-vrf' +set vrf name vrf-blue table '100' +set vrf name vrf-blue vni '1000' +set vrf name vrf-red description 'red-vrf' +set vrf name vrf-red disable +set vrf name vrf-red ip disable-forwarding +set vrf name vrf-red ip protocol rip route-map 'rm1' +set vrf name vrf-red ipv6 nht no-resolve-via-default +set vrf name vrf-red table '101' +set vrf name vrf-red vni '1001' +set vrf name vrf-red protocols bgp system-as 65000 +set vrf name vrf-red protocols bgp neighbor 192.0.2.1 remote-as 65002 +set vrf name vrf-red protocols bgp neighbor 1.1.1.3 passive +set vrf name vrf-red protocols bgp neighbor 1.1.1.3 remote-as 400 diff --git a/tests/unit/modules/network/vyos/test_rm_templates_perf.py b/tests/unit/modules/network/vyos/test_rm_templates_perf.py new file mode 100644 index 00000000..d2060f46 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_rm_templates_perf.py @@ -0,0 +1,163 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +"""Performance budget tests for rm_templates parsers. + +These tests guard against re-introducing catastrophic regex backtracking +in the rm_template parsers (T8609). Pre-fix, parse() over realistic +device-output input could take 50+ seconds because of `(group)*` +quantifiers on groups containing `\\S+`. Post-fix, the same input +parses in single-digit milliseconds. + +A 1-second budget is comfortably above post-fix runtime and well below +the pre-regression cliff, so the test fails sharply if the bug returns. +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import time + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family_14 import ( + Bgp_address_familyTemplate14, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_global_14 import ( + Bgp_globalTemplate14, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps_14 import ( + Route_mapsTemplate14, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.snmp_server import ( + Snmp_serverTemplate, +) + + +PARSE_BUDGET_SECONDS = 1.0 + + +def _time_parse(parser_class, lines): + """Time a single ``parse()`` call against ``lines``; returns elapsed seconds.""" + parser = parser_class(lines=lines) + t0 = time.perf_counter() + parser.parse() + return time.perf_counter() - t0 + + +def test_route_maps_14_parse_budget(): + """Realistic route-map config: parse() must finish under 1s. + + Pre-T8609: ~50s. Post-fix: <50ms. Inputs use 20+ char names + because the backtracking is exponential in the first \\S+ run + after the prefix. + """ + lines = [ + "set policy route-map ADVERTISE-ANYCAST-v6 rule 10 action 'permit'", + "set policy route-map ADVERTISE-ANYCAST-v6 rule 10 match ipv6 address prefix-list 'ANYCAST-AGGREGATE-v6'", + "set policy route-map DEFAULT-ORIGINATE-SENTINEL-v6 rule 10 action 'permit'", + "set policy route-map DEFAULT-ORIGINATE-SENTINEL-v6 rule 10 match ipv6 address prefix-list 'AS64496-SENTINEL-v6'", + "set policy route-map DEFAULT-ORIGINATE-SENTINEL-v6 rule 10 set local-preference '120'", + "set policy route-map IXP-PEER-INGRESS-v4 rule 10 action 'permit'", + "set policy route-map IXP-PEER-INGRESS-v4 rule 10 match ip address prefix-list 'IXP-INBOUND-v4'", + "set policy route-map IXP-PEER-INGRESS-v4 rule 10 set community 'additive 65000:100'", + "set policy route-map TRANSIT-EGRESS-v4 rule 100 action 'permit'", + "set policy route-map TRANSIT-EGRESS-v4 rule 100 match ip address prefix-list 'CUSTOMER-PREFIXES-v4'", + "set policy route-map TRANSIT-EGRESS-v4 rule 100 set as-path prepend '65000 65000'", + "set policy route-map UPSTREAM-INGRESS-v4 rule 10 action 'permit'", + ] + elapsed = _time_parse(Route_mapsTemplate14, lines) + assert elapsed < PARSE_BUDGET_SECONDS, ( + "Route_mapsTemplate14.parse() took %.2fs (budget %.2fs); " + "possible regression of T8609 (rm_templates regex backtracking)." + % (elapsed, PARSE_BUDGET_SECONDS) + ) + + +def test_bgp_global_14_parse_budget(): + """Realistic BGP neighbor/address-family config: parse() under 1s.""" + lines = [ + "set protocols bgp 65001 neighbor 2001:db8:abcd:1234::1 remote-as '65002'", + "set protocols bgp 65001 neighbor 2001:db8:abcd:1234::1 description 'IXP-PEER-1'", + "set protocols bgp 65001 neighbor 2001:db8:abcd:1234::1 address-family ipv6-unicast route-map import 'IXP-INGRESS-v6'", + "set protocols bgp 65001 neighbor 2001:db8:abcd:1234::1 address-family ipv6-unicast route-map export 'IXP-EGRESS-v6'", + "set protocols bgp 65001 neighbor 192.0.2.1 remote-as '65003'", + "set protocols bgp 65001 neighbor 192.0.2.1 description 'TRANSIT-PROVIDER-1'", + "set protocols bgp 65001 neighbor 192.0.2.1 address-family ipv4-unicast route-map import 'TRANSIT-INGRESS-v4'", + "set protocols bgp 65001 neighbor 192.0.2.1 address-family ipv4-unicast route-map export 'TRANSIT-EGRESS-v4'", + ] + elapsed = _time_parse(Bgp_globalTemplate14, lines) + assert elapsed < PARSE_BUDGET_SECONDS, ( + "Bgp_globalTemplate14.parse() took %.2fs (budget %.2fs); " + "possible regression of T8609 (rm_templates regex backtracking)." + % (elapsed, PARSE_BUDGET_SECONDS) + ) + + +def test_snmp_server_parse_budget(): + """Realistic SNMP v3 config: parse() under 1s.""" + lines = [ + "set service snmp community PUBLIC-COMMUNITY-NAME-1 authorization 'ro'", + "set service snmp community PUBLIC-COMMUNITY-NAME-1 client '192.0.2.0/24'", + "set service snmp v3 trap-target TRAP-TARGET-LONG-NAME-1 user 'monitor'", + "set service snmp v3 trap-target TRAP-TARGET-LONG-NAME-1 protocol 'udp'", + "set service snmp v3 trap-target TRAP-TARGET-LONG-NAME-1 port '162'", + "set service snmp v3 user TRAP-USER-LONG-NAME-1 mode 'auth'", + "set service snmp v3 user TRAP-USER-LONG-NAME-1 group 'monitor'", + ] + elapsed = _time_parse(Snmp_serverTemplate, lines) + assert elapsed < PARSE_BUDGET_SECONDS, ( + "Snmp_serverTemplate.parse() took %.2fs (budget %.2fs); " + "possible regression of T8609 (rm_templates regex backtracking)." + % (elapsed, PARSE_BUDGET_SECONDS) + ) + + +def test_bgp_address_family_14_parse_budget(): + """Realistic BGP address-family aggregate config: parse() under 1s.""" + lines = [ + "set protocols bgp 65001 address-family ipv4-unicast network 198.51.100.0/24 backdoor", + "set protocols bgp 65001 address-family ipv4-unicast network 198.51.100.0/24 path-limit '4'", + "set protocols bgp 65001 address-family ipv4-unicast network 198.51.100.0/24 route-map 'NET-IN-v4'", + "set protocols bgp 65001 address-family ipv4-unicast aggregate-address 203.0.113.0/24 as-set", + "set protocols bgp 65001 address-family ipv4-unicast aggregate-address 203.0.113.0/24 summary-only", + "set protocols bgp 65001 address-family ipv6-unicast network 2001:db8:abcd:1234::/64 backdoor", + "set protocols bgp 65001 address-family ipv6-unicast network 2001:db8:abcd:1234::/64 route-map 'NET-IN-v6'", + "set protocols bgp 65001 address-family ipv6-unicast aggregate-address 2001:db8::/32 as-set", + "set protocols bgp 65001 address-family ipv6-unicast aggregate-address 2001:db8::/32 summary-only", + ] + elapsed = _time_parse(Bgp_address_familyTemplate14, lines) + assert elapsed < PARSE_BUDGET_SECONDS, ( + "Bgp_address_familyTemplate14.parse() took %.2fs (budget %.2fs); " + "possible regression of T8609 (rm_templates regex backtracking)." + % (elapsed, PARSE_BUDGET_SECONDS) + ) + + +def test_route_maps_14_set_comm_list_delete_matches_setval(): + """Round-trip check: the `set_comm_list_delete` parser must match the line its setval generates. + + `set_comm_list_delete`'s setval emits `set policy route-map X rule N set + comm-list delete` with no token after `delete`. Pre-T8609 the getval + happened to match this by accident (a `*` quantifier on the trailing + `(?P<delete>\\S+)` made the group optional after VERBOSE-strip). An + earlier draft of T8609's fix made the group required, causing the + parser to silently ignore its own output. This test guards the + round-trip. + """ + line = "set policy route-map MY-MAP rule 10 set comm-list delete" + parser = Route_mapsTemplate14(lines=[line]) + result = parser.parse() + rm = result.get("route_maps", {}).get("MY-MAP") + assert rm is not None, ( + "route_maps_14: set_comm_list_delete parser failed to match its " + "own setval-generated line %r; the parser is broken." % line + ) + entry = rm.get("entries", {}).get(10, {}) + comm_list = entry.get("set", {}).get("comm_list", {}) + assert comm_list.get("delete"), ( + "route_maps_14: set_comm_list_delete matched the line but did not " + "populate set.comm_list.delete; check the result template." + ) diff --git a/tests/unit/modules/network/vyos/test_vyos_bgp_address_family14.py b/tests/unit/modules/network/vyos/test_vyos_bgp_address_family14.py index 9e615ff0..c0c661ad 100644 --- a/tests/unit/modules/network/vyos/test_vyos_bgp_address_family14.py +++ b/tests/unit/modules/network/vyos/test_vyos_bgp_address_family14.py @@ -45,13 +45,13 @@ class TestVyosBgpafModule14(TestVyosModule): ) self.execute_show_command = self.mock_execute_show_command.start() self.mock_get_os_version = patch( - "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_address_family.bgp_address_family.get_os_version" + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_address_family.bgp_address_family.get_os_version", ) self.test_version = "1.4" self.get_os_version = self.mock_get_os_version.start() self.get_os_version.return_value = self.test_version self.mock_facts_get_os_version = patch( - "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_address_family.bgp_address_family.get_os_version" + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_address_family.bgp_address_family.get_os_version", ) self.get_facts_os_version = self.mock_facts_get_os_version.start() self.get_facts_os_version.return_value = self.test_version @@ -425,46 +425,6 @@ class TestVyosBgpafModule14(TestVyosModule): self.execute_module(changed=True, commands=commands) - # def test_vyos_bgp_address_family_incorrect_instance(self): - # set_module_args( - # dict( - # state="overridden", - # config=dict( - # as_number=100, - # address_family=[ - # dict( - # afi="ipv4", - # networks=[ - # dict(prefix="192.1.13.0/24", route_map="map01"), - # ], - # ), - # dict( - # afi="ipv6", - # redistribute=[dict(protocol="ospfv3", metric=20)], - # ), - # ], - # neighbors=[ - # dict( - # neighbor_address="192.10.21.25", - # address_family=[ - # dict( - # afi="ipv4", - # route_map=[dict(action="import", route_map="map01")], - # ), - # dict( - # afi="ipv6", - # distribute_list=[dict(action="export", acl=10)], - # route_server_client=True, - # ), - # ], - # ), - # ], - # ), - # ), - # ) - # result = self.execute_module(failed=True) - # self.assertIn("Only one bgp instance is allowed per device", result["msg"]) - def test_vyos_bgp_address_family_rendered(self): set_module_args( dict( @@ -708,7 +668,7 @@ class TestVyosBgpafModule14(TestVyosModule): ) self.execute_module(changed=False, commands=[]) - def test_vyos_bgp_address_family_replaced_asn(self): + def test_vyos_bgp_address_family_replaced_asn2(self): set_module_args( dict( state="replaced", @@ -764,7 +724,7 @@ class TestVyosBgpafModule14(TestVyosModule): ] self.execute_module(changed=True, commands=commands) - def test_vyos_bgp_address_family_overridden_asn(self): + def test_vyos_bgp_address_family_overridden_asn2(self): set_module_args( dict( state="overridden", @@ -842,13 +802,13 @@ class TestVyosBgpafOpsModule14(TestVyosModule): ) self.execute_show_command = self.mock_execute_show_command.start() self.mock_get_os_version = patch( - "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_address_family.bgp_address_family.get_os_version" + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_address_family.bgp_address_family.get_os_version", ) self.test_version = "1.4" self.get_os_version = self.mock_get_os_version.start() self.get_os_version.return_value = self.test_version self.mock_facts_get_os_version = patch( - "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_address_family.bgp_address_family.get_os_version" + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_address_family.bgp_address_family.get_os_version", ) self.get_facts_os_version = self.mock_facts_get_os_version.start() self.get_facts_os_version.return_value = self.test_version @@ -911,13 +871,13 @@ class TestVyosBgpafOpsModule14(TestVyosModule): ], redistribute=[ dict(protocol="ospf", metric=25), - ] + ], ), dict( afi="ipv6", redistribute=[ dict(protocol="ospfv3", metric=20), - dict(protocol="ripng") + dict(protocol="ripng"), ], ), ], @@ -948,13 +908,13 @@ class TestVyosBgpafOpsModule14(TestVyosModule): ], redistribute=[ dict(protocol="ospf", metric=25), - ] + ], ), dict( afi="ipv6", redistribute=[ dict(protocol="ospfv3", metric=20), - dict(protocol="ripng") + dict(protocol="ripng"), ], ), ], @@ -982,7 +942,7 @@ class TestVyosBgpafOpsModule14(TestVyosModule): afi="ipv4", networks=[ dict(prefix="192.2.13.0/24"), - ] + ], ), dict( afi="ipv6", diff --git a/tests/unit/modules/network/vyos/test_vyos_config.py b/tests/unit/modules/network/vyos/test_vyos_config.py index 4f1cac6a..601971cd 100644 --- a/tests/unit/modules/network/vyos/test_vyos_config.py +++ b/tests/unit/modules/network/vyos/test_vyos_config.py @@ -140,3 +140,41 @@ class TestVyosConfigModule(TestVyosModule): return_value=self.cliconf_obj.get_diff(candidate, None, diff_match="none"), ) self.execute_module(changed=True, commands=lines, sort=False) + + def test_vyos_config_confirm_automatic(self): + src = load_fixture("vyos_config_src.cfg") + confirm_timeout = 7 + set_module_args(dict(src=src, confirm="automatic", confirm_timeout=confirm_timeout)) + candidate = "\n".join(self.module.format_commands(src.splitlines())) + commands = [ + "set system host-name foo", + "delete interfaces ethernet eth0 address", + ] + self.conn.get_diff = MagicMock( + return_value=self.cliconf_obj.get_diff(candidate, self.running_config), + ) + + self.execute_module(changed=True, commands=commands) + + self.assertEqual(self.load_config.call_args[1]["confirm"], confirm_timeout) + self.run_commands.assert_called_once() + self.assertEqual( + ["configure", "confirm", "exit"], + self.run_commands.call_args[0][1], + ) + + def test_vyos_config_confirm_manual(self): + lines = [ + "set system host-name foo", + ] + confirm_timeout = 12 + set_module_args(dict(lines=lines, confirm="manual", confirm_timeout=confirm_timeout)) + candidate = "\n".join(lines) + self.conn.get_diff = MagicMock( + return_value=self.cliconf_obj.get_diff(candidate, self.running_config), + ) + + self.execute_module(changed=True, commands=lines) + + self.assertEqual(self.load_config.call_args[1]["confirm"], confirm_timeout) + self.run_commands.assert_not_called() diff --git a/tests/unit/modules/network/vyos/test_vyos_firewall_global.py b/tests/unit/modules/network/vyos/test_vyos_firewall_global.py index 481cc1dd..862bf06c 100644 --- a/tests/unit/modules/network/vyos/test_vyos_firewall_global.py +++ b/tests/unit/modules/network/vyos/test_vyos_firewall_global.py @@ -109,7 +109,7 @@ class TestVyosFirewallGlobalModule(TestVyosModule): ), dict( afi="ipv6", - ip_src_route=True, + ip_src_route=False, icmp_redirects=dict(receive=False), ), ], @@ -183,6 +183,7 @@ class TestVyosFirewallGlobalModule(TestVyosModule): "set firewall group port-group TELNET description 'This group has the telnet ports'", "set firewall group port-group TELNET", "set firewall ip-src-route 'enable'", + "set firewall ipv6-src-route 'disable'", "set firewall receive-redirects 'disable'", "set firewall config-trap 'enable'", "set firewall ipv6-receive-redirects 'disable'", @@ -269,6 +270,12 @@ class TestVyosFirewallGlobalModule(TestVyosModule): ], ), dict( + afi="ipv4", + name="DELETE-HOSTS", + description="The (single) last address from this group will be deleted in the tests", + # No members here + ), + dict( afi="ipv6", name="LOCAL-v6", description="This group has the hosts address lists of this machine", @@ -309,6 +316,7 @@ class TestVyosFirewallGlobalModule(TestVyosModule): "delete firewall send-redirects", "delete firewall group address-group RND-HOSTS address 192.0.2.3", "delete firewall group address-group RND-HOSTS address 192.0.2.5", + "delete firewall group address-group DELETE-HOSTS address", "set firewall group address-group RND-HOSTS address 192.0.2.7", "set firewall group address-group RND-HOSTS address 192.0.2.9", "delete firewall group network-group RND description", @@ -376,6 +384,7 @@ class TestVyosFirewallGlobalModule(TestVyosModule): ), ) commands = [ + "delete firewall group address-group DELETE-HOSTS", "delete firewall group address-group RND-HOSTS address 192.0.2.3", "delete firewall group address-group RND-HOSTS address 192.0.2.5", "delete firewall ipv6-src-route", @@ -412,6 +421,14 @@ class TestVyosFirewallGlobalModule(TestVyosModule): ], ), dict( + afi="ipv4", + name="DELETE-HOSTS", + description="The (single) last address from this group will be deleted in the tests", + members=[ + dict(address='1.2.3.4'), + ] + ), + dict( afi="ipv6", name="LOCAL-v6", description="This group has the hosts address lists of this machine", diff --git a/tests/unit/modules/network/vyos/test_vyos_firewall_global14.py b/tests/unit/modules/network/vyos/test_vyos_firewall_global14.py index aae4aa83..b8bca0cc 100644 --- a/tests/unit/modules/network/vyos/test_vyos_firewall_global14.py +++ b/tests/unit/modules/network/vyos/test_vyos_firewall_global14.py @@ -109,7 +109,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): ), dict( afi="ipv6", - ip_src_route=True, + ip_src_route=False, icmp_redirects=dict(receive=False), ), ], @@ -185,6 +185,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): "set firewall global-options ip-src-route 'enable'", "set firewall global-options receive-redirects 'disable'", "set firewall global-options config-trap 'enable'", + "set firewall global-options ipv6-src-route 'disable'", "set firewall global-options ipv6-receive-redirects 'disable'", "set firewall global-options state-policy established action 'accept'", "set firewall global-options state-policy established log", @@ -206,6 +207,10 @@ class TestVyosFirewallRulesModule14(TestVyosModule): address_group=[ dict( afi="ipv4", + name="A-EMPTY", + ), + dict( + afi="ipv4", name="RND-HOSTS", description="This group has the Management hosts address lists", members=[ @@ -272,6 +277,12 @@ class TestVyosFirewallRulesModule14(TestVyosModule): ], ), dict( + afi="ipv4", + name="DELETE-HOSTS", + description="The (single) last address from this group will be deleted in the tests", + # No members here + ), + dict( afi="ipv6", name="LOCAL-v6", description="This group has the hosts address lists of this machine", @@ -280,6 +291,14 @@ class TestVyosFirewallRulesModule14(TestVyosModule): dict(address="fdec:2503:89d6:59b3::2"), ], ), + dict( + afi="ipv4", + name="ZONE-TEST", + members=[ + dict(address="1.2.3.4"), + ], + description="This is a new description for a address group name that is also in a zone", + ), ], network_group=[ dict( @@ -308,8 +327,10 @@ class TestVyosFirewallRulesModule14(TestVyosModule): ), ) commands = [ + "delete firewall group address-group A-EMPTY", "delete firewall group address-group RND-HOSTS address 192.0.2.3", "delete firewall group address-group RND-HOSTS address 192.0.2.5", + "delete firewall group address-group DELETE-HOSTS address", "delete firewall global-options all-ping", "delete firewall global-options state-policy related", "delete firewall global-options ipv6-src-route", @@ -317,6 +338,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): "set firewall global-options state-policy invalid action 'reject'", "set firewall group address-group RND-HOSTS address 192.0.2.7", "set firewall group address-group RND-HOSTS address 192.0.2.9", + "set firewall group address-group ZONE-TEST description 'This is a new description for a address group name that is also in a zone'", "delete firewall group network-group RND description", "delete firewall group ipv6-address-group LOCAL-v6 address fdec:2503:89d6:59b3::1", "set firewall group ipv6-address-group LOCAL-v6 address fdec:2503:89d6:59b3::2", @@ -341,6 +363,10 @@ class TestVyosFirewallRulesModule14(TestVyosModule): address_group=[ dict( afi="ipv4", + name="A-EMPTY", + ), + dict( + afi="ipv4", name="RND-HOSTS", description="This group has the Management hosts address lists", members=[ @@ -350,6 +376,21 @@ class TestVyosFirewallRulesModule14(TestVyosModule): ], ), dict( + afi="ipv4", + name="DELETE-HOSTS", + description="The (single) last address from this group will be deleted in the tests", + members=[ + dict(address='1.2.3.4'), + ] + ), + dict( + afi="ipv4", + name="ZONE-TEST", + members=[ + dict(address="1.2.3.4"), + ] + ), + dict( afi="ipv6", name="LOCAL-v6", description="This group has the hosts address lists of this machine", @@ -416,6 +457,14 @@ class TestVyosFirewallRulesModule14(TestVyosModule): dict(address="fdec:2503:89d6:59b3::2"), ], ), + dict( + afi="ipv4", + name="ZONE-TEST", + members=[ + dict(address="4.3.2.1"), + ], + description="This is a new description for a address group name that is also in a zone", + ), ], network_group=[ dict( @@ -444,16 +493,21 @@ class TestVyosFirewallRulesModule14(TestVyosModule): ), ) commands = [ + "delete firewall group address-group A-EMPTY", "delete firewall group address-group RND-HOSTS address 192.0.2.3", "delete firewall group address-group RND-HOSTS address 192.0.2.5", + "delete firewall group address-group ZONE-TEST address 1.2.3.4", "delete firewall global-options all-ping", "delete firewall global-options ipv6-src-route", "delete firewall global-options send-redirects", "set firewall global-options state-policy related action 'drop'", "delete firewall global-options state-policy related log-level", + "delete firewall group address-group DELETE-HOSTS", "set firewall global-options state-policy invalid action 'reject'", "set firewall group address-group RND-HOSTS address 192.0.2.7", "set firewall group address-group RND-HOSTS address 192.0.2.9", + "set firewall group address-group ZONE-TEST address 4.3.2.1", + "set firewall group address-group ZONE-TEST description 'This is a new description for a address group name that is also in a zone'", "delete firewall group ipv6-address-group LOCAL-v6 address fdec:2503:89d6:59b3::1", "set firewall group ipv6-address-group LOCAL-v6 address fdec:2503:89d6:59b3::2", "delete firewall group port-group SSH port 22", diff --git a/tests/unit/modules/network/vyos/test_vyos_firewall_rules13.py b/tests/unit/modules/network/vyos/test_vyos_firewall_rules13.py index 9a25198f..edce123b 100644 --- a/tests/unit/modules/network/vyos/test_vyos_firewall_rules13.py +++ b/tests/unit/modules/network/vyos/test_vyos_firewall_rules13.py @@ -167,6 +167,13 @@ class TestVyosFirewallRulesModule13(TestVyosModule): fragment="match-frag", disable=True, ), + dict( + number="105", + action="offload", + offload_target="test", + description="Rule 105 is configured by Ansible", + protocol="udp", + ), ], ), ], @@ -187,12 +194,17 @@ class TestVyosFirewallRulesModule13(TestVyosModule): "set firewall name INBOUND rule 101 action 'accept'", "set firewall name INBOUND rule 101 ipsec 'match-ipsec'", "set firewall name INBOUND rule 101 log 'disable'", + "set firewall name INBOUND rule 105", + "set firewall name INBOUND rule 105 action 'offload'", + "set firewall name INBOUND rule 105 offload-target 'test'", + "set firewall name INBOUND rule 105 description 'Rule 105 is configured by Ansible'", + "set firewall name INBOUND rule 105 protocol 'udp'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_02(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( @@ -253,7 +265,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): def test_vyos_firewall_v4_rule_sets_rule_merged_03(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( @@ -302,7 +314,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): def test_vyos_firewall_v4_rule_sets_rule_merged_04(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( @@ -327,9 +339,8 @@ class TestVyosFirewallRulesModule13(TestVyosModule): tcp=dict( flags=[ dict(flag="all"), - ] + ], ), - ), ], ), @@ -399,7 +410,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): def test_vyos_firewall_v6_rule_sets_rule_merged_02(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( @@ -460,7 +471,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): def test_vyos_firewall_v6_rule_sets_rule_merged_03(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( @@ -509,7 +520,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): def test_vyos_firewall_v6_rule_sets_rule_merged_04(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( @@ -534,7 +545,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): tcp=dict( flags=[ dict(flag="all"), - ] + ], ), ), dict( @@ -545,8 +556,8 @@ class TestVyosFirewallRulesModule13(TestVyosModule): dict(flag="syn"), dict(flag="fin", invert=True), ], - ) - ) + ), + ), ], ), ], @@ -572,7 +583,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): def test_vyos_firewall_v6_rule_sets_rule_merged_icmp_01(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( @@ -605,7 +616,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): def test_vyos_firewall_v4_rule_sets_rule_merged_icmp_01(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( @@ -639,7 +650,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): def test_vyos_firewall_v4_rule_sets_rule_merged_icmp_02(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( @@ -671,8 +682,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_del_01(self): - """Test if plugin correctly removes existing rule set - """ + """Test if plugin correctly removes existing rule set""" set_module_args( dict( config=[dict(afi="ipv4", rule_sets=[dict(name="V4-INGRESS")])], @@ -683,33 +693,32 @@ class TestVyosFirewallRulesModule13(TestVyosModule): self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_02(self): - """Test if plugin correctly removes existing rule sets, both ipv4 and ipv6 - """ + """Test if plugin correctly removes existing rule sets, both ipv4 and ipv6""" set_module_args( dict( config=[ dict(afi="ipv4", rule_sets=[dict(name="V4-INGRESS")]), dict(afi="ipv6", rule_sets=[dict(name="V6-INGRESS")]), + dict(afi="ipv4", rule_sets=[dict(name="V4-OFFLOAD")]), ], state="deleted", ), ) commands = [ + "delete firewall name V4-OFFLOAD", "delete firewall name V4-INGRESS", "delete firewall ipv6-name V6-INGRESS", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_03(self): - """Test if plugin correctly removes existing AFIs, both ipv4 and ipv6 - """ + """Test if plugin correctly removes existing AFIs, both ipv4 and ipv6""" set_module_args(dict(config=[], state="deleted")) commands = ["delete firewall name", "delete firewall ipv6-name"] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_04(self): - """Test if plugin has no effect on non-existent rule sets - """ + """Test if plugin has no effect on non-existent rule sets""" set_module_args( dict( config=[ @@ -723,7 +732,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): def test_vyos_firewall_v4v6_rule_sets_rule_rep_01(self): """Test if plugin correctly replaces a particular rule set(s) - without affecting the others + without affecting the others """ set_module_args( dict( @@ -803,7 +812,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): def test_vyos_firewall_v4v6_rule_sets_rule_rep_02(self): """Test if plugin correctly replaces a particular rule(s) and rule set attribute(s) - without affecting the others + without affecting the others """ set_module_args( dict( @@ -867,8 +876,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_rep_idem_01(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ @@ -890,7 +898,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): fragment="match-frag", disable=True, log="enable", - ) + ), ], ), dict( @@ -925,8 +933,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_rep_idem_02(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ @@ -947,7 +954,33 @@ class TestVyosFirewallRulesModule13(TestVyosModule): protocol="icmp", fragment="match-frag", disable=True, - log="enable" + log="enable", + ), + ], + ), + ], + ), + ], + state="replaced", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_firewall_v4v6_rule_sets_rule_rep_idem_03(self): + """Test if plugin correctly has no effect if there is no change in the configuration""" + set_module_args( + dict( + config=[ + dict( + afi="ipv4", + rule_sets=[ + dict( + name="V4-OFFLOAD", + rules=[ + dict( + number="1", + action="offload", + offload_target="test", ), ], ), @@ -960,8 +993,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_mer_idem_01(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ @@ -982,7 +1014,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): protocol="icmp", fragment="match-frag", disable=True, - ) + ), ], ), dict( @@ -1016,9 +1048,74 @@ class TestVyosFirewallRulesModule13(TestVyosModule): ) self.execute_module(changed=False, commands=[]) + def test_vyos_firewall_v4v6_rule_sets_rule_mer_idem_02(self): + """Test if plugin correctly has no effect if there is no change in the configuration""" + set_module_args( + dict( + config=[ + dict( + afi="ipv4", + rule_sets=[ + dict( + name="V4-INGRESS", + description="This is IPv4 V4-INGRESS rule set", + default_action="accept", + enable_default_log=True, + rules=[ + dict( + number="101", + action="accept", + description="Rule 101 is configured by Ansible", + ipsec="match-ipsec", + protocol="icmp", + fragment="match-frag", + disable=True, + ), + ], + ), + dict( + name="EGRESS", + default_action="reject", + ), + dict( + name="V4-OFFLOAD", + rules=[ + dict( + number="1", + action="offload", + offload_target="test", + ), + ], + ), + ], + ), + dict( + afi="ipv6", + rule_sets=[ + dict( + name="V6-INGRESS", + default_action="accept", + ), + dict( + name="EGRESS", + default_action="reject", + rules=[ + dict( + icmp=dict(type_name="echo-request"), + number=20, + ), + ], + ), + ], + ), + ], + state="merged", + ), + ) + self.execute_module(changed=False, commands=[]) + def test_vyos_firewall_v4v6_rule_sets_rule_ovr_01(self): - """Test if plugin correctly resets the entire rule set if there is a change in the configuration - """ + """Test if plugin correctly resets the entire rule set if there is a change in the configuration""" set_module_args( dict( config=[ @@ -1095,6 +1192,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): "delete firewall name V4-INGRESS", "delete firewall name EGRESS", "delete firewall name MULTIPLE-RULE", + "delete firewall name V4-OFFLOAD", "set firewall name MULTIPLE-RULE default-action 'drop'", "set firewall name MULTIPLE-RULE rule 1", "set firewall name MULTIPLE-RULE rule 1 action 'accept'", @@ -1126,7 +1224,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): def test_vyos_firewall_v4v6_rule_sets_rule_ovr_02(self): """Test if plugin correctly resets the entire rule set - while removing the absent ones if there is a change in the configuration + while removing the absent ones if there is a change in the configuration """ set_module_args( dict( @@ -1176,6 +1274,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): "delete firewall name V4-INGRESS", "delete firewall name EGRESS", "delete firewall name MULTIPLE-RULE", + "delete firewall name V4-OFFLOAD", "set firewall name V4-INGRESS rule 101", "set firewall name V4-INGRESS description 'This is IPv4 INGRESS rule set'", "set firewall name V4-INGRESS default-action 'accept'", @@ -1186,13 +1285,12 @@ class TestVyosFirewallRulesModule13(TestVyosModule): "set firewall ipv6-name EGRESS default-action 'reject'", "set firewall ipv6-name EGRESS rule 20", "set firewall ipv6-name EGRESS rule 20 protocol 'udp'", - "set firewall ipv6-name EGRESS rule 20 action 'accept'" + "set firewall ipv6-name EGRESS rule 20 action 'accept'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_ovr_idem_01(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ @@ -1214,7 +1312,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): fragment="match-frag", disable=True, log="enable", - ) + ), ], ), dict( @@ -1237,6 +1335,16 @@ class TestVyosFirewallRulesModule13(TestVyosModule): ), ], ), + dict( + name="V4-OFFLOAD", + rules=[ + dict( + number="1", + action="offload", + offload_target="test", + ), + ], + ), ], ), dict( @@ -1265,8 +1373,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v6_rule_sets_rule_merged_01_version(self): - """Test if plugin correctly adds ipv6 rule set with rules - """ + """Test if plugin correctly adds ipv6 rule set with rules""" self.get_os_version.return_value = "1.3" set_module_args( dict( @@ -1321,13 +1428,12 @@ class TestVyosFirewallRulesModule13(TestVyosModule): "set firewall ipv6-name INBOUND rule 102 action 'reject'", "set firewall ipv6-name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", "set firewall ipv6-name INBOUND rule 102 protocol 'ipv6-icmp'", - 'set firewall ipv6-name INBOUND rule 102 icmpv6 type 7', + "set firewall ipv6-name INBOUND rule 102 icmpv6 type 7", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_jump_rules_merged_01(self): - """Test if plugin correctly adds rule set with a jump action - """ + """Test if plugin correctly adds rule set with a jump action""" set_module_args( dict( config=[ @@ -1348,7 +1454,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): protocol="icmp", icmp=dict(type_name="echo-request"), jump_target="PROTECT-RE", - packet_length_exclude=[dict(length=100), dict(length=200)] + packet_length_exclude=[dict(length=100), dict(length=200)], ), dict( number="102", @@ -1360,10 +1466,10 @@ class TestVyosFirewallRulesModule13(TestVyosModule): ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6-name INBOUND default-action 'accept'", @@ -1382,7 +1488,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): "set firewall ipv6-name INBOUND rule 102 action 'reject'", "set firewall ipv6-name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", "set firewall ipv6-name INBOUND rule 102 protocol 'ipv6-icmp'", - 'set firewall ipv6-name INBOUND rule 102 icmpv6 type 7', + "set firewall ipv6-name INBOUND rule 102 icmpv6 type 7", ] self.execute_module(changed=True, commands=commands) @@ -1409,10 +1515,10 @@ class TestVyosFirewallRulesModule13(TestVyosModule): ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6-name INBOUND default-action 'accept'", @@ -1428,7 +1534,7 @@ class TestVyosFirewallRulesModule13(TestVyosModule): def test_vyos_firewall_log_replace_01(self): """Test that stanza is correctly replaced - without touching the other stanzas + without touching the other stanzas """ set_module_args( dict( @@ -1453,10 +1559,10 @@ class TestVyosFirewallRulesModule13(TestVyosModule): ], ), ], - ) + ), ], state="replaced", - ) + ), ) commands = [ "delete firewall name V4-INGRESS rule 101", @@ -1470,3 +1576,17 @@ class TestVyosFirewallRulesModule13(TestVyosModule): ] self.maxDiff = None self.execute_module(changed=True, commands=commands) + + def test_vyos_firewall_rules_parsed_icmp_type_code(self): + """parse_icmp_attr: legacy 'type/code' token parses into integer type and code.""" + raw = ( + "set firewall name TEST rule 1 action 'accept'\n" + "set firewall name TEST rule 1 protocol 'icmp'\n" + "set firewall name TEST rule 1 icmp type '3/4'\n" + ) + set_module_args(dict(running_config=raw, state="parsed")) + result = self.execute_module(changed=False) + parsed = result["parsed"] + rule = parsed[0]["rule_sets"][0]["rules"][0] + self.assertEqual(rule["icmp"]["type"], 3) + self.assertEqual(rule["icmp"]["code"], 4) diff --git a/tests/unit/modules/network/vyos/test_vyos_firewall_rules14.py b/tests/unit/modules/network/vyos/test_vyos_firewall_rules14.py index 64884282..0c371b92 100644 --- a/tests/unit/modules/network/vyos/test_vyos_firewall_rules14.py +++ b/tests/unit/modules/network/vyos/test_vyos_firewall_rules14.py @@ -35,35 +35,35 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def setUp(self): super(TestVyosFirewallRulesModule14, self).setUp() self.mock_get_config = patch( - "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config" + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config", ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( - "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config" + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config", ) self.load_config = self.mock_load_config.start() self.mock_get_resource_connection_config = patch( - "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base.get_resource_connection" + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base.get_resource_connection", ) self.get_resource_connection_config = self.mock_get_resource_connection_config.start() self.mock_get_resource_connection_facts = patch( - "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection" + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection", ) self.get_resource_connection_facts = self.mock_get_resource_connection_facts.start() self.mock_execute_show_command = patch( - "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes.Static_routesFacts.get_device_data" + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes.Static_routesFacts.get_device_data", ) self.mock_execute_show_command = patch( - "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules.Firewall_rulesFacts.get_device_data" + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules.Firewall_rulesFacts.get_device_data", ) self.execute_show_command = self.mock_execute_show_command.start() self.mock_get_os_version = patch( - "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_rules.firewall_rules.get_os_version" + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_rules.firewall_rules.get_os_version", ) self.get_os_version = self.mock_get_os_version.start() self.get_os_version.return_value = "1.4" @@ -164,15 +164,15 @@ class TestVyosFirewallRulesModule14(TestVyosModule): description="Rule 101 is configured by Ansible", jump_target="PROTECT-RE", packet_length_exclude=[dict(length=100), dict(length=200)], - packet_length=[dict(length=22)] + packet_length=[dict(length=22)], ), ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6 name INBOUND default-action 'accept'", @@ -191,7 +191,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_packet_length_replace_01(self): """Test that stanza is correctly replaced - without touching the other stanzas + without touching the other stanzas """ set_module_args( dict( @@ -210,15 +210,15 @@ class TestVyosFirewallRulesModule14(TestVyosModule): action="accept", description="Rule 101 is configured by Ansible", packet_length_exclude=[dict(length=100), dict(length=200)], - packet_length=[dict(length=22)] + packet_length=[dict(length=22)], ), ], ), ], - ) + ), ], state="replaced", - ) + ), ) commands = [ "delete firewall ipv4 name V4-INGRESS rule 101", @@ -252,15 +252,15 @@ class TestVyosFirewallRulesModule14(TestVyosModule): description="Rule 101 is configured by Ansible", jump_target="PROTECT-RE", packet_length_exclude=[dict(length=100), dict(length=200)], - packet_length=[dict(length=22)] + packet_length=[dict(length=22)], ), ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6 input filter default-action 'accept'", @@ -298,13 +298,20 @@ class TestVyosFirewallRulesModule14(TestVyosModule): inbound_interface=dict(name="eth0"), outbound_interface=dict(group="eth1"), ), + dict( + number="105", + action="offload", + offload_target="test", + description="Rule 105 is configured by Ansible", + protocol="udp", + ), ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6 name V6-INGRESS description 'This is IPv6 INBOUND rule set with a jump action'", @@ -314,13 +321,18 @@ class TestVyosFirewallRulesModule14(TestVyosModule): "set firewall ipv6 name V6-INGRESS rule 101", "set firewall ipv6 name V6-INGRESS rule 101 action 'jump'", "set firewall ipv6 name V6-INGRESS rule 101 jump-target 'PROTECT-RE'", + "set firewall ipv6 name V6-INGRESS rule 105", + "set firewall ipv6 name V6-INGRESS rule 105 action 'offload'", + "set firewall ipv6 name V6-INGRESS rule 105 offload-target 'test'", + "set firewall ipv6 name V6-INGRESS rule 105 description 'Rule 105 is configured by Ansible'", + "set firewall ipv6 name V6-INGRESS rule 105 protocol 'udp'", ] self.maxDiff = None self.execute_module(changed=True, commands=commands) def test_vyos_firewall_interface_replace_02(self): """Test that new stanza is correctly replaced - without touching the other stanzas + without touching the other stanzas """ set_module_args( dict( @@ -341,17 +353,17 @@ class TestVyosFirewallRulesModule14(TestVyosModule): ], ), ], - ) + ), ], state="replaced", - ) + ), ) commands = [ "delete firewall ipv4 name IF-TEST rule 10", "set firewall ipv4 name IF-TEST rule 10", "set firewall ipv4 name IF-TEST description 'Changed'", "set firewall ipv4 name IF-TEST rule 10 description 'Rule 10 is configured by Ansible'", - 'set firewall ipv4 name IF-TEST rule 10 inbound-interface name eth1', + "set firewall ipv4 name IF-TEST rule 10 inbound-interface name eth1", "set firewall ipv4 name IF-TEST rule 10 action 'accept'", ] self.maxDiff = None @@ -405,7 +417,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_v4_rule_sets_rule_merged_02(self): """Test that a rule set is correctly applied - including variant attributes such as state + including variant attributes such as state """ set_module_args( dict( @@ -466,7 +478,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_v4_rule_sets_rule_merged_03(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( @@ -515,7 +527,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_v4_rule_sets_rule_merged_04(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( @@ -540,9 +552,8 @@ class TestVyosFirewallRulesModule14(TestVyosModule): tcp=dict( flags=[ dict(flag="all"), - ] + ], ), - ), ], ), @@ -612,7 +623,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_v6_rule_sets_rule_merged_02(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( @@ -673,7 +684,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_v6_rule_sets_rule_merged_03(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( @@ -722,7 +733,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_v6_rule_sets_rule_merged_04(self): """Test that the plugin correctly applies configuration - within exsiting rule set + within exsiting rule set """ set_module_args( dict( @@ -747,7 +758,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): tcp=dict( flags=[ dict(flag="all"), - ] + ], ), ), dict( @@ -758,8 +769,8 @@ class TestVyosFirewallRulesModule14(TestVyosModule): dict(flag="syn"), dict(flag="fin", invert=True), ], - ) - ) + ), + ), ], ), ], @@ -787,7 +798,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_v4_rule_sets_change_state_01(self): """Test that a rule set is replaced applied without touching the other stanzas - in particular variant attributes such as state + in particular variant attributes such as state """ set_module_args( dict( @@ -826,7 +837,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_v6_rule_sets_rule_merged_icmp_01(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( @@ -859,7 +870,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_v4_rule_sets_rule_merged_icmp_01(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( @@ -893,7 +904,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_v4_rule_sets_rule_merged_icmp_02(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( @@ -925,8 +936,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_del_01(self): - """Test if plugin correctly removes existing rule set - """ + """Test if plugin correctly removes existing rule set""" set_module_args( dict( config=[dict(afi="ipv4", rule_sets=[dict(name="V4-INGRESS")])], @@ -937,8 +947,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_02(self): - """Test if plugin correctly removes existing rule sets, both ipv4 and ipv6 - """ + """Test if plugin correctly removes existing rule sets, both ipv4 and ipv6""" set_module_args( dict( config=[ @@ -956,15 +965,14 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_v4v6_rule_sets_del_03(self): """Test that the plugin correctly deprovisions - variant configuration + variant configuration """ set_module_args(dict(config=[], state="deleted")) commands = ["delete firewall ipv4", "delete firewall ipv6"] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_04(self): - """Test if plugin has no effect on non-existent rule sets - """ + """Test if plugin has no effect on non-existent rule sets""" set_module_args( dict( config=[ @@ -976,9 +984,20 @@ class TestVyosFirewallRulesModule14(TestVyosModule): ) self.execute_module(changed=False, commands=[]) + def test_vyos_firewall_v4_rule_sets_del_05(self): + """Test if plugin correctly removes existing rule set""" + set_module_args( + dict( + config=[dict(afi="ipv4", rule_sets=[dict(name="V4-OFFLOAD")])], + state="deleted", + ), + ) + commands = ["delete firewall ipv4 name V4-OFFLOAD"] + self.execute_module(changed=True, commands=commands) + def test_vyos_firewall_v4v6_rule_sets_rule_rep_01(self): """Test if plugin correctly replaces a particular rule set(s) - without affecting the others + without affecting the others """ set_module_args( dict( @@ -1058,7 +1077,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_v4v6_rule_sets_rule_rep_02(self): """Test if plugin correctly replaces a particular rule(s) and rule set attribute(s) - without affecting the others + without affecting the others """ set_module_args( dict( @@ -1122,8 +1141,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_rep_idem_01(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ @@ -1144,7 +1162,17 @@ class TestVyosFirewallRulesModule14(TestVyosModule): protocol="icmp", disable=True, log="enable", - ) + ), + ], + ), + dict( + name="V4-OFFLOAD", + rules=[ + dict( + number="105", + action="offload", + offload_target="test", + ), ], ), dict( @@ -1178,7 +1206,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): inbound_interface=dict(name="eth0"), outbound_interface=dict(group="the-ethers"), disable=True, - ) + ), ], ), dict( @@ -1233,8 +1261,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_rep_idem_02(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ @@ -1255,7 +1282,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): protocol="icmp", disable=True, log="enable", - ) + ), ], ), ], @@ -1267,8 +1294,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_mer_idem_01(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ @@ -1289,7 +1315,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): protocol="icmp", disable=True, log="enable", - ) + ), ], ), dict( @@ -1323,7 +1349,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): inbound_interface=dict(name="eth0"), outbound_interface=dict(group="the-ethers"), disable=True, - ) + ), ], ), dict( @@ -1378,8 +1404,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_ovr_01(self): - """Test if plugin correctly resets the entire rule set if there is a change in the configuration - """ + """Test if plugin correctly resets the entire rule set if there is a change in the configuration""" set_module_args( dict( config=[ @@ -1461,6 +1486,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): "delete firewall ipv6 input filter", "delete firewall ipv6 output filter", "delete firewall ipv4 name IF-TEST", + "delete firewall ipv4 name V4-OFFLOAD", "set firewall ipv4 name MULTIPLE-RULE default-action 'drop'", "set firewall ipv4 name MULTIPLE-RULE rule 1", "set firewall ipv4 name MULTIPLE-RULE rule 1 action 'accept'", @@ -1492,7 +1518,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_v4v6_rule_sets_rule_ovr_02(self): """Test that the plugin correctly resets the entire - rule sets configuration if changes are detected + rule sets configuration if changes are detected """ set_module_args( dict( @@ -1547,6 +1573,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): "delete firewall ipv6 output filter", "delete firewall ipv4 name IF-TEST", "delete firewall ipv4 name MULTIPLE-RULE", + "delete firewall ipv4 name V4-OFFLOAD", "set firewall ipv4 name V4-INGRESS rule 101", "set firewall ipv4 name V4-INGRESS default-log", "set firewall ipv4 name V4-INGRESS description 'This is IPv4 INGRESS rule set'", @@ -1557,13 +1584,13 @@ class TestVyosFirewallRulesModule14(TestVyosModule): "set firewall ipv6 name EGRESS default-action 'reject'", "set firewall ipv6 name EGRESS rule 20", "set firewall ipv6 name EGRESS rule 20 protocol 'udp'", - "set firewall ipv6 name EGRESS rule 20 action 'accept'" + "set firewall ipv6 name EGRESS rule 20 action 'accept'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_ovr_idem_01(self): """Test that the plugin is idempotent in overridden state - if there are no changes to the rule sets + if there are no changes to the rule sets """ set_module_args( dict( @@ -1585,7 +1612,17 @@ class TestVyosFirewallRulesModule14(TestVyosModule): protocol="icmp", disable=True, log="enable", - ) + ), + ], + ), + dict( + name="V4-OFFLOAD", + rules=[ + dict( + number="105", + action="offload", + offload_target="test", + ), ], ), dict( @@ -1619,7 +1656,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): inbound_interface=dict(name="eth0"), outbound_interface=dict(group="the-ethers"), disable=True, - ) + ), ], ), dict( @@ -1690,8 +1727,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v6_rule_sets_rule_merged_01_version(self): - """Test if plugin correctly adds ipv6 rule set with rules - """ + """Test if plugin correctly adds ipv6 rule set with rules""" set_module_args( dict( config=[ @@ -1745,13 +1781,12 @@ class TestVyosFirewallRulesModule14(TestVyosModule): "set firewall ipv6 name INBOUND rule 102 action 'reject'", "set firewall ipv6 name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", "set firewall ipv6 name INBOUND rule 102 protocol 'ipv6-icmp'", - 'set firewall ipv6 name INBOUND rule 102 icmpv6 type 7', + "set firewall ipv6 name INBOUND rule 102 icmpv6 type 7", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_jump_rules_merged_01(self): - """Test if plugin correctly adds rule set with a jump action - """ + """Test if plugin correctly adds rule set with a jump action""" set_module_args( dict( config=[ @@ -1772,7 +1807,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): protocol="icmp", icmp=dict(type_name="echo-request"), jump_target="PROTECT-RE", - packet_length_exclude=[dict(length=100), dict(length=200)] + packet_length_exclude=[dict(length=100), dict(length=200)], ), dict( number="102", @@ -1784,10 +1819,10 @@ class TestVyosFirewallRulesModule14(TestVyosModule): ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6 name INBOUND default-action 'accept'", @@ -1806,7 +1841,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): "set firewall ipv6 name INBOUND rule 102 action 'reject'", "set firewall ipv6 name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", "set firewall ipv6 name INBOUND rule 102 protocol 'ipv6-icmp'", - 'set firewall ipv6 name INBOUND rule 102 icmpv6 type 7', + "set firewall ipv6 name INBOUND rule 102 icmpv6 type 7", ] self.execute_module(changed=True, commands=commands) @@ -1833,10 +1868,10 @@ class TestVyosFirewallRulesModule14(TestVyosModule): ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6 name INBOUND default-action 'accept'", @@ -1852,7 +1887,7 @@ class TestVyosFirewallRulesModule14(TestVyosModule): def test_vyos_firewall_log_replace_01(self): """Test that stanza is correctly replaced - without touching the other stanzas + without touching the other stanzas """ set_module_args( dict( @@ -1877,10 +1912,10 @@ class TestVyosFirewallRulesModule14(TestVyosModule): ], ), ], - ) + ), ], state="replaced", - ) + ), ) commands = [ "delete firewall ipv4 name V4-INGRESS rule 101", diff --git a/tests/unit/modules/network/vyos/test_vyos_route_maps.py b/tests/unit/modules/network/vyos/test_vyos_route_maps.py index ce13dcf2..db83ea40 100644 --- a/tests/unit/modules/network/vyos/test_vyos_route_maps.py +++ b/tests/unit/modules/network/vyos/test_vyos_route_maps.py @@ -50,6 +50,18 @@ class TestVyosRouteMapsModule(TestVyosModule): ) self.execute_show_command = self.mock_execute_show_command.start() + self.mock_get_os_version = patch( + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.route_maps.route_maps.get_os_version", + ) + self.test_version = "1.2" + self.get_os_version = self.mock_get_os_version.start() + self.get_os_version.return_value = self.test_version + self.mock_facts_get_os_version = patch( + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.route_maps.route_maps.get_os_version", + ) + self.get_facts_os_version = self.mock_facts_get_os_version.start() + self.get_facts_os_version.return_value = self.test_version + self.maxDiff = None def tearDown(self): super(TestVyosRouteMapsModule, self).tearDown() @@ -176,21 +188,25 @@ class TestVyosRouteMapsModule(TestVyosModule): self.execute_module(changed=True, commands=commands) - def test_route_maps_replaced(self): + def test_route_maps_extras_merged(self): set_module_args( dict( config=[ dict( - route_map="test3", + route_map="test2", entries=[ dict( sequence=1, action="permit", + call="2", + continue_sequence=2, match=dict( rpki="invalid", + interface="eth2", metric=1, peer="1.1.1.3", ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), + community=dict(community_list="235"), ), set=dict( ipv6_next_hop=dict( @@ -198,9 +214,17 @@ class TestVyosRouteMapsModule(TestVyosModule): value="fdda:5cc1:23:4::1f", ), community=dict(value="internet"), + extcommunity_rt="22:11", + extcommunity_soo="220:110", + extcommunity_bandwidth="100", + extcommunity_bandwidth_non_transitive=True, + atomic_aggregate=True, + aggregator={"ip": "10.20.11.22", "as": "245"}, bgp_extcommunity_rt="22:11", ip_next_hop="10.20.10.22", large_community="10:20:21", + as_path_prepend="100 200 350", + as_path_exclude="150", local_preference=4, metric=5, metric_type="type-2", @@ -209,6 +233,85 @@ class TestVyosRouteMapsModule(TestVyosModule): src="10.0.2.15", tag=4, weight=4, + table=7, + ), + ), + ], + ), + ], + state="merged", + ), + ) + commands = [ + "set policy route-map test2 rule 1 action permit", + "set policy route-map test2 rule 1 call 2", + "set policy route-map test2 rule 1 set bgp-extcommunity-rt 22:11", + "set policy route-map test2 rule 1 set ip-next-hop 10.20.10.22", + "set policy route-map test2 rule 1 set ipv6-next-hop global fdda:5cc1:23:4::1f", + "set policy route-map test2 rule 1 set large-community 10:20:21", + "set policy route-map test2 rule 1 set as-path-prepend '100 200 350'", + "set policy route-map test2 rule 1 set as-path-exclude 150", + "set policy route-map test2 rule 1 set local-preference 4", + "set policy route-map test2 rule 1 set metric 5", + "set policy route-map test2 rule 1 set metric-type type-2", + "set policy route-map test2 rule 1 set origin egp", + "set policy route-map test2 rule 1 set originator-id 10.0.2.2", + "set policy route-map test2 rule 1 set src 10.0.2.15", + "set policy route-map test2 rule 1 set tag 4", + "set policy route-map test2 rule 1 set weight 4", + "set policy route-map test2 rule 1 set table 7", + "set policy route-map test2 rule 1 set community internet", + "set policy route-map test2 rule 1 set extcommunity-rt 22:11", + "set policy route-map test2 rule 1 set extcommunity-soo 220:110", + "set policy route-map test2 rule 1 set extcommunity bandwidth 100", + "set policy route-map test2 rule 1 set extcommunity bandwidth-non-transitive", + "set policy route-map test2 rule 1 set atomic-aggregate", + "set policy route-map test2 rule 1 set aggregator as 245", + "set policy route-map test2 rule 1 set aggregator ip 10.20.11.22", + "set policy route-map test2 rule 1 match interface eth2", + "set policy route-map test2 rule 1 match metric 1", + "set policy route-map test2 rule 1 match peer 1.1.1.3", + "set policy route-map test2 rule 1 match ipv6 nexthop fdda:5cc1:23:4::1f", + "set policy route-map test2 rule 1 match rpki invalid", + "set policy route-map test2 rule 1 match community community-list 235", + "set policy route-map test2 rule 1 continue 2", + ] + + self.execute_module(changed=True, commands=commands) + + def test_route_maps_replaced(self): + set_module_args( + dict( + config=[ + dict( + route_map="test3", + entries=[ + dict( + sequence=1, + action="permit", + match=dict( + rpki="invalid", + metric=1, + peer="1.1.1.3", + ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), + ), + set=dict( + ipv6_next_hop=dict( + ip_type="global", + value="fdda:5cc1:23:4::1f", + ), + community=dict(value="100:100"), + bgp_extcommunity_rt="22:11", + ip_next_hop="10.20.10.22", + large_community="10:20:21", + local_preference=4, + metric=5, + metric_type="type-2", + origin="egp", + originator_id="10.0.2.2", + src="fdda:5cc1:23:4::12", + tag=4, + weight=4, ), ), ], @@ -220,10 +323,12 @@ class TestVyosRouteMapsModule(TestVyosModule): commands = [ "delete policy route-map test3 rule 1 match interface eth2", "set policy route-map test3 rule 1 set ip-next-hop 10.20.10.22", + "set policy route-map test3 rule 1 set community 100:100", "set policy route-map test3 rule 1 set large-community 10:20:21", "set policy route-map test3 rule 1 set metric-type type-2", "set policy route-map test3 rule 1 set originator-id 10.0.2.2", "set policy route-map test3 rule 1 set tag 4", + "set policy route-map test3 rule 1 set src fdda:5cc1:23:4::12", "set policy route-map test3 rule 1 match peer 1.1.1.3", ] self.execute_module(changed=True, commands=commands) @@ -328,6 +433,32 @@ class TestVyosRouteMapsModule(TestVyosModule): ] self.execute_module(changed=True, commands=commands) + def test_route_maps__deny_overridden(self): + set_module_args( + dict( + config=[ + dict( + route_map="test2", + entries=[ + dict( + sequence=1, + action="deny", + match=dict(rpki="invalid", peer="1.1.1.5"), + ), + ], + ), + ], + state="overridden", + ), + ) + commands = [ + "delete policy route-map test3", + "set policy route-map test2 rule 1 action deny", + "set policy route-map test2 rule 1 match peer 1.1.1.5", + "set policy route-map test2 rule 1 match rpki invalid", + ] + self.execute_module(changed=True, commands=commands) + def test_vyos_route_maps_overridden_idempotent(self): set_module_args( dict( diff --git a/tests/unit/modules/network/vyos/test_vyos_route_maps14.py b/tests/unit/modules/network/vyos/test_vyos_route_maps14.py new file mode 100644 index 00000000..7bc5b74e --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_route_maps14.py @@ -0,0 +1,717 @@ +# (c) 2021 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 <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from unittest.mock import patch + +from ansible_collections.vyos.vyos.plugins.modules import vyos_route_maps +from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args + +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosRouteMapsModule(TestVyosModule): + module = vyos_route_maps + + def setUp(self): + super(TestVyosRouteMapsModule, self).setUp() + + self.mock_get_resource_connection_config = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base.get_resource_connection", + ) + self.get_resource_connection_config = self.mock_get_resource_connection_config.start() + + self.mock_get_resource_connection_facts = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection", + ) + self.get_resource_connection_facts = self.mock_get_resource_connection_facts.start() + + self.mock_execute_show_command = patch( + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.route_maps.route_maps.Route_mapsFacts.get_config", + ) + + self.execute_show_command = self.mock_execute_show_command.start() + self.mock_get_os_version = patch( + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.route_maps.route_maps.get_os_version", + ) + self.test_version = "1.4" + self.get_os_version = self.mock_get_os_version.start() + self.get_os_version.return_value = self.test_version + self.mock_facts_get_os_version = patch( + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.route_maps.route_maps.get_os_version", + ) + self.get_facts_os_version = self.mock_facts_get_os_version.start() + self.get_facts_os_version.return_value = self.test_version + self.maxDiff = None + + def tearDown(self): + super(TestVyosRouteMapsModule, self).tearDown() + self.mock_get_resource_connection_config.stop() + self.mock_get_resource_connection_facts.stop() + self.mock_execute_show_command.stop() + + def load_fixtures(self, commands=None, filename=None): + if filename is None: + filename = "vyos_route_maps_config_v14.cfg" + + def load_from_file(*args, **kwargs): + output = load_fixture(filename) + return output + + self.execute_show_command.side_effect = load_from_file + + def test_vyos_route_maps_merged_idempotent(self): + set_module_args( + dict( + config=[ + dict( + route_map="test3", + entries=[ + dict( + sequence=1, + action="permit", + match=dict( + rpki="invalid", + interface="eth2", + metric=1, + peer="1.1.1.2", + ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), + ), + set=dict( + ipv6_next_hop=dict( + ip_type="global", + value="fdda:5cc1:23:4::1f", + ), + community=dict(value="internet"), + bgp_extcommunity_rt="22:11", + ip_next_hop="10.20.10.20", + local_preference=4, + metric=5, + metric_type="type-1", + origin="egp", + originator_id="10.0.2.3", + src="10.0.2.15", + tag=5, + weight=4, + ), + ), + ], + ), + ], + state="merged", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_route_maps_merged(self): + set_module_args( + dict( + config=[ + dict( + route_map="test2", + entries=[ + dict( + sequence=1, + action="permit", + match=dict( + rpki="invalid", + interface="eth2", + metric=1, + peer="1.1.1.3", + ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), + ), + set=dict( + ipv6_next_hop=dict( + ip_type="global", + value="fdda:5cc1:23:4::1f", + ), + community=dict(value="internet"), + bgp_extcommunity_rt="22:11", + ip_next_hop="10.20.10.22", + large_community="10:20:21", + local_preference=4, + metric=5, + metric_type="type-2", + origin="egp", + originator_id="10.0.2.2", + src="10.0.2.15", + tag=4, + weight=4, + ), + ), + ], + ), + ], + state="merged", + ), + ) + commands = [ + "set policy route-map test2 rule 1 action permit", + "set policy route-map test2 rule 1 set bgp-extcommunity-rt 22:11", + "set policy route-map test2 rule 1 set ip-next-hop 10.20.10.22", + "set policy route-map test2 rule 1 set ipv6-next-hop global fdda:5cc1:23:4::1f", + "set policy route-map test2 rule 1 set large-community replace 10:20:21", + "set policy route-map test2 rule 1 set local-preference 4", + "set policy route-map test2 rule 1 set metric 5", + "set policy route-map test2 rule 1 set metric-type type-2", + "set policy route-map test2 rule 1 set origin egp", + "set policy route-map test2 rule 1 set originator-id 10.0.2.2", + "set policy route-map test2 rule 1 set src 10.0.2.15", + "set policy route-map test2 rule 1 set tag 4", + "set policy route-map test2 rule 1 set weight 4", + "set policy route-map test2 rule 1 set community replace internet", + "set policy route-map test2 rule 1 match interface eth2", + "set policy route-map test2 rule 1 match metric 1", + "set policy route-map test2 rule 1 match peer 1.1.1.3", + "set policy route-map test2 rule 1 match ipv6 nexthop fdda:5cc1:23:4::1f", + "set policy route-map test2 rule 1 match rpki invalid", + ] + + self.execute_module(changed=True, commands=commands) + + def test_route_maps_extras_merged(self): + set_module_args( + dict( + config=[ + dict( + route_map="test2", + entries=[ + dict( + sequence=1, + action="permit", + call="2", + continue_sequence=2, + match=dict( + rpki="invalid", + interface="eth2", + metric=1, + peer="1.1.1.3", + ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), + community=dict(community_list="235"), + protocol="bgp", + ), + set=dict( + ipv6_next_hop=dict( + ip_type="global", + value="fdda:5cc1:23:4::1f", + ), + community=dict(value="internet"), + extcommunity_rt="22:11", + extcommunity_soo="220:110", + extcommunity_bandwidth="100", + extcommunity_bandwidth_non_transitive=True, + atomic_aggregate=True, + aggregator={"ip": "10.20.11.22", "as": "245"}, + bgp_extcommunity_rt="22:11", + ip_next_hop="10.20.10.22", + large_community="10:20:21", + as_path_prepend="100 200 350", + as_path_exclude="150", + local_preference=4, + metric=5, + metric_type="type-2", + origin="egp", + originator_id="10.0.2.2", + src="10.0.2.15", + tag=4, + weight=4, + table=7, + ), + ), + ], + ), + ], + state="merged", + ), + ) + commands = [ + "set policy route-map test2 rule 1 action permit", + "set policy route-map test2 rule 1 call 2", + "set policy route-map test2 rule 1 set bgp-extcommunity-rt 22:11", + "set policy route-map test2 rule 1 set ip-next-hop 10.20.10.22", + "set policy route-map test2 rule 1 set ipv6-next-hop global fdda:5cc1:23:4::1f", + "set policy route-map test2 rule 1 set large-community replace 10:20:21", + "set policy route-map test2 rule 1 set as-path prepend '100 200 350'", + "set policy route-map test2 rule 1 set as-path exclude 150", + "set policy route-map test2 rule 1 set local-preference 4", + "set policy route-map test2 rule 1 set metric 5", + "set policy route-map test2 rule 1 set metric-type type-2", + "set policy route-map test2 rule 1 set origin egp", + "set policy route-map test2 rule 1 set originator-id 10.0.2.2", + "set policy route-map test2 rule 1 set src 10.0.2.15", + "set policy route-map test2 rule 1 set tag 4", + "set policy route-map test2 rule 1 set weight 4", + "set policy route-map test2 rule 1 set table 7", + "set policy route-map test2 rule 1 set community replace internet", + "set policy route-map test2 rule 1 set extcommunity rt 22:11", + "set policy route-map test2 rule 1 set extcommunity soo 220:110", + "set policy route-map test2 rule 1 set extcommunity bandwidth 100", + "set policy route-map test2 rule 1 set extcommunity bandwidth-non-transitive", + "set policy route-map test2 rule 1 set atomic-aggregate", + "set policy route-map test2 rule 1 set aggregator as 245", + "set policy route-map test2 rule 1 set aggregator ip 10.20.11.22", + "set policy route-map test2 rule 1 match interface eth2", + "set policy route-map test2 rule 1 match metric 1", + "set policy route-map test2 rule 1 match peer 1.1.1.3", + "set policy route-map test2 rule 1 match ipv6 nexthop fdda:5cc1:23:4::1f", + "set policy route-map test2 rule 1 match rpki invalid", + "set policy route-map test2 rule 1 match protocol bgp", + "set policy route-map test2 rule 1 match community community-list 235", + "set policy route-map test2 rule 1 continue 2", + ] + + self.execute_module(changed=True, commands=commands) + + def test_route_maps_replaced(self): + set_module_args( + dict( + config=[ + dict( + route_map="test3", + entries=[ + dict( + sequence=1, + action="permit", + match=dict( + rpki="invalid", + metric=1, + peer="1.1.1.3", + ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), + ), + set=dict( + ipv6_next_hop=dict( + ip_type="global", + value="fdda:5cc1:23:4::1f", + ), + community=dict(value="100:100"), + bgp_extcommunity_rt="22:11", + ip_next_hop="10.20.10.22", + large_community="10:20:21", + local_preference=4, + metric=5, + metric_type="type-2", + origin="egp", + originator_id="10.0.2.2", + src="fdda:5cc1:23:4::12", + tag=4, + weight=4, + ), + ), + ], + ), + ], + state="replaced", + ), + ) + commands = [ + "delete policy route-map test3 rule 1 match interface eth2", + "set policy route-map test3 rule 1 set ip-next-hop 10.20.10.22", + "set policy route-map test3 rule 1 set community replace 100:100", + "set policy route-map test3 rule 1 set large-community replace 10:20:21", + "set policy route-map test3 rule 1 set metric-type type-2", + "set policy route-map test3 rule 1 set originator-id 10.0.2.2", + "set policy route-map test3 rule 1 set tag 4", + "set policy route-map test3 rule 1 set src fdda:5cc1:23:4::12", + "set policy route-map test3 rule 1 match peer 1.1.1.3", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_route_maps_replaced_idempotent(self): + set_module_args( + dict( + config=[ + dict( + route_map="test3", + entries=[ + dict( + sequence=1, + action="permit", + match=dict( + rpki="invalid", + interface="eth2", + metric=1, + peer="1.1.1.2", + ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), + ), + set=dict( + ipv6_next_hop=dict( + ip_type="global", + value="fdda:5cc1:23:4::1f", + ), + community=dict(value="internet"), + bgp_extcommunity_rt="22:11", + ip_next_hop="10.20.10.20", + local_preference=4, + metric=5, + metric_type="type-1", + origin="egp", + originator_id="10.0.2.3", + src="10.0.2.15", + tag=5, + weight=4, + ), + ), + ], + ), + ], + state="replaced", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_route_maps_overridden(self): + set_module_args( + dict( + config=[ + dict( + route_map="test2", + entries=[ + dict( + sequence=1, + action="permit", + match=dict(rpki="invalid", peer="1.1.1.3"), + set=dict( + ipv6_next_hop=dict( + ip_type="global", + value="fdda:5cc1:23:4::1f", + ), + community=dict(value="internet"), + bgp_extcommunity_rt="22:11", + ip_next_hop="10.20.10.22", + large_community="10:20:21", + local_preference=4, + metric=5, + metric_type="type-2", + origin="egp", + originator_id="10.0.2.2", + src="10.0.2.15", + tag=4, + weight=4, + ), + ), + ], + ), + ], + state="overridden", + ), + ) + commands = [ + "delete policy route-map test3", + "set policy route-map test2 rule 1 action permit", + "set policy route-map test2 rule 1 set bgp-extcommunity-rt 22:11", + "set policy route-map test2 rule 1 set ip-next-hop 10.20.10.22", + "set policy route-map test2 rule 1 set ipv6-next-hop global fdda:5cc1:23:4::1f", + "set policy route-map test2 rule 1 set large-community replace 10:20:21", + "set policy route-map test2 rule 1 set local-preference 4", + "set policy route-map test2 rule 1 set metric 5", + "set policy route-map test2 rule 1 set metric-type type-2", + "set policy route-map test2 rule 1 set origin egp", + "set policy route-map test2 rule 1 set originator-id 10.0.2.2", + "set policy route-map test2 rule 1 set src 10.0.2.15", + "set policy route-map test2 rule 1 set tag 4", + "set policy route-map test2 rule 1 set weight 4", + "set policy route-map test2 rule 1 set community replace internet", + "set policy route-map test2 rule 1 match peer 1.1.1.3", + "set policy route-map test2 rule 1 match rpki invalid", + ] + self.execute_module(changed=True, commands=commands) + + def test_route_maps__deny_overridden(self): + set_module_args( + dict( + config=[ + dict( + route_map="test2", + entries=[ + dict( + sequence=1, + action="deny", + match=dict(rpki="invalid", peer="1.1.1.5"), + ), + ], + ), + ], + state="overridden", + ), + ) + commands = [ + "delete policy route-map test3", + "set policy route-map test2 rule 1 action deny", + "set policy route-map test2 rule 1 match peer 1.1.1.5", + "set policy route-map test2 rule 1 match rpki invalid", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_route_maps_overridden_idempotent(self): + set_module_args( + dict( + config=[ + dict( + route_map="test3", + entries=[ + dict( + sequence=1, + action="permit", + match=dict( + rpki="invalid", + interface="eth2", + metric=1, + peer="1.1.1.2", + ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), + ), + set=dict( + ipv6_next_hop=dict( + ip_type="global", + value="fdda:5cc1:23:4::1f", + ), + community=dict(value="internet"), + bgp_extcommunity_rt="22:11", + ip_next_hop="10.20.10.20", + local_preference=4, + metric=5, + metric_type="type-1", + origin="egp", + originator_id="10.0.2.3", + src="10.0.2.15", + tag=5, + weight=4, + ), + ), + ], + ), + ], + state="overridden", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_route_maps_rendered(self): + set_module_args( + dict( + config=[ + dict( + route_map="test3", + entries=[ + dict( + sequence=1, + action="permit", + match=dict( + rpki="invalid", + interface="eth2", + metric=1, + peer="1.1.1.2", + ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), + ), + set=dict( + ipv6_next_hop=dict( + ip_type="global", + value="fdda:5cc1:23:4::1f", + ), + community=dict(value="internet"), + bgp_extcommunity_rt="22:11", + ip_next_hop="10.20.10.20", + local_preference=4, + metric=5, + metric_type="type-1", + origin="egp", + originator_id="10.0.2.3", + src="10.0.2.15", + tag=5, + weight=4, + ), + ), + ], + ), + dict( + route_map="test1", + entries=[ + dict( + sequence=1, + action="permit", + description="test", + on_match=dict(next=True), + ), + dict( + sequence=2, + action="permit", + on_match=dict(goto=4), + ), + ], + ), + ], + state="rendered", + ), + ) + rendered_cmds = [ + "set policy route-map test3 rule 1 action permit", + "set policy route-map test3 rule 1 set bgp-extcommunity-rt 22:11", + "set policy route-map test3 rule 1 set ip-next-hop 10.20.10.20", + "set policy route-map test3 rule 1 set ipv6-next-hop global fdda:5cc1:23:4::1f", + "set policy route-map test3 rule 1 set local-preference 4", + "set policy route-map test3 rule 1 set metric 5", + "set policy route-map test3 rule 1 set metric-type type-1", + "set policy route-map test3 rule 1 set origin egp", + "set policy route-map test3 rule 1 set originator-id 10.0.2.3", + "set policy route-map test3 rule 1 set src 10.0.2.15", + "set policy route-map test3 rule 1 set tag 5", + "set policy route-map test3 rule 1 set weight 4", + "set policy route-map test3 rule 1 set community replace internet", + "set policy route-map test3 rule 1 match interface eth2", + "set policy route-map test3 rule 1 match metric 1", + "set policy route-map test3 rule 1 match peer 1.1.1.2", + "set policy route-map test3 rule 1 match ipv6 nexthop fdda:5cc1:23:4::1f", + "set policy route-map test3 rule 1 match rpki invalid", + "set policy route-map test1 rule 1 description test", + "set policy route-map test1 rule 1 action permit", + "set policy route-map test1 rule 1 on-match next", + "set policy route-map test1 rule 2 action permit", + "set policy route-map test1 rule 2 on-match goto 4", + ] + result = self.execute_module(changed=False) + self.assertEqual( + sorted(result["rendered"]), + sorted(rendered_cmds), + result["rendered"], + ) + + def test_yos_route_maps_parsed(self): + parsed_str = ( + "set policy route-map test3 rule 1 action 'permit'" + "\nset policy route-map test3 rule 1 match interface 'eth2'\nset policy route-map test3 rule 1 match ipv6 nexthop" + " 'fdda:5cc1:23:4::1f'\nset policy route-map test3 rule 1 match metric '1'\nset policy route-map test3 rule 1 match peer " + "'1.1.1.2'\nset policy route-map test3 rule 1 match rpki 'invalid'\nset policy route-map test3 rule 1 set bgp-extcommunity-rt " + "'22:11'\nset policy route-map test3 rule 1 set community replace 'internet'\nset policy route-map test3 rule 1 set ipv6-next-hop global" + " 'fdda:5cc1:23:4::1f'\nset policy route-map test3 rule 1 set ip-next-hop '10.20.10.20'\nset policy route-map " + "test3 rule 1 set local-preference '4'\nset policy route-map test3 rule 1 set metric '5'\nset policy route-map test3 " + "rule 1 set metric-type 'type-1'\nset policy route-map test3 rule 1 set origin 'egp'\nset policy route-map test3 rule 1 set originator-id " + "'10.0.2.3'\nset policy route-map test3 rule 1 set src '10.0.2.15'" + "\nset policy route-map test3 rule 1 set tag '5'\nset policy route-map test3 rule 1 set weight '4'" + ) + set_module_args(dict(running_config=parsed_str, state="parsed")) + result = self.execute_module(changed=False) + parsed_list = [ + { + "entries": [ + { + "action": "permit", + "match": { + "interface": "eth2", + "ipv6": {"next_hop": "fdda:5cc1:23:4::1f"}, + "metric": 1, + "peer": "1.1.1.2", + "rpki": "invalid", + }, + "sequence": 1, + "set": { + "bgp_extcommunity_rt": "22:11", + "community": {"value": "internet"}, + "ip_next_hop": "10.20.10.20", + "ipv6_next_hop": { + "ip_type": "global", + "value": "fdda:5cc1:23:4::1f", + }, + "local_preference": "4", + "metric": "5", + "metric_type": "type-1", + "origin": "egp", + "originator_id": "10.0.2.3", + "src": "10.0.2.15", + "tag": "5", + "weight": "4", + }, + }, + ], + "route_map": "test3", + }, + ] + self.assertEqual(parsed_list, result["parsed"]) + + def test_vyos_route_maps_gathered(self): + set_module_args(dict(state="gathered")) + result = self.execute_module(changed=False) + gathered_list = [ + { + "entries": [ + { + "action": "permit", + "match": { + "interface": "eth2", + "ipv6": {"next_hop": "fdda:5cc1:23:4::1f"}, + "metric": 1, + "peer": "1.1.1.2", + "rpki": "invalid", + }, + "sequence": 1, + "set": { + "bgp_extcommunity_rt": "22:11", + "community": {"value": "internet"}, + "ip_next_hop": "10.20.10.20", + "ipv6_next_hop": { + "ip_type": "global", + "value": "fdda:5cc1:23:4::1f", + }, + "local_preference": "4", + "metric": "5", + "metric_type": "type-1", + "origin": "egp", + "originator_id": "10.0.2.3", + "src": "10.0.2.15", + "tag": "5", + "weight": "4", + }, + }, + ], + "route_map": "test3", + }, + ] + self.assertEqual(gathered_list, result["gathered"]) + + def test_vyos_route_maps_deleted(self): + set_module_args( + dict( + config=[ + dict( + route_map="test3", + entries=[ + dict( + sequence=1, + action="permit", + match=dict( + rpki="invalid", + interface="eth2", + ), + set=dict( + origin="egp", + originator_id="10.0.2.3", + src="10.0.2.15", + tag=5, + weight=4, + ), + ), + ], + ), + ], + state="deleted", + ), + ) + commands = ["delete policy route-map test3"] + self.execute_module(changed=True, commands=commands) diff --git a/tests/unit/modules/network/vyos/test_vyos_system.py b/tests/unit/modules/network/vyos/test_vyos_system.py index cf405cab..5edfa0df 100644 --- a/tests/unit/modules/network/vyos/test_vyos_system.py +++ b/tests/unit/modules/network/vyos/test_vyos_system.py @@ -45,11 +45,26 @@ class TestVyosSystemModule(TestVyosModule): ) self.load_config = self.mock_load_config.start() + self.mock_get_os_version = patch( + "ansible_collections.vyos.vyos.plugins.modules.vyos_system.get_os_version", + ) + self.test_version = "1.2" + self.get_os_version = self.mock_get_os_version.start() + self.get_os_version.return_value = self.test_version + self.mock_facts_get_os_version = patch( + "ansible_collections.vyos.vyos.plugins.modules.vyos_system.get_os_version", + ) + self.get_facts_os_version = self.mock_facts_get_os_version.start() + self.get_facts_os_version.return_value = self.test_version + self.maxDiff = None + def tearDown(self): super(TestVyosSystemModule, self).tearDown() self.mock_get_config.stop() self.mock_load_config.stop() + self.mock_get_os_version.stop() + self.mock_facts_get_os_version.stop() def load_fixtures(self, commands=None, filename=None): self.get_config.return_value = load_fixture("vyos_config_config.cfg") @@ -112,3 +127,67 @@ class TestVyosSystemModule(TestVyosModule): "delete system name-server", ] self.execute_module(changed=True, commands=commands) + + +class TestVyosSystemModule14(TestVyosModule): + module = vyos_system + + def setUp(self): + super(TestVyosSystemModule14, self).setUp() + + self.mock_get_config = patch( + "ansible_collections.vyos.vyos.plugins.modules.vyos_system.get_config", + ) + self.get_config = self.mock_get_config.start() + + self.mock_load_config = patch( + "ansible_collections.vyos.vyos.plugins.modules.vyos_system.load_config", + ) + self.load_config = self.mock_load_config.start() + + self.mock_get_os_version = patch( + "ansible_collections.vyos.vyos.plugins.modules.vyos_system.get_os_version", + ) + self.test_version = "1.4" + self.get_os_version = self.mock_get_os_version.start() + self.get_os_version.return_value = self.test_version + self.mock_facts_get_os_version = patch( + "ansible_collections.vyos.vyos.plugins.modules.vyos_system.get_os_version", + ) + self.get_facts_os_version = self.mock_facts_get_os_version.start() + self.get_facts_os_version.return_value = self.test_version + self.maxDiff = None + + def tearDown(self): + super(TestVyosSystemModule14, self).tearDown() + + self.mock_get_config.stop() + self.mock_load_config.stop() + self.mock_get_os_version.stop() + self.mock_facts_get_os_version.stop() + + def load_fixtures(self, commands=None, filename=None): + self.get_config.return_value = load_fixture("vyos_config_config.cfg") + + def test_vyos_system_domain_search(self): + set_module_args(dict(domain_search=["foo.example.com", "bar.example.com"])) + commands = [ + "set system domain-search 'foo.example.com'", + "set system domain-search 'bar.example.com'", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_system_clear_domain_search(self): + set_module_args(dict(domain_search=[])) + commands = ["delete system domain-search"] + self.execute_module(changed=True, commands=commands) + + def test_vyos_system_clear_all(self): + set_module_args(dict(state="absent")) + commands = [ + "delete system host-name", + "delete system domain-search", + "delete system domain-name", + "delete system name-server", + ] + self.execute_module(changed=True, commands=commands) diff --git a/tests/unit/modules/network/vyos/test_vyos_user.py b/tests/unit/modules/network/vyos/test_vyos_user.py index e8c50783..2cbd3820 100644 --- a/tests/unit/modules/network/vyos/test_vyos_user.py +++ b/tests/unit/modules/network/vyos/test_vyos_user.py @@ -62,6 +62,36 @@ class TestVyosUserModule(TestVyosModule): ["set system login user ansible authentication plaintext-password test"], ) + def test_vyos_user_password_special_chars(self): + set_module_args(dict(name="ansible", configured_password="test$123!@")) + result = self.execute_module(changed=True) + self.assertEqual( + result["commands"], + [ + "set system login user ansible authentication plaintext-password 'test$123!@'", + ], + ) + + def test_vyos_user_password_embedded_quote(self): + set_module_args(dict(name="ansible", configured_password="pa'ss")) + result = self.execute_module(changed=True) + self.assertEqual( + result["commands"], + [ + "set system login user ansible authentication plaintext-password 'pa'\"'\"'ss'", + ], + ) + + def test_vyos_user_password_complex_special_chars(self): + set_module_args(dict(name="ansible", configured_password="P@ss w0rd!$#'xy\\")) + result = self.execute_module(changed=True) + self.assertEqual( + result["commands"], + [ + "set system login user ansible authentication plaintext-password 'P@ss w0rd!$#'\"'\"'xy\\'", + ], + ) + def test_vyos_user_delete(self): set_module_args(dict(name="ansible", state="absent")) result = self.execute_module(changed=True) @@ -237,3 +267,43 @@ class TestVyosUserModule(TestVyosModule): ), ) result = self.execute_module(changed=False) + + def test_vyos_user_aggregate_with_public_keys(self): + set_module_args( + dict( + aggregate=[ + dict( + name="user1", + public_keys=[ + dict( + name="user1@host1", + key="AAAAC3NzaC1lZDI1NTE5AAAAIFIR0jrMvBdmvTJNY5EDhOD+eixvbOinhY1eBU2u", + type="ssh-ed25519", + ), + ], + ), + dict( + name="user2", + public_keys=[ + dict( + name="user2@host2", + key="AAAAC3NzaC1lZDI1NTE5AAAAIFIR0jrMvBdmvTJNY5EDhOD+eixvbOinhY1eBU2u", + type="ssh-ed25519", + ), + ], + ), + ], + ), + ) + result = self.execute_module(changed=True) + self.assertEqual( + sorted(result["commands"]), + sorted( + [ + "set system login user user1 authentication public-keys user1@host1 key 'AAAAC3NzaC1lZDI1NTE5AAAAIFIR0jrMvBdmvTJNY5EDhOD+eixvbOinhY1eBU2u'", + "set system login user user1 authentication public-keys user1@host1 type 'ssh-ed25519'", + "set system login user user2 authentication public-keys user2@host2 key 'AAAAC3NzaC1lZDI1NTE5AAAAIFIR0jrMvBdmvTJNY5EDhOD+eixvbOinhY1eBU2u'", + "set system login user user2 authentication public-keys user2@host2 type 'ssh-ed25519'", + ], + ), + ) diff --git a/tests/unit/modules/network/vyos/test_vyos_vlan.py b/tests/unit/modules/network/vyos/test_vyos_vlan.py new file mode 100644 index 00000000..5bbf87c2 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_vlan.py @@ -0,0 +1,158 @@ +# (c) 2016 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 <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from unittest.mock import patch + +from ansible_collections.vyos.vyos.plugins.modules import vyos_vlan +from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args + +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosVlanModule(TestVyosModule): + module = vyos_vlan + + def setUp(self): + super(TestVyosVlanModule, self).setUp() + + self.mock_load_config = patch( + "ansible_collections.vyos.vyos.plugins.modules.vyos_vlan.load_config", + ) + self.load_config = self.mock_load_config.start() + + self.mock_run_commands = patch( + "ansible_collections.vyos.vyos.plugins.modules.vyos_vlan.run_commands", + ) + self.run_commands = self.mock_run_commands.start() + + def tearDown(self): + super(TestVyosVlanModule, self).tearDown() + self.mock_load_config.stop() + self.mock_run_commands.stop() + + def load_fixtures(self, commands=None, filename=None): + self.load_config.return_value = dict(diff=None, session="session") + if filename == "empty": + self.run_commands.return_value = [ + load_fixture("vyos_vlan_show_interfaces_empty.cfg"), + ] + else: + self.run_commands.return_value = [ + load_fixture("vyos_vlan_show_interfaces.cfg"), + ] + + def test_vyos_vlan_present(self): + """Create a new VLAN with a description on eth2 (not in have).""" + set_module_args( + dict( + vlan_id=300, + name="vlan-300", + interfaces=["eth2"], + state="present", + ) + ) + commands = ["set interfaces ethernet eth2 vif 300 description vlan-300"] + self.execute_module(changed=True, commands=commands) + + def test_vyos_vlan_present_no_change(self): + """Existing VLAN 100 on eth0 — no commands should be generated.""" + set_module_args( + dict( + vlan_id=100, + name="vlan-100", + interfaces=["eth0"], + state="present", + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_vlan_absent(self): + """Delete an existing VLAN (200 on eth1).""" + set_module_args( + dict( + vlan_id=200, + interfaces=["eth1"], + state="absent", + ) + ) + commands = ["delete interfaces ethernet eth1 vif 200"] + self.execute_module(changed=True, commands=commands) + + def test_vyos_vlan_absent_no_change(self): + """Delete a VLAN that does not exist — no commands.""" + set_module_args( + dict( + vlan_id=999, + interfaces=["eth0"], + state="absent", + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_vlan_aggregate(self): + """Create two new VLANs via aggregate; neither is in have.""" + set_module_args( + dict( + aggregate=[ + dict(vlan_id=301, interfaces=["eth2"], name="vlan-301"), + dict(vlan_id=302, interfaces=["eth2"], name="vlan-302"), + ], + ) + ) + commands = [ + "set interfaces ethernet eth2 vif 301 description vlan-301", + "set interfaces ethernet eth2 vif 302 description vlan-302", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_vlan_purge(self): + """Purge VLANs not in want. Want only VLAN 100; VLAN 200 should be removed. + + The fixed parser only maps ethX.Y sub-interfaces, so bare ethX interfaces + (vlan_id=None) are not in have. Only real VLANs (eth1.200) are purged. + """ + set_module_args( + dict( + vlan_id=100, + interfaces=["eth0"], + state="present", + purge=True, + ) + ) + commands = [ + "delete interfaces ethernet eth1 vif 200", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_vlan_with_address(self): + """Create a VLAN with an IP address and no description.""" + set_module_args( + dict( + vlan_id=400, + address="10.10.40.1/24", + interfaces=["eth1"], + state="present", + ) + ) + commands = ["set interfaces ethernet eth1 vif 400 address 10.10.40.1/24"] + self.execute_module(changed=True, commands=commands, filename="empty") diff --git a/tests/unit/modules/network/vyos/test_vyos_vrf.py b/tests/unit/modules/network/vyos/test_vyos_vrf.py new file mode 100644 index 00000000..e86227a9 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_vrf.py @@ -0,0 +1,689 @@ +# (c) 2021 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 <http://www.gnu.org/licenses/>. + + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from unittest.mock import MagicMock, patch + +from ansible_collections.vyos.vyos.plugins.modules import vyos_vrf +from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args + +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosVrfModule(TestVyosModule): + module = vyos_vrf + + def setUp(self): + super(TestVyosVrfModule, self).setUp() + + self.fake_connection = MagicMock() + self.fake_connection.get.return_value = "{}" + + self.fake_connection.get_device_info.return_value = { + "version": "1.5", + } + + self.fake_connection.get_device_info.return_value = { + "network_os_major_version": "1.5", + } + patch( + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos.get_connection", + return_value=self.fake_connection, + ).start() + + def _get_resource_connection_side_effect(module, *args, **kwargs): + module._connection = self.fake_connection + return self.fake_connection + + patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base.get_resource_connection", + side_effect=_get_resource_connection_side_effect, + ).start() + + patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection", + side_effect=_get_resource_connection_side_effect, + ).start() + + self.mock_execute_show_command = patch( + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.vrf.vrf.VrfFacts.get_config", + ).start() + + patch( + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.vrf.vrf.get_os_version", + return_value="1.5", + ).start() + + patch( + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_global.bgp_global.get_os_version", + return_value="1.5", + ).start() + + self.maxDiff = None + + def tearDown(self): + super(TestVyosVrfModule, self).tearDown() + patch.stopall() + + def load_fixtures(self, commands=None, filename=None): + if filename is None: + filename = "vyos_vrf_config.cfg" + + def load_from_file(*args, **kwargs): + return load_fixture(filename) + + self.mock_execute_show_command.side_effect = load_from_file + + def test_vrf_merged_idempotent(self): + set_module_args( + dict( + config=dict( + bind_to_all=True, + instances=[ + dict( + name="vrf-blue", + description="blue-vrf", + disable=False, + table_id=100, + vni=1000, + ), + dict( + name="vrf-red", + description="red-vrf", + disable=True, + table_id=101, + vni=1001, + address_family=[ + dict( + afi="ipv4", + disable_forwarding=True, + route_maps=[ + dict(rm_name="rm1", protocol="rip"), + ], + ), + dict( + afi="ipv6", + nht_no_resolve_via_default=True, + ), + ], + protocols=dict( + bgp=dict( + as_number=65000, + neighbor=[ + dict( + address="192.0.2.1", + remote_as=65002, + ), + dict( + address="1.1.1.3", + remote_as=400, + passive=True, + ), + ], + ), + ), + ), + ], + ), + state="merged", + ), + ) + + self.execute_module(changed=False, commands=[]) + + def test_vrf_merged(self): + set_module_args( + dict( + config=dict( + bind_to_all=False, + instances=[ + dict( + name="vrf-green", + description="green-vrf", + table_id=110, + vni=1010, + protocols=dict( + ospf=dict( + log_adjacency_changes="detail", + max_metric=dict( + router_lsa=dict( + administrative=True, + on_shutdown=10, + on_startup=10, + ), + ), + default_information=dict( + originate=dict( + always=True, + metric=10, + metric_type=2, + ), + ), + auto_cost=dict( + reference_bandwidth=2, + ), + neighbor=[ + dict( + neighbor_id="192.0.11.12", + poll_interval=10, + priority=2, + ), + ], + redistribute=[ + dict( + route_type="bgp", + metric=10, + metric_type=2, + ), + ], + parameters=dict( + router_id="192.0.1.1", + rfc1583_compatibility=True, + abr_type="cisco", + ), + areas=[ + dict( + area_id="2", + area_type=dict( + normal=True, + ), + authentication="plaintext-password", + shortcut="enable", + ), + dict( + area_id="3", + area_type=dict( + nssa=dict( + set=True, + ), + ), + ), + dict( + area_id="4", + area_type=dict( + stub=dict( + default_cost=20, + ), + ), + network=[ + dict( + address="192.0.2.0/24", + ), + ], + range=[ + dict( + address="192.0.3.0/24", + cost=10, + ), + dict( + address="192.0.4.0/24", + cost=12, + ), + ], + ), + ], + ), + ), + ), + ], + ), + state="merged", + ), + ) + + commands = [ + "delete vrf bind-to-all", + "set vrf name vrf-green table 110", + "set vrf name vrf-green vni 1010", + "set vrf name vrf-green description green-vrf", + "set vrf name vrf-green protocols ospf log-adjacency-changes 'detail'", + "set vrf name vrf-green protocols ospf max-metric router-lsa administrative", + "set vrf name vrf-green protocols ospf max-metric router-lsa on-shutdown 10", + "set vrf name vrf-green protocols ospf max-metric router-lsa on-startup 10", + "set vrf name vrf-green protocols ospf default-information originate always", + "set vrf name vrf-green protocols ospf default-information originate metric 10", + "set vrf name vrf-green protocols ospf default-information originate metric-type 2", + "set vrf name vrf-green protocols ospf auto-cost reference-bandwidth '2'", + "set vrf name vrf-green protocols ospf neighbor 192.0.11.12", + "set vrf name vrf-green protocols ospf neighbor 192.0.11.12 poll-interval 10", + "set vrf name vrf-green protocols ospf neighbor 192.0.11.12 priority 2", + "set vrf name vrf-green protocols ospf redistribute bgp", + "set vrf name vrf-green protocols ospf redistribute bgp metric 10", + "set vrf name vrf-green protocols ospf redistribute bgp metric-type 2", + "set vrf name vrf-green protocols ospf parameters router-id '192.0.1.1'", + "set vrf name vrf-green protocols ospf parameters rfc1583-compatibility", + "set vrf name vrf-green protocols ospf parameters abr-type 'cisco'", + "set vrf name vrf-green protocols ospf area '2'", + "set vrf name vrf-green protocols ospf area 2 area-type normal", + "set vrf name vrf-green protocols ospf area 2 authentication plaintext-password", + "set vrf name vrf-green protocols ospf area 2 shortcut enable", + "set vrf name vrf-green protocols ospf area '3'", + "set vrf name vrf-green protocols ospf area 3 area-type nssa", + "set vrf name vrf-green protocols ospf area '4'", + "set vrf name vrf-green protocols ospf area 4 area-type stub default-cost 20", + "set vrf name vrf-green protocols ospf area 4 network 192.0.2.0/24", + "set vrf name vrf-green protocols ospf area 4 range 192.0.3.0/24", + "set vrf name vrf-green protocols ospf area 4 range 192.0.3.0/24 cost 10", + "set vrf name vrf-green protocols ospf area 4 range 192.0.4.0/24", + "set vrf name vrf-green protocols ospf area 4 range 192.0.4.0/24 cost 12", + ] + + self.execute_module(changed=True, commands=commands) + + def test_vrf_replaced(self): + set_module_args( + dict( + config=dict( + bind_to_all=True, + instances=[ + dict( + name="vrf-blue", + description="blue-vrf-replaced", + disable=True, + table_id=100, + vni=1000, + ), + dict( + name="vrf-red", + description="red-vrf", + disable=True, + table_id=101, + vni=1001, + address_family=[ + dict( + afi="ipv4", + disable_forwarding=False, + route_maps=[ + dict(rm_name="rm1", protocol="rip"), + ], + ), + dict( + afi="ipv6", + nht_no_resolve_via_default=False, + ), + ], + ), + ], + ), + state="replaced", + ), + ) + commands = [ + "set vrf name vrf-blue description blue-vrf-replaced", + "set vrf name vrf-blue disable", + "delete vrf name vrf-red ip disable-forwarding", + "delete vrf name vrf-red ipv6 nht no-resolve-via-default", + ] + self.execute_module(changed=True, commands=commands) + + def test_vrf_replaced_idempotent(self): + set_module_args( + dict( + config=dict( + bind_to_all=True, + instances=[ + dict( + name="vrf-blue", + description="blue-vrf", + disable=False, + table_id=100, + vni=1000, + ), + dict( + name="vrf-red", + description="red-vrf", + disable=True, + table_id=101, + vni=1001, + address_family=[ + dict( + afi="ipv4", + disable_forwarding=True, + route_maps=[ + dict(rm_name="rm1", protocol="rip"), + ], + ), + dict( + afi="ipv6", + nht_no_resolve_via_default=True, + ), + ], + ), + ], + ), + state="replaced", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_vrf_overridden(self): + set_module_args( + dict( + config=dict( + instances=[ + dict( + name="vrf-blue", + description="blue-vrf", + disable=True, + table_id=103, + vni=1002, + ), + dict( + name="vrf-red", + description="red-vrf", + disable=True, + table_id=101, + vni=1001, + protocols=dict( + bgp=dict( + as_number=65000, + neighbor=[ + dict( + address="192.0.2.1", + remote_as=65003, + ), + ], + ), + ), + ), + ], + ), + state="overridden", + ), + ) + commands = [ + "delete vrf name vrf-blue", + "commit", + "delete vrf name vrf-red", + "commit", + "set vrf name vrf-blue table 103", + "set vrf name vrf-blue vni 1002", + "set vrf name vrf-blue description blue-vrf", + "set vrf name vrf-blue disable", + "set vrf name vrf-red table 101", + "set vrf name vrf-red vni 1001", + "set vrf name vrf-red description red-vrf", + "set vrf name vrf-red disable", + "set vrf name vrf-red protocols bgp system-as 65000", + "set vrf name vrf-red protocols bgp neighbor 192.0.2.1 remote-as 65003", + "delete vrf bind-to-all", + ] + self.execute_module(changed=True, commands=commands) + + def test_vrf_overridden_idempotent(self): + set_module_args( + dict( + config=dict( + bind_to_all=True, + instances=[ + dict( + name="vrf-blue", + description="blue-vrf", + disable=False, + table_id=100, + vni=1000, + ), + dict( + name="vrf-red", + description="red-vrf", + disable=True, + table_id=101, + vni=1001, + address_family=[ + dict( + afi="ipv4", + disable_forwarding=True, + nht_no_resolve_via_default=False, + route_maps=[ + dict(rm_name="rm1", protocol="rip"), + ], + ), + dict( + afi="ipv6", + disable_forwarding=False, + nht_no_resolve_via_default=True, + ), + ], + protocols=dict( + bgp=dict( + as_number=65000, + neighbor=[ + dict( + address="192.0.2.1", + remote_as=65002, + ), + dict( + address="1.1.1.3", + remote_as=400, + passive=True, + ), + ], + ), + ), + ), + ], + ), + state="overridden", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_vrf_rendered(self): + set_module_args( + dict( + config=dict( + bind_to_all=True, + instances=[ + dict( + name="vrf-green", + description="green-vrf", + disabled=True, + table_id=105, + vni=1000, + ), + dict( + name="vrf-amber", + description="amber-vrf", + disable=False, + table_id=111, + vni=1001, + address_family=[ + dict( + afi="ipv4", + disable_forwarding=True, + route_maps=[ + dict(rm_name="rm1", protocol="ospf"), + ], + ), + dict( + afi="ipv6", + nht_no_resolve_via_default=False, + ), + ], + ), + ], + ), + state="rendered", + ), + ) + rendered_commands = [ + "set vrf bind-to-all", + "set vrf name vrf-green table 105", + "set vrf name vrf-green vni 1000", + "set vrf name vrf-green description green-vrf", + "set vrf name vrf-green disable", + "set vrf name vrf-amber table 111", + "set vrf name vrf-amber vni 1001", + "set vrf name vrf-amber description amber-vrf", + "set vrf name vrf-amber ip protocol ospf route-map rm1", + "set vrf name vrf-amber ip disable-forwarding", + ] + result = self.execute_module(changed=False) + self.assertEqual( + sorted(result["rendered"]), + sorted(rendered_commands), + result["rendered"], + ) + + def test_vrf_parsed(self): + commands = ( + "set vrf bind-to-all", + "set vrf name vrf1 description 'red'", + "set vrf name vrf1 disable", + "set vrf name vrf1 table 101", + "set vrf name vrf1 vni 501", + "set vrf name vrf2 description 'green'", + "set vrf name vrf2 disable", + "set vrf name vrf2 table 102", + "set vrf name vrf2 vni 102", + "set vrf name vrf1 ip disable-forwarding", + "set vrf name vrf1 ip nht no-resolve-via-default", + "set vrf name vrf-red ip protocol ospf route-map 'rm1'", + "set vrf name vrf-red ipv6 nht no-resolve-via-default", + ) + parsed_str = "\n".join(commands) + set_module_args(dict(running_config=parsed_str, state="parsed")) + result = self.execute_module(changed=False) + parsed_list = { + "bind_to_all": True, + "instances": [ + { + "address_family": [ + { + "afi": "ipv4", + "disable_forwarding": True, + "nht_no_resolve_via_default": True, + }, + ], + "description": "red", + "disable": True, + "name": "vrf1", + }, + { + "description": "green", + "disable": True, + "name": "vrf2", + }, + { + "address_family": [ + { + "afi": "ipv4", + "disable_forwarding": False, + "nht_no_resolve_via_default": False, + "route_maps": [ + { + "protocol": "ospf", + "rm_name": "rm1", + }, + ], + }, + { + "afi": "ipv6", + "disable_forwarding": False, + "nht_no_resolve_via_default": True, + }, + ], + "disable": False, + "name": "vrf-red", + }, + ], + } + self.assertEqual(parsed_list, result["parsed"]) + + def test_vrf_gathered(self): + set_module_args(dict(state="gathered")) + result = self.execute_module(changed=False) + gathered_list = { + "bind_to_all": True, + "instances": [ + { + "description": "blue-vrf", + "disable": False, + "name": "vrf-blue", + "table_id": 100, + "vni": 1000, + }, + { + "address_family": [ + { + "afi": "ipv4", + "disable_forwarding": True, + "nht_no_resolve_via_default": False, + "route_maps": [ + {"protocol": "rip", "rm_name": "rm1"}, + ], + }, + { + "afi": "ipv6", + "disable_forwarding": False, + "nht_no_resolve_via_default": True, + }, + ], + "description": "red-vrf", + "disable": True, + "name": "vrf-red", + "protocols": { + "bgp": { + "as_number": 65000, + "neighbor": [ + {"address": "1.1.1.3", "passive": True, "remote_as": 400}, + {"address": "192.0.2.1", "remote_as": 65002}, + ], + }, + }, + "table_id": 101, + "vni": 1001, + }, + ], + } + self.assertEqual(gathered_list, result["gathered"]) + + def test_vrf_deleted(self): + set_module_args( + dict( + config=dict( + bind_to_all=True, + instances=[ + dict( + name="vrf-blue", + ), + ], + ), + state="deleted", + ), + ) + commands = [ + "delete vrf bind-to-all", + "delete vrf name vrf-blue", + ] + self.execute_module(changed=True, commands=commands) + + def test_vrf_all_deleted(self): + set_module_args( + dict( + state="deleted", + ), + ) + commands = [ + "delete vrf", + ] + self.execute_module(changed=True, commands=commands) |
