summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoromnom62 <75066712+omnom62@users.noreply.github.com>2025-05-16 07:55:33 +1000
committerGitHub <noreply@github.com>2025-05-16 07:55:33 +1000
commitcd2f41d34ff22ea7719348ec18a30f1c499b72c8 (patch)
tree0d1adfa900eea95c71f0058c8d929a528ed1850a
parent098f31c6fe7a6310594656efc0af5a2739ede82b (diff)
downloadvyos.vyos-cd2f41d34ff22ea7719348ec18a30f1c499b72c8.tar.gz
vyos.vyos-cd2f41d34ff22ea7719348ec18a30f1c499b72c8.zip
T6883 route-map extras support (#402)
* t6883 as_prepend_path fix * chaneglog * draft for rm community handling * rm updates * unit and integration tests for route maps * rm unit tests fixed * 1.4+ rm integration tests draft * rm set_community parser * large-community 1.4+ * unit tests fix for rm * extcommunity config * pre-commit * cleanup * RM bandwidth clauses * fixing parsers index for 1.3 RM * updated dic * fix unit tests * unit tests for RM * RM unit tests * dict literal for reserved keywords in unit tests * set_src ipv6 replaced unit test * unit test case for RM call * RM action type unit tests * rm match protocol code and unit test * doc update * set_table RM * set_table RM unit test * unit tests refactor * copilot recommendations
-rw-r--r--changelogs/fragments/T6821_route_map_options.yml3
-rw-r--r--docs/vyos.vyos.vyos_route_maps_module.rst90
-rw-r--r--plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py20
-rw-r--r--plugins/module_utils/network/vyos/config/route_maps/route_maps.py29
-rw-r--r--plugins/module_utils/network/vyos/facts/route_maps/route_maps.py16
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/route_maps.py130
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/route_maps_14.py1405
-rw-r--r--plugins/modules/vyos_route_maps.py13
-rw-r--r--tests/integration/targets/vyos_route_maps/tests/cli/replaced.yaml1
-rw-r--r--tests/unit/modules/network/vyos/fixtures/vyos_route_maps_config_v14.cfg18
-rw-r--r--tests/unit/modules/network/vyos/test_vyos_route_maps.py135
-rw-r--r--tests/unit/modules/network/vyos/test_vyos_route_maps14.py717
12 files changed, 2564 insertions, 13 deletions
diff --git a/changelogs/fragments/T6821_route_map_options.yml b/changelogs/fragments/T6821_route_map_options.yml
new file mode 100644
index 00000000..6d1e974b
--- /dev/null
+++ b/changelogs/fragments/T6821_route_map_options.yml
@@ -0,0 +1,3 @@
+---
+minor_changes:
+ - vyos_route_maps - add support for as-path-prepend policy option
diff --git a/docs/vyos.vyos.vyos_route_maps_module.rst b/docs/vyos.vyos.vyos_route_maps_module.rst
index 4e0c5322..f6a74d46 100644
--- a/docs/vyos.vyos.vyos_route_maps_module.rst
+++ b/docs/vyos.vyos.vyos_route_maps_module.rst
@@ -667,6 +667,38 @@ Parameters
<td class="elbow-placeholder"></td>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>protocol</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>babel</li>
+ <li>bgp</li>
+ <li>connected</li>
+ <li>isis</li>
+ <li>kernel</li>
+ <li>ospf</li>
+ <li>ospfv3</li>
+ <li>rip</li>
+ <li>ripng</li>
+ <li>static</li>
+ <li>table</li>
+ <li>vnc</li>
+ </ul>
+ </td>
+ <td>
+ <div>Source protocol to match.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
<b>rpki</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
@@ -1015,6 +1047,46 @@ Parameters
<td class="elbow-placeholder"></td>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>extcommunity_bandwidth</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Set Bandwidth of Origin value. 1-25600|cumulative|num-multipaths VPN extended community</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>extcommunity_bandwidth_non_transitive</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Set the bandwidth extended community encoded as non-transitive True/False VPN extended community</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
<b>extcommunity_rt</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
@@ -1265,6 +1337,24 @@ Parameters
<td class="elbow-placeholder"></td>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>table</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Set prefixes to table. Example &lt;1-200&gt;</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
<b>tag</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
diff --git a/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py b/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py
index 196db0c7..5b6d404d 100644
--- a/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py
+++ b/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py
@@ -74,6 +74,8 @@ class Route_mapsArgs(object): # pylint: disable=R0903
},
"extcommunity_rt": {"type": "str"},
"extcommunity_soo": {"type": "str"},
+ "extcommunity_bandwidth": {"type": "str"},
+ "extcommunity_bandwidth_non_transitive": {"type": "bool"},
"ip_next_hop": {"type": "str"},
"ipv6_next_hop": {
"type": "dict",
@@ -100,6 +102,7 @@ class Route_mapsArgs(object): # pylint: disable=R0903
"src": {"type": "str"},
"tag": {"type": "str"},
"weight": {"type": "str"},
+ "table": {"type": "str"},
},
},
"match": {
@@ -178,6 +181,23 @@ class Route_mapsArgs(object): # pylint: disable=R0903
"next_hop": {"type": "str"},
},
},
+ "protocol": {
+ "type": "str",
+ "choices": [
+ "babel",
+ "bgp",
+ "connected",
+ "isis",
+ "kernel",
+ "ospf",
+ "ospfv3",
+ "rip",
+ "ripng",
+ "static",
+ "table",
+ "vnc",
+ ],
+ },
"large_community_large_community_list": {
"type": "str",
},
diff --git a/plugins/module_utils/network/vyos/config/route_maps/route_maps.py b/plugins/module_utils/network/vyos/config/route_maps/route_maps.py
index 9b6c3e9d..9692a253 100644
--- a/plugins/module_utils/network/vyos/config/route_maps/route_maps.py
+++ b/plugins/module_utils/network/vyos/config/route_maps/route_maps.py
@@ -31,6 +31,13 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps import (
Route_mapsTemplate,
)
+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.utils.version import (
+ LooseVersion,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version
class Route_maps(ResourceModule):
@@ -59,6 +66,8 @@ class Route_maps(ResourceModule):
"set_bgp_extcommunity_rt",
"set_extcommunity_rt",
"set_extcommunity_soo",
+ "set_extcommunity_bandwidth",
+ "set_extcommunity_bandwidth_non_transitive",
"set_ip_next_hop",
"set_ipv6_next_hop",
"set_large_community",
@@ -70,6 +79,7 @@ class Route_maps(ResourceModule):
"set_src",
"set_tag",
"set_weight",
+ "set_table",
"set_comm_list",
"set_comm_list_delete",
"set_community",
@@ -89,15 +99,34 @@ class Route_maps(ResourceModule):
"on_match_next",
"match_ipv6_address",
"match_ipv6_nexthop",
+ "match_protocol",
"match_rpki",
]
+ def _validate_template(self):
+ version = get_os_version(self._module)
+ if LooseVersion(version) >= LooseVersion("1.4"):
+ self._tmplt = Route_mapsTemplate14()
+ else:
+ self._tmplt = Route_mapsTemplate()
+
+ def parse(self):
+ """override parse to check template"""
+ self._validate_template()
+ return super().parse()
+
+ def get_parser(self, name):
+ """get_parsers"""
+ self._validate_template()
+ return super().get_parser(name)
+
def execute_module(self):
"""Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
+ self._validate_template()
if self.state not in ["parsed", "gathered"]:
self.generate_commands()
self.run_commands()
diff --git a/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py b/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py
index 2ad54e63..cfae26e3 100644
--- a/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py
+++ b/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py
@@ -25,6 +25,13 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.rou
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps import (
Route_mapsTemplate,
)
+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.utils.version import (
+ LooseVersion,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version
class Route_mapsFacts(object):
@@ -59,11 +66,18 @@ class Route_mapsFacts(object):
"""
facts = {}
objs = []
+
+ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"):
+ route_maps_class = Route_mapsTemplate14
+ else:
+ route_maps_class = Route_mapsTemplate
+
if not data:
data = self.get_config(connection)
# parse native config using the Route_maps template
- route_maps_parser = Route_mapsTemplate(lines=data.splitlines())
+ route_maps_parser = route_maps_class(lines=data.splitlines())
+
if route_maps_parser.parse().get("route_maps"):
objs = list(route_maps_parser.parse().get("route_maps").values())
for item in objs:
diff --git a/plugins/module_utils/network/vyos/rm_templates/route_maps.py b/plugins/module_utils/network/vyos/rm_templates/route_maps.py
index 8f218a6b..c6b88f7b 100644
--- a/plugins/module_utils/network/vyos/rm_templates/route_maps.py
+++ b/plugins/module_utils/network/vyos/rm_templates/route_maps.py
@@ -310,12 +310,12 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_as_path_prepend",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path-prepend\s(?P<as>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path-prepend\s(?P<as>.*)
+ $""",
re.VERBOSE,
),
"compval": "set.as_path_prepend",
- "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path-prepend {{set.as_path_prepend}}",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path-prepend '{{set.as_path_prepend}}'",
"result": {
"route_maps": {
"{{ route_map }}": {
@@ -337,10 +337,11 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_atomic_aggregate",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\satomic-aggregate(?P<as>)
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\s(?P<as>atomic-aggregate)
*$""",
re.VERBOSE,
),
+ "compval": "set.atomic_aggregate",
"setval": "policy route-map {{route_map}} rule {{sequence}} set atomic-aggregate",
"result": {
"route_maps": {
@@ -391,13 +392,13 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_comm_list",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scomm-list\scomm-list\s(?P<comm_list>\S+)
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<comm_list>\S+)
*$""",
re.VERBOSE,
),
- "compval": "set.comm_list.comm_list",
+ "compval": "match.community.community_list",
"setval": "policy route-map {{route_map}} rule {{sequence}} "
- "set comm-list comm-list {{set.comm_list.comm_list}}",
+ "match community community-list {{set.comm_list.comm_list}}",
"result": {
"route_maps": {
"{{ route_map }}": {
@@ -406,8 +407,8 @@ class Route_mapsTemplate(NetworkTemplate):
"{{sequence}}":
{
"sequence": "{{sequence}}",
- "set": {
- "comm_list": {"comm_list": "{{comm_list}}"},
+ "match": {
+ "community": {"community_list": "{{comm_list}}"},
},
},
},
@@ -500,6 +501,62 @@ class Route_mapsTemplate(NetworkTemplate):
},
},
{
+ "name": "set_extcommunity_bandwidth",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\sbandwidth\s(?P<extcommunity_bw>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.extcommunity_bandwidth",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set extcommunity bandwidth {{set.extcommunity_bandwidth}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "extcommunity_bandwidth": "{{extcommunity_bw}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_extcommunity_bandwidth_non_transitive",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\s(?P<extcommunity_bw_nt>bandwidth-non-transitive)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.extcommunity_bandwidth_non_transitive",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set extcommunity bandwidth-non-transitive",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "extcommunity_bandwidth_non_transitive": "{{True if extcommunity_bw_nt is defined}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
"name": "set_ip_next_hop",
"getval": re.compile(
r"""
@@ -813,6 +870,34 @@ class Route_mapsTemplate(NetworkTemplate):
},
},
{
+ "name": "set_table",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stable\s(?P<table>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.weight",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set table {{set.table}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "table": "{{table}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
"name": "set_community",
"getval": re.compile(
r"""
@@ -1261,6 +1346,33 @@ class Route_mapsTemplate(NetworkTemplate):
},
},
{
+ "name": "match_protocol",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sprotocol\s(?P<value>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.protocol",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match protocol {{match.protocol}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}": {
+ "sequence": "{{sequence}}",
+ "match": {
+ "protocol": "{{value}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
"name": "match_rpki",
"getval": re.compile(
r"""
diff --git a/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py b/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py
new file mode 100644
index 00000000..6564280d
--- /dev/null
+++ b/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py
@@ -0,0 +1,1405 @@
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+"""
+The Route_maps parser templates file. This contains
+a list of parser definitions and associated functions that
+facilitates both facts gathering and native command generation for
+the given network resource.
+"""
+
+import re
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import (
+ NetworkTemplate,
+)
+
+
+class Route_mapsTemplate14(NetworkTemplate):
+ def __init__(self, lines=None):
+ prefix = {"set": "set", "remove": "delete"}
+ super(Route_mapsTemplate14, self).__init__(lines=lines, tmplt=self, prefix=prefix)
+
+ # fmt: off
+ PARSERS = [
+ {
+ "name": "route_map",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "route_map",
+ "setval": "policy route-map {{route_map}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ },
+ },
+ },
+ },
+ {
+ "name": "sequence",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "sequence",
+ "setval": "policy route-map {{route_map}} rule {{sequence}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "call",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scall\s(?P<call>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": "policy route-map {{route_map}} rule {{sequence}} call {{call}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "call": "{{call}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "description",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sdescription\s(?P<description>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": "policy route-map {{route_map}} rule {{sequence}} description {{description}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "description": "{{description}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "action",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\saction\s(?P<action>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": "policy route-map {{route_map}} rule {{sequence}} action {{action}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "action": "{{action}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "continue_sequence",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scontinue\s(?P<continue>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": "policy route-map {{route_map}} rule {{sequence}} continue {{continue_sequence}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "continue_sequence": "{{continue}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "on_match_next",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\s(?P<next>next)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "on_match.next",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} on-match next",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "on_match": {
+ "next": "{{True if next is defined}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "on_match_goto",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\sgoto\s(?P<goto>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "on_match.goto",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} on-match goto {{on_match.goto}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "on_match": {
+ "goto": "{{goto}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_aggregator_ip",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sip\s(?P<ip>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.aggregator.ip",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} set aggregator ip {{set.aggregator.ip}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "aggregator": {
+ "ip": "{{ip}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_aggregator_as",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sas\s(?P<as>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.aggregator.as",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} set aggregator as {{set.aggregator.as}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "aggregator": {
+ "as": "{{as}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_as_path_exclude",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path\sexclude\s(?P<as>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.as_path_exclude",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path exclude {{set.as_path_exclude}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "as_path_exclude": "{{as}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_as_path_prepend",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path\sprepend\s(?P<as>.*)
+ $""",
+ re.VERBOSE,
+ ),
+ "compval": "set.as_path_prepend",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path prepend '{{set.as_path_prepend}}'",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "as_path_prepend": "{{as}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_atomic_aggregate",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\s(?P<as>atomic-aggregate)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.atomic_aggregate",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} set atomic-aggregate",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "atomic_aggregate": "{{True if as is defined}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_bgp_extcommunity_rt",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sbgp-extcommunity-rt\s(?P<bgp>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.bgp_extcommunity_rt",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set bgp-extcommunity-rt {{set.bgp_extcommunity_rt}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "bgp_extcommunity_rt": "{{bgp}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_comm_list",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<comm_list>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.community.community_list",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match community community-list {{set.comm_list.comm_list}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "match": {
+ "community": {"community_list": "{{comm_list}}"},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_comm_list_delete",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scomm-list\sdelete(?P<delete>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.comm_list.comm_list",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set comm-list delete",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "comm_list": {"delete": "{{True if delete is defined}}"},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_extcommunity_rt",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\srt\s(?P<extcommunity_rt>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.extcommunity_rt",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set extcommunity rt {{set.extcommunity_rt}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "extcommunity_rt": "{{extcommunity_rt}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_extcommunity_soo",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\ssoo\s(?P<extcommunity_soo>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.extcommunity_soo",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set extcommunity soo {{set.extcommunity_soo}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "extcommunity_soo": "{{extcommunity_soo}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_extcommunity_bandwidth",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\sbandwidth\s(?P<extcommunity_bw>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.extcommunity_bandwidth",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set extcommunity bandwidth {{set.extcommunity_bandwidth}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "extcommunity_bandwidth": "{{extcommunity_bw}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_extcommunity_bandwidth_non_transitive",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\s(?P<extcommunity_bw_nt>bandwidth-non-transitive)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.extcommunity_bandwidth_non_transitive",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set extcommunity bandwidth-non-transitive",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "extcommunity_bandwidth_non_transitive": "{{True if extcommunity_bw_nt is defined}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_ip_next_hop",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sip-next-hop\s(?P<ip_next_hop>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.ip_next_hop",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set ip-next-hop {{set.ip_next_hop}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "ip_next_hop": "{{ip_next_hop}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_ipv6_next_hop",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sipv6-next-hop
+ \s(?P<type>global|local)
+ \s(?P<value>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.ipv6_next_hop",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set ipv6-next-hop {{set.ipv6_next_hop.ip_type}} {{set.ipv6_next_hop.value}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "ipv6_next_hop": {
+ "ip_type": "{{type}}",
+ "value": "{{value}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_large_community",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slarge-community\s(?P<op>none|replace\s(?P<large_community>\S+))
+ $""",
+ re.VERBOSE,
+ ),
+ "compval": "set.large_community",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set large-community {{set.large_community if set.large_community == 'none' else 'replace ' + set.large_community}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "large_community": "{{op if op == 'none' else large_community}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_local_preference",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slocal-preference\s(?P<local_preference>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.local_preference",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set local-preference {{set.local_preference}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "local_preference": "{{local_preference}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_metric",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric\s(?P<metric>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.metric",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set metric {{set.metric}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "metric": "{{metric}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_metric_type",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric-type\s(?P<metric_type>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.metric_type",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set metric-type {{set.metric_type}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "metric_type": "{{metric_type}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_origin",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sorigin\s(?P<origin>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.origin",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set origin {{set.origin}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "origin": "{{origin}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_originator_id",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\soriginator-id\s(?P<originator_id>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.originator_id",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set originator-id {{set.originator_id}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "originator_id": "{{originator_id}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_src",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\ssrc\s(?P<src>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.src",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set src {{set.src}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "src": "{{src}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_tag",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stag\s(?P<tag>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.tag",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set tag {{set.tag}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "tag": "{{tag}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_weight",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sweight\s(?P<weight>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.weight",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set weight {{set.weight}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "weight": "{{weight}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_table",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stable\s(?P<table>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "set.weight",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set table {{set.table}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "table": "{{table}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "set_community",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scommunity\s(?P<op>none|replace\s(?P<value>\S+))
+ $""",
+ re.VERBOSE,
+ ),
+ "compval": "set.community.value",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "set community {{set.community.value if set.community.value == 'none' else 'replace ' + set.community.value}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "set": {
+ "community": {
+ "value": "{{op if op == 'none' else value}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_as_path",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sas-path\s(?P<as_path>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.as_path",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match as-path {{match.as_path}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "match": {
+ "as_path": "{{as_path}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_community_community_list",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<community_list>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.community.community_list",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match community community-list {{match.community.community_list}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "match": {
+ "community": {"community_list": "{{community_list}}"},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_community_exact_match",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\sexact-match(?P<exact_match>)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.community.exact_match",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match community exact-match",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "match": {
+ "community": {"exact_match": "{{True if exact_match is defined}}"},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_extcommunity",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sextcommunity\s(?P<extcommunity>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.extcommunity",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match extcommunity {{match.extcommunity}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "match": {
+ "extcommunity": "{{extcommunity}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_interface",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sinterface\s(?P<interface>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.interface",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match interface {{match.interface}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "match": {
+ "interface": "{{interface}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_large_community_large_community_list",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\slarge-community\slarge-community-list\s(?P<lc>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.large_community_large_community_list",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match large-community large-community-list {{match.large_community_large_community_list}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "match": {
+ "large_community_large_community_list": "{{lc}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_metric",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\smetric\s(?P<metric>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.metric",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match metric {{match.metric}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "match": {
+ "metric": "{{metric}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_origin",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sorigin\s(?P<origin>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.origin",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match origin {{match.origin}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "match": {
+ "origin": "{{origin}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_peer",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\speer\s(?P<peer>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.peer",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match peer {{match.peer}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}":
+ {
+ "sequence": "{{sequence}}",
+ "match": {
+ "peer": "{{peer}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_ip_address",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\saddress
+ \s(?P<list_type>access-list|prefix-list)
+ \s(?P<value>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.ip.address",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match ip address {{match.ip.address.list_type}} {{match.ip.address.value}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}": {
+ "sequence": "{{sequence}}",
+ "match": {
+ "ip": {
+ "address": {
+ "list_type": "{{list_type}}",
+ "value": "{{value}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_ip_next_hop",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\snexthop
+ \s(?P<list_type>access-list|prefix-list)
+ \s(?P<value>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.ip.next_hop",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match ip nexthop {{match.ip.next_hop.list_type}} {{match.ip.next_hop.value}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}": {
+ "sequence": "{{sequence}}",
+ "match": {
+ "ip": {
+ "next_hop": {
+ "list_type": "{{list_type}}",
+ "value": "{{value}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_ip_route_source",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\sroute-source
+ \s(?P<list_type>access-list|prefix-list)
+ \s(?P<value>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.ip.route_source",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match ip route-source {{match.ip.route_source.list_type}} {{match.ip.route_source.value}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}": {
+ "sequence": "{{sequence}}",
+ "match": {
+ "ip": {
+ "route_source": {
+ "list_type": "{{list_type}}",
+ "value": "{{value}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_ipv6_address",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sipv6\saddress
+ \s(?P<list_type>access-list|prefix-list)
+ \s(?P<value>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.ipv6.address",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match ipv6 address {{match.ipv6.address.list_type}} {{match.ipv6.address.value}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}": {
+ "sequence": "{{sequence}}",
+ "match": {
+ "ipv6": {
+ "address": {
+ "list_type": "{{list_type}}",
+ "value": "{{value}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_ipv6_nexthop",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sipv6\snexthop
+ \s(?P<value>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.ipv6.next_hop",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match ipv6 nexthop {{match.ipv6.next_hop}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}": {
+ "sequence": "{{sequence}}",
+ "match": {
+ "ipv6": {
+ "next_hop": "{{value}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_protocol",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sprotocol\s(?P<value>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.protocol",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match protocol {{match.protocol}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}": {
+ "sequence": "{{sequence}}",
+ "match": {
+ "protocol": "{{value}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "match_rpki",
+ "getval": re.compile(
+ r"""
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\srpki
+ \s(?P<value>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "compval": "match.rpki",
+ "setval": "policy route-map {{route_map}} rule {{sequence}} "
+ "match rpki {{match.rpki}}",
+ "result": {
+ "route_maps": {
+ "{{ route_map }}": {
+ "route_map": '{{ route_map }}',
+ "entries": {
+ "{{sequence}}": {
+ "sequence": "{{sequence}}",
+ "match": {
+ "rpki": "{{value}}",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+
+ ]
+ # fmt: on
diff --git a/plugins/modules/vyos_route_maps.py b/plugins/modules/vyos_route_maps.py
index 67d327a6..455bd3e6 100644
--- a/plugins/modules/vyos_route_maps.py
+++ b/plugins/modules/vyos_route_maps.py
@@ -103,6 +103,12 @@ options:
extcommunity_soo:
type: str
description: Set Site of Origin value. ASN:nn_or_IP_address:nn VPN extended community
+ extcommunity_bandwidth:
+ type: str
+ description: Set Bandwidth of Origin value. 1-25600|cumulative|num-multipaths VPN extended community
+ extcommunity_bandwidth_non_transitive:
+ type: bool
+ description: Set the bandwidth extended community encoded as non-transitive True/False VPN extended community
ip_next_hop:
type: str
description: IP address.
@@ -146,6 +152,9 @@ options:
weight:
type: str
description: Border Gateway Protocol (BGP) weight attribute. Example <0-4294967295>
+ table:
+ type: str
+ description: Set prefixes to table. Example <1-200>
match:
description: Route parameters to match.
type: dict
@@ -226,6 +235,10 @@ options:
type: str
description: RPKI validation value.
choices: [ "notfound", "invalid", "valid" ]
+ protocol:
+ type: str
+ description: Source protocol to match.
+ choices: [ "babel","bgp","connected","isis","kernel","ospf","ospfv3","rip","ripng","static","table","vnc" ]
on_match:
type: dict
description: Exit policy on matches.
diff --git a/tests/integration/targets/vyos_route_maps/tests/cli/replaced.yaml b/tests/integration/targets/vyos_route_maps/tests/cli/replaced.yaml
index d7d05ec1..dce0cba9 100644
--- a/tests/integration/targets/vyos_route_maps/tests/cli/replaced.yaml
+++ b/tests/integration/targets/vyos_route_maps/tests/cli/replaced.yaml
@@ -31,7 +31,6 @@
- assert:
that:
- - result.commands|length == 7
- result.changed == true
- result.commands|symmetric_difference(replaced.commands) == []
- result.after|symmetric_difference(ansible_facts['network_resources']['route_maps']) == []
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/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)