diff options
author | Ashwini Mhatre <amhatre@redhat.com> | 2021-05-12 00:04:47 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-11 18:34:47 +0000 |
commit | 407eec39119320875ffcdede81155a9a5ed201d7 (patch) | |
tree | 49dc6845763c9fcc082129089c1eebc5cf36968c | |
parent | 6de551224ff6ada0eb7a0a45938d535302a38d7c (diff) | |
download | vyos-ansible-old-407eec39119320875ffcdede81155a9a5ed201d7.tar.gz vyos-ansible-old-407eec39119320875ffcdede81155a9a5ed201d7.zip |
Vyos route maps (#156)
Vyos route maps
Reviewed-by: https://github.com/apps/ansible-zuul
32 files changed, 3956 insertions, 0 deletions
diff --git a/changelogs/fragments/vyos-route-maps.yaml b/changelogs/fragments/vyos-route-maps.yaml new file mode 100644 index 0000000..6f4e07f --- /dev/null +++ b/changelogs/fragments/vyos-route-maps.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Add vyos_route_maps resource module (https://github.com/ansible-collections/vyos.vyos/pull/156.). diff --git a/plugins/module_utils/network/vyos/argspec/route_maps/__init__.py b/plugins/module_utils/network/vyos/argspec/route_maps/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/route_maps/__init__.py 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 new file mode 100644 index 0000000..790f5e6 --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py @@ -0,0 +1,230 @@ +# -*- 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 + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the vyos_route_maps module +""" + + +class Route_mapsArgs(object): # pylint: disable=R0903 + """The arg spec for the vyos_route_maps module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "route_map": {"type": "str"}, + "entries": { + "aliases": ["rules"], + "type": "list", + "elements": "dict", + "options": { + "sequence": {"type": "int"}, + "call": {"type": "str"}, + "description": {"type": "str"}, + "action": { + "type": "str", + "choices": ["deny", "permit"], + }, + "continue_sequence": {"type": "int"}, + "set": { + "type": "dict", + "options": { + "aggregator": { + "type": "dict", + "options": { + "ip": {"type": "str"}, + "as": {"type": "str"}, + }, + }, + "as_path_exclude": {"type": "str"}, + "as_path_prepend": {"type": "str"}, + "atomic_aggregate": {"type": "bool"}, + "bgp_extcommunity_rt": {"type": "str"}, + "comm_list": { + "type": "dict", + "options": { + "comm_list": {"type": "str"}, + "delete": {"type": "bool"}, + }, + }, + "community": { + "type": "dict", + "options": {"value": {"type": "str"}}, + }, + "extcommunity_rt": {"type": "str"}, + "extcommunity_soo": {"type": "str"}, + "ip_next_hop": {"type": "str"}, + "ipv6_next_hop": { + "type": "dict", + "options": { + "ip_type": { + "type": "str", + "choices": ["global", "local"], + }, + "value": {"type": "str"}, + }, + }, + "large_community": {"type": "str"}, + "local_preference": {"type": "str"}, + "metric": {"type": "str"}, + "metric_type": { + "type": "str", + "choices": ["type-1", "type-2"], + }, + "origin": { + "type": "str", + "choices": ["egp", "igp", "incomplete"], + }, + "originator_id": {"type": "str"}, + "src": {"type": "str"}, + "tag": {"type": "str"}, + "weight": {"type": "str"}, + }, + }, + "match": { + "type": "dict", + "options": { + "as_path": {"type": "str"}, + "community": { + "type": "dict", + "options": { + "community_list": {"type": "str"}, + "exact_match": {"type": "bool"}, + }, + }, + "extcommunity": {"type": "str"}, + "interface": {"type": "str"}, + "ip": { + "type": "dict", + "options": { + "address": { + "type": "dict", + "options": { + "list_type": { + "type": "str", + "choices": [ + "access-list", + "prefix-list", + ], + }, + "value": {"type": "str"}, + }, + }, + "next_hop": { + "type": "dict", + "options": { + "list_type": { + "type": "str", + "choices": [ + "access-list", + "prefix-list", + ], + }, + "value": {"type": "str"}, + }, + }, + "route_source": { + "type": "dict", + "options": { + "list_type": { + "type": "str", + "choices": [ + "access-list", + "prefix-list", + ], + }, + "value": {"type": "str"}, + }, + }, + }, + }, + "ipv6": { + "type": "dict", + "options": { + "address": { + "type": "dict", + "options": { + "list_type": { + "type": "str", + "choices": [ + "access-list", + "prefix-list", + ], + }, + "value": {"type": "str"}, + }, + }, + "next_hop": {"type": "str"}, + }, + }, + "large_community_large_community_list": { + "type": "str" + }, + "metric": {"type": "int"}, + "origin": { + "type": "str", + "choices": ["ebgp", "ibgp", "incomplete"], + }, + "peer": {"type": "str"}, + "rpki": { + "type": "str", + "choices": [ + "notfound", + "invalid", + "valid", + ], + }, + }, + }, + "on_match": { + "type": "dict", + "options": { + "next": {"type": "bool"}, + "goto": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "type": "str", + "choices": [ + "deleted", + "merged", + "overridden", + "replaced", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + }, + } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/route_maps/__init__.py b/plugins/module_utils/network/vyos/config/route_maps/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/config/route_maps/__init__.py 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 new file mode 100644 index 0000000..5e4bbea --- /dev/null +++ b/plugins/module_utils/network/vyos/config/route_maps/route_maps.py @@ -0,0 +1,160 @@ +# +# -*- 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 vyos_route_maps config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import ( + ResourceModule, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( + Facts, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps import ( + Route_mapsTemplate, +) + + +class Route_maps(ResourceModule): + """ + The vyos_route_maps config class + """ + + def __init__(self, module): + super(Route_maps, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="route_maps", + tmplt=Route_mapsTemplate(), + ) + self.parsers = [ + "call", + "description", + "action", + "continue_sequence", + "set_aggregator_ip", + "set_aggregator_as", + "set_as_path_exclude", + "set_as_path_prepend", + "set_atomic_aggregate", + "set_bgp_extcommunity_rt", + "set_extcommunity_rt", + "set_extcommunity_soo", + "set_ip_next_hop", + "set_ipv6_next_hop", + "set_large_community", + "set_local_preference", + "set_metric", + "set_metric_type", + "set_origin", + "set_originator_id", + "set_src", + "set_tag", + "set_weight", + "set_comm_list", + "set_comm_list_delete", + "set_community", + "match_as_path", + "match_community_community_list", + "match_community_exact_match", + "match_extcommunity", + "match_interface", + "match_large_community_large_community_list", + "match_metric", + "match_origin", + "match_peer", + "match_ip_address", + "match_ip_next_hop", + "match_ip_route_source", + "on_match_goto", + "on_match_next", + "match_ipv6_address", + "match_ipv6_nexthop", + "match_rpki", + ] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + wantd = self._route_maps_list_to_dict(self.want) + haved = self._route_maps_list_to_dict(self.have) + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to wantd + if self.state == "deleted": + haved = { + k: v for k, v in iteritems(haved) if k in wantd or not wantd + } + wantd = {} + + # remove superfluous config for overridden and deleted + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + if k not in wantd: + self.commands.append( + self._tmplt.render({"route_map": k}, "route_map", True) + ) + + for wk, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(wk, {})) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Route_maps network resource. + """ + w_entries = want.get("entries", {}) + h_entries = have.get("entries", {}) + self._compare_entries(want=w_entries, have=h_entries) + + def _compare_entries(self, want, have): + for wk, wentry in iteritems(want): + hentry = have.pop(wk, {}) + self.compare(parsers=self.parsers, want=wentry, have=hentry) + + def _route_maps_list_to_dict(self, entry): + entry = {x["route_map"]: x for x in entry} + for rmap, data in iteritems(entry): + if "entries" in data: + for x in data["entries"]: + x.update({"route_map": rmap}) + data["entries"] = { + (rmap, entry.get("sequence")): entry + for entry in data["entries"] + } + return entry diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index 1a2d786..81518f8 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -55,6 +55,9 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_g from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_address_family.bgp_address_family import ( Bgp_address_familyFacts, ) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.route_maps.route_maps import ( + Route_mapsFacts, +) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import ( Default, Neighbors, @@ -78,6 +81,7 @@ FACT_RESOURCE_SUBSETS = dict( ospf_interfaces=Ospf_interfacesFacts, bgp_global=Bgp_globalFacts, bgp_address_family=Bgp_address_familyFacts, + route_maps=Route_mapsFacts, ) diff --git a/plugins/module_utils/network/vyos/facts/route_maps/__init__.py b/plugins/module_utils/network/vyos/facts/route_maps/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/route_maps/__init__.py 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 new file mode 100644 index 0000000..51e8f97 --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py @@ -0,0 +1,84 @@ +# -*- 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 vyos route_maps fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +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.argspec.route_maps.route_maps import ( + Route_mapsArgs, +) + + +class Route_mapsFacts(object): + """The vyos route_maps facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Route_mapsArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_config(self, connection): + return connection.get("show configuration commands | grep route-map") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Route_maps network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + if not data: + data = self.get_config(connection) + + # parse native config using the Route_maps template + route_maps_parser = Route_mapsTemplate(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: + if item.get("entries"): + item["entries"] = list(item["entries"].values()) + + ansible_facts["ansible_network_resources"].pop("route_maps", None) + + # import epdb;epdb.serve() + params = utils.remove_empties( + utils.validate_config(self.argument_spec, {"config": objs}) + ) + + if params.get("config"): + facts["route_maps"] = params["config"] + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/plugins/module_utils/network/vyos/rm_templates/route_maps.py b/plugins/module_utils/network/vyos/rm_templates/route_maps.py new file mode 100644 index 0000000..3ab18b4 --- /dev/null +++ b/plugins/module_utils/network/vyos/rm_templates/route_maps.py @@ -0,0 +1,1293 @@ +# -*- 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.network_template import ( + NetworkTemplate, +) + + +class Route_mapsTemplate(NetworkTemplate): + def __init__(self, lines=None): + prefix = {"set": "set", "remove": "delete"} + super(Route_mapsTemplate, 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-exclude\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-prepend\s(?P<as>\S+) + *$""", + 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\satomic-aggregate(?P<as>) + *$""", + re.VERBOSE, + ), + "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+)\sset\scomm-list\scomm-list\s(?P<comm_list>\S+) + *$""", + re.VERBOSE, + ), + "compval": "set.comm_list.comm_list", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set comm-list comm-list {{set.comm_list.comm_list}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "comm_list": {"comm_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-rt\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-soo\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": "{{set.extcommunity_soo}}" + } + } + } + } + } + } + }, + { + "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<large_community>\S+) + *$""", + re.VERBOSE, + ), + "compval": "set.large_community", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set large-community {{set.large_community}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "large_community": "{{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_community", + "getval": re.compile( + r""" + ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scommunity\s(?P<value>\S+) + *$""", + re.VERBOSE, + ), + "compval": "set.community.value", + "setval": "policy route-map {{route_map}} rule {{sequence}} " + "set community {{set.community.value}}", + "result": { + "route_maps": { + "{{ route_map }}": { + "route_map": '{{ route_map }}', + "entries": { + "{{sequence}}": + { + "sequence": "{{sequence}}", + "set": { + "community": { + "value": "{{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_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 new file mode 100644 index 0000000..bca8383 --- /dev/null +++ b/plugins/modules/vyos_route_maps.py @@ -0,0 +1,953 @@ +#!/usr/bin/python +# -*- 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) + +""" +The module file for vyos_route_maps +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +module: vyos_route_maps +version_added: 2.3.0 +short_description: Route Map Resource Module. +description: +- This module manages route map configurations on devices running VYOS. +author: Ashwini Mhatre (@amhatre) +notes: +- Tested against vyos 1.2. +- This module works with connection C(network_cli). +options: + config: + description: A list of route-map configuration. + type: list + elements: dict + suboptions: + route_map: + description: Route map name. + type: str + entries: + description: Route Map rules. + aliases: ["rules"] + type: list + elements: dict + suboptions: + sequence: + type: int + description: Route map rule number <1-65535>. + call: + description: Route map name + type: str + description: + description: Description for the rule. + type: str + action: + description: Action for matching routes + type: str + choices: ["deny", "permit"] + continue_sequence: + description: Continue on a different entry within the route-map. + type: int + set: + description: Route parameters. + type: dict + suboptions: + aggregator: + type: dict + description: Border Gateway Protocol (BGP) aggregator attribute. + suboptions: + ip: + type: str + description: IP address. + as: + type: str + description: AS number of an aggregation. + as_path_exclude: + type: str + description: BGP AS path exclude string ex "456 64500 45001" + as_path_prepend: + type: str + description: Prepend string for a Border Gateway Protocol (BGP) AS-path attribute. + atomic_aggregate: + type: bool + description: Border Gateway Protocol (BGP) atomic aggregate attribute. + bgp_extcommunity_rt: + type: str + description: ExtCommunity in format AS:value + comm_list: + type: dict + description: Border Gateway Protocol (BGP) communities matching a community-list. + suboptions: + comm_list: + type: str + description: BGP communities with a community-list. + delete: + type: bool + description: Delete BGP communities matching the community-list. + community: + type: dict + description: Border Gateway Protocl (BGP) community attribute. + suboptions: + value: + type: str + description: Community in 4 octet AS:value format or it can be from local-AS, no-advertise,no-expert,internet,additive,none. + extcommunity_rt: + type: str + description: Set route target value.ASN:nn_or_IP_address:nn VPN extended community. + extcommunity_soo: + type: str + description: Set Site of Origin value. ASN:nn_or_IP_address:nn VPN extended community + ip_next_hop: + type: str + description: IP address. + ipv6_next_hop: + type: dict + description: Nexthop IPv6 address. + suboptions: + ip_type: + description: Global or Local + type: str + choices: ["global", "local"] + value: + description: ipv6 address + type: str + large_community: + type: str + description: Set BGP large community value. + local_preference: + type: str + description: Border Gateway Protocol (BGP) local preference attribute.Example <0-4294967295>. + metric: + type: str + description: Destination routing protocol metric. Example <0-4294967295>. + metric_type: + type: str + choices: ['type-1', 'type-2'] + description: Open Shortest Path First (OSPF) external metric-type. + origin: + description: Set bgp origin. + type: str + choices: [ "egp", "igp", "incomplete" ] + originator_id: + type: str + description: Border Gateway Protocol (BGP) originator ID attribute. Orignator IP address. + src: + type: str + description: Source address for route. Example <x.x.x.x> IP address. + tag: + type: str + description: Tag value for routing protocol. Example <1-65535> + weight: + type: str + description: Border Gateway Protocol (BGP) weight attribute. Example <0-4294967295> + match: + description: Route parameters to match. + type: dict + suboptions: + as_path: + description: Set as-path. + type: str + community: + description: BGP community attribute. + type: dict + suboptions: + community_list: + description: BGP community-list to match + type: str + exact_match: + description: BGP community-list to match + type: bool + extcommunity: + description: Extended community name. + type: str + interface: + description: First hop interface of a route to match. + type: str + ip: + description: IP prefix parameters to match. + type: dict + suboptions: + address: + description: IP address of route to match. + type: dict + suboptions: + list_type: &list_type + description: type of list + type: str + choices: ['access-list', 'prefix-list'] + value: &value + type: str + description: value of access-list and prefix list + next_hop: + description: next hop prefix list. + type: dict + suboptions: + list_type: *list_type + value: *value + route_source: + description: IP route-source to match + type: dict + suboptions: + list_type: *list_type + value: *value + ipv6: + description: IPv6 prefix parameters to match. + type: dict + suboptions: + address: + description: IPv6 address of route to match. + type: dict + suboptions: + list_type: *list_type + value: *value + next_hop: + description: next-hop ipv6 address IPv6 <h:h:h:h:h:h:h:h>. + type: str + large_community_large_community_list: + type: str + description: BGP large-community-list to match. + metric: + description: Route metric <1-65535>. + type: int + origin: + description: bgp origin. + type: str + choices: [ "ebgp", "ibgp", "incomplete" ] + peer: + type: str + description: Peer IP address <x.x.x.x>. + rpki: + type: str + description: RPKI validation value. + choices: [ "notfound", "invalid", "valid" ] + on_match: + type: dict + description: Exit policy on matches. + suboptions: + next: + type: bool + description: Next sequence number to goto on match. + goto: + type: int + description: Rule number to goto on match <1-65535>. + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the VYOS device by + executing the command B(show configuration commands | grep route-map). + - The state I(parsed) reads the configuration from C(show configuration commands | grep route-map) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + description: + - The state the configuration should be left in. + type: str + choices: + - deleted + - merged + - overridden + - replaced + - gathered + - rendered + - parsed + default: merged +""" +EXAMPLES = """ +# Using merged +# Before state + +# vyos@vyos:~$ show configuration commands | match "set policy route-map" +# vyos@vyos:~$ + - name: Merge the provided configuration with the exisiting running configuration + register: result + vyos.vyos.vyos_route_maps: &id001 + config: + - route_map: test1 + entries: + - sequence: 1 + description: "test" + action: permit + continue: 2 + on_match: + next: True + - route_map: test3 + entries: + - sequence: 1 + action: permit + match: + rpki: invalid + metric: 1 + peer: 192.0.2.32 + set: + local_preference: 4 + metric: 5 + metric_type: "type-1" + origin: egp + originator_id: 192.0.2.34 + tag: 5 + weight: 4 + state: merged +# After State +# vyos@vyos:~$ show configuration commands | match "set policy route-maps" +# 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 continue 2 +# set policy route-map test1 rule 1 on-match next +# set policy route-map test3 rule 1 action permit +# 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 192.0.2.34 +# 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 match metric 1 +# set policy route-map test3 rule 1 match peer 192.0.2.32 +# set policy route-map test3 rule 1 match rpki invalid + +# "after": [ +# { +# "entries": [ +# { +# "action": "permit", +# "continue_sequence": 2, +# "description": "test", +# "on_match": { +# "next": true +# }, +# "sequence": 1 +# } +# ], +# "route_map": "test1" +# }, +# { +# "entries": [ +# { +# "action": "permit", +# "match": { +# "metric": 1, +# "peer": "192.0.2.32", +# "rpki": "invalid" +# }, +# "sequence": 1, +# "set": { +# "local_preference": "4", +# "metric": "5", +# "metric_type": "type-1", +# "origin": "egp", +# "originator_id": "192.0.2.34", +# "tag": "5", +# "weight": "4" +# } +# } +# ], +# "route_map": "test3" +# } +# ], +# "before": {}, +# "changed": true, +# "commands": [ +# "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 continue 2", +# "set policy route-map test1 rule 1 on-match next", +# "set policy route-map test3 rule 1 action permit", +# "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 192.0.2.34", +# "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 match metric 1", +# "set policy route-map test3 rule 1 match peer 192.0.2.32", +# "set policy route-map test3 rule 1 match rpki invalid" +# ], + +# Using replaced: +# -------------- + +# Before state: +# vyos@vyos:~$ show configuration commands | match "set route-map policy" +# set policy route-map test2 rule 1 action 'permit' +# set policy route-map test2 rule 1 description 'test' +# set policy route-map test2 rule 1 on-match next +# set policy route-map test2 rule 2 action 'permit' +# set policy route-map test2 rule 2 on-match goto '4' +# set policy route-map test3 rule 1 action 'permit' +# set policy route-map test3 rule 1 match metric '1' +# set policy route-map test3 rule 1 match peer '192.0.2.32' +# set policy route-map test3 rule 1 match rpki 'invalid' +# set policy route-map test3 rule 1 set community 'internet' +# set policy route-map test3 rule 1 set ip-next-hop '192.0.2.33' +# 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 '192.0.2.34' +# set policy route-map test3 rule 1 set tag '5' +# set policy route-map test3 rule 1 set weight '4' +# +# - name: Replace the provided configuration with the exisiting running configuration +# register: result +# vyos.vyos.vyos_route_maps: &id001 +# config: +# - route_map: test3 +# entries: +# - sequence: 1 +# action: permit +# match: +# rpki: invalid +# metric: 3 +# peer: 192.0.2.35 +# set: +# local_preference: 6 +# metric: 4 +# metric_type: "type-1" +# origin: egp +# originator_id: 192.0.2.34 +# tag: 4 +# weight: 4 +# state: replaced +# After state: + +# vyos@vyos:~$ show configuration commands | match "set policy route-map" +# set policy route-map test3 rule 1 set local-preference 6 +# set policy route-map test3 rule 1 set metric 4 +# set policy route-map test3 rule 1 set tag 4 +# set policy route-map test3 rule 1 match metric 3 +# set policy route-map test3 rule 1 match peer 192.0.2.35 +# vyos@vyos:~$ +# +# +# Module Execution: +# +# "after": [ +# { +# "entries": [ +# { +# "action": "permit", +# "description": "test", +# "on_match": { +# "next": true +# }, +# "sequence": 1 +# }, +# { +# "action": "permit", +# "on_match": { +# "goto": 4 +# }, +# "sequence": 2 +# } +# ], +# "route_map": "test2" +# }, +# { +# "entries": [ +# { +# "action": "permit", +# "match": { +# "metric": 3, +# "peer": "192.0.2.35", +# "rpki": "invalid" +# }, +# "sequence": 1, +# "set": { +# "local_preference": "6", +# "metric": "4", +# "metric_type": "type-1", +# "origin": "egp", +# "originator_id": "192.0.2.34", +# "tag": "4", +# "weight": "4" +# } +# } +# ], +# "route_map": "test3" +# } +# ], +# "before": [ +# { +# "entries": [ +# { +# "action": "permit", +# "description": "test", +# "on_match": { +# "next": true +# }, +# "sequence": 1 +# }, +# { +# "action": "permit", +# "on_match": { +# "goto": 4 +# }, +# "sequence": 2 +# } +# ], +# "route_map": "test2" +# }, +# { +# "entries": [ +# { +# "action": "permit", +# "match": { +# "metric": 1, +# "peer": "192.0.2.32", +# "rpki": "invalid" +# }, +# "sequence": 1, +# "set": { +# "community": { +# "value": "internet" +# }, +# "ip_next_hop": "192.0.2.33", +# "local_preference": "4", +# "metric": "5", +# "metric_type": "type-1", +# "origin": "egp", +# "originator_id": "192.0.2.34", +# "tag": "5", +# "weight": "4" +# } +# } +# ], +# "route_map": "test3" +# } +# ], +# "changed": true, +# "commands": [ +# "delete policy route-map test3 rule 1 set ip-next-hop 192.0.2.33", +# "set policy route-map test3 rule 1 set local-preference 6", +# "set policy route-map test3 rule 1 set metric 4", +# "set policy route-map test3 rule 1 set tag 4", +# "delete policy route-map test3 rule 1 set community internet", +# "set policy route-map test3 rule 1 match metric 3", +# "set policy route-map test3 rule 1 match peer 192.0.2.35" +# ], +# +# Using deleted: +# ------------- + +# Before state: +# vyos@vyos:~$ show configuration commands | match "set policy route-map" +# set policy route-map test3 rule 1 set local-preference 6 +# set policy route-map test3 rule 1 set metric 4 +# set policy route-map test3 rule 1 set tag 4 +# set policy route-map test3 rule 1 match metric 3 +# set policy route-map test3 rule 1 match peer 192.0.2.35 +# vyos@vyos:~$ +# +# - name: Delete the provided configuration +# register: result +# vyos.vyos.vyos_route_maps: +# config: +# state: deleted +# After state: + +# vyos@vyos:~$ show configuration commands | match "set policy route-map" +# vyos@vyos:~$ +# +# +# Module Execution: +# +# "after": {}, +# "before": [ +# { +# "entries": [ +# { +# "action": "permit", +# "match": { +# "metric": 3, +# "peer": "192.0.2.35", +# }, +# "sequence": 1, +# "set": { +# "local_preference": "6", +# "metric": "4", +# "tag": "4", +# } +# } +# ], +# "route_map": "test3" +# } +# ], +# "changed": true, +# "commands": [ +# "delete policy route-map test3" +# ], +# +# using gathered: +# -------------- +# +# Before state: +# vyos@vyos:~$ show configuration commands | match "set policy route-maps" +# 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 continue 2 +# set policy route-map test1 rule 1 on-match next +# set policy route-map test3 rule 1 action permit +# 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 192.0.2.34 +# 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 match metric 1 +# set policy route-map test3 rule 1 match peer 192.0.2.32 +# set policy route-map test3 rule 1 match rpki invalid +# +# - name: gather configs +# vyos.vyos.vyos_route_maps: +# state: gathered + +# "gathered": [ +# { +# "entries": [ +# { +# "action": "permit", +# "continue_sequence": 2, +# "description": "test", +# "on_match": { +# "next": true +# }, +# "sequence": 1 +# } +# ], +# "route_map": "test1" +# }, +# { +# "entries": [ +# { +# "action": "permit", +# "match": { +# "metric": 1, +# "peer": "192.0.2.32", +# "rpki": "invalid" +# }, +# "sequence": 1, +# "set": { +# "local_preference": "4", +# "metric": "5", +# "metric_type": "type-1", +# "origin": "egp", +# "originator_id": "192.0.2.34", +# "tag": "5", +# "weight": "4" +# } +# } +# ], +# "route_map": "test3" +# } +# ] + +# Using parsed: +# ------------ + +# parsed.cfg +# 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 continue 2 +# set policy route-map test1 rule 1 on-match next +# set policy route-map test3 rule 1 action permit +# 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 192.0.2.34 +# 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 match metric 1 +# set policy route-map test3 rule 1 match peer 192.0.2.32 +# set policy route-map test3 rule 1 match rpki invalid +# +# - name: parse configs +# vyos.vyos.vyos_route_maps: +# running_config: "{{ lookup('file', './parsed.cfg') }}" +# state: parsed +# tags: +# - parsed +# +# Module execution: +# "parsed": [ +# { +# "entries": [ +# { +# "action": "permit", +# "continue_sequence": 2, +# "description": "test", +# "on_match": { +# "next": true +# }, +# "sequence": 1 +# } +# ], +# "route_map": "test1" +# }, +# { +# "entries": [ +# { +# "action": "permit", +# "match": { +# "metric": 1, +# "peer": "192.0.2.32", +# "rpki": "invalid" +# }, +# "sequence": 1, +# "set": { +# "local_preference": "4", +# "metric": "5", +# "metric_type": "type-1", +# "origin": "egp", +# "originator_id": "192.0.2.34", +# "tag": "5", +# "weight": "4" +# } +# } +# ], +# "route_map": "test3" +# } +# ] +# +# +# Using rendered: +# -------------- +# - name: Structure provided configuration into device specific commands +# register: result +# vyos.vyos.vyos_route_maps: &id001 +# config: +# - route_map: test1 +# entries: +# - sequence: 1 +# description: "test" +# action: permit +# continue_sequence: 2 +# on_match: +# next: True +# - route_map: test3 +# entries: +# - sequence: 1 +# action: permit +# match: +# rpki: invalid +# metric: 1 +# peer: 192.0.2.32 +# set: +# local_preference: 4 +# metric: 5 +# metric_type: "type-1" +# origin: egp +# originator_id: 192.0.2.34 +# tag: 5 +# weight: 4 +# state: rendered +# Module Execution: +# "rendered": [ +# "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 continue 2", +# "set policy route-map test1 rule 1 on-match next", +# "set policy route-map test3 rule 1 action permit", +# "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 192.0.2.34", +# "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 match metric 1", +# "set policy route-map test3 rule 1 match peer 192.0.2.32", +# "set policy route-map test3 rule 1 match rpki invalid" +# ] +# +# +# Using overridden: +# -------------- +# Before state: +# vyos@vyos:~$ show configuration commands | match "set policy route-map" +# set policy route-map test2 rule 1 action 'permit' +# set policy route-map test2 rule 1 description 'test' +# set policy route-map test2 rule 1 on-match next +# set policy route-map test2 rule 2 action 'permit' +# set policy route-map test2 rule 2 on-match goto '4' +# set policy route-map test3 rule 1 action 'permit' +# set policy route-map test3 rule 1 match metric '1' +# set policy route-map test3 rule 1 match peer '192.0.2.32' +# set policy route-map test3 rule 1 match rpki 'invalid' +# set policy route-map test3 rule 1 set community 'internet' +# set policy route-map test3 rule 1 set ip-next-hop '192.0.2.33' +# 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 '192.0.2.34' +# set policy route-map test3 rule 1 set tag '5' +# set policy route-map test3 rule 1 set weight '4' +# +# - name: Override the existing configuration with the provided running configuration +# register: result +# vyos.vyos.vyos_route_maps: &id001 +# config: +# - route_map: test3 +# entries: +# - sequence: 1 +# action: permit +# match: +# rpki: invalid +# metric: 3 +# peer: 192.0.2.35 +# set: +# local_preference: 6 +# metric: 4 +# metric_type: "type-1" +# origin: egp +# originator_id: 192.0.2.34 +# tag: 4 +# weight: 4 +# state: overridden +# After state: + +# vyos@vyos:~$ show configuration commands | match "set policy route-map" +# 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 '192.0.2.34' +# set policy route-map test3 rule 1 set weight '4' +# set policy route-map test3 rule 1 set local-preference 6 +# set policy route-map test3 rule 1 set metric 4 +# set policy route-map test3 rule 1 set tag 4 +# set policy route-map test3 rule 1 match metric 3 +# set policy route-map test3 rule 1 match peer 192.0.2.35 +# set policy route-map test3 rule 1 match rpki 'invalid' + +# Module Execution: +# "after": [ +# { +# "entries": [ +# { +# "action": "permit", +# "match": { +# "metric": 3, +# "peer": "192.0.2.35", +# "rpki": "invalid" +# }, +# "sequence": 1, +# "set": { +# "local_preference": "6", +# "metric": "4", +# "metric_type": "type-1", +# "origin": "egp", +# "originator_id": "192.0.2.34", +# "tag": "4", +# "weight": "4" +# } +# } +# ], +# "route_map": "test3" +# } +# ], +# "before": [ +# { +# "entries": [ +# { +# "action": "permit", +# "description": "test", +# "on_match": { +# "next": true +# }, +# "sequence": 1 +# }, +# { +# "action": "permit", +# "on_match": { +# "goto": 4 +# }, +# "sequence": 2 +# } +# ], +# "route_map": "test2" +# }, +# { +# "entries": [ +# { +# "action": "permit", +# "match": { +# "metric": 1, +# "peer": "192.0.2.32", +# "rpki": "invalid" +# }, +# "sequence": 1, +# "set": { +# "community": { +# "value": "internet" +# }, +# "ip_next_hop": "192.0.2.33", +# "local_preference": "4", +# "metric": "5", +# "metric_type": "type-1", +# "origin": "egp", +# "originator_id": "192.0.2.34", +# "tag": "5", +# "weight": "4" +# } +# } +# ], +# "route_map": "test3" +# } +# ], +# "changed": true, +# "commands": [ +# "delete policy route-map test2", +# "delete policy route-map test3 rule 1 set ip-next-hop 192.0.2.33", +# "set policy route-map test3 rule 1 set local-preference 6", +# "set policy route-map test3 rule 1 set metric 4", +# "set policy route-map test3 rule 1 set tag 4", +# "delete policy route-map test3 rule 1 set community internet", +# "set policy route-map test3 rule 1 match metric 3", +# "set policy route-map test3 rule 1 match peer 192.0.2.35" +# ], +# + +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.route_maps.route_maps import ( + Route_mapsArgs, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.route_maps.route_maps import ( + Route_maps, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Route_mapsArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Route_maps(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/vyos_route_maps/defaults/main.yaml b/tests/integration/targets/vyos_route_maps/defaults/main.yaml new file mode 100644 index 0000000..852a6be --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: '[^_].*' +test_items: [] diff --git a/tests/integration/targets/vyos_route_maps/tasks/cli.yaml b/tests/integration/targets/vyos_route_maps/tasks/cli.yaml new file mode 100644 index 0000000..93eb2fe --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tasks/cli.yaml @@ -0,0 +1,19 @@ +--- +- name: Collect all cli test cases + find: + paths: '{{ role_path }}/tests/cli' + patterns: '{{ testcase }}.yaml' + use_regex: true + register: test_cases + delegate_to: localhost + +- name: Set test_items + set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" + +- name: Run test case (connection=ansible.netcommon.network_cli) + include: '{{ test_case_to_run }}' + vars: + ansible_connection: ansible.netcommon.network_cli + with_items: '{{ test_items }}' + loop_control: + loop_var: test_case_to_run diff --git a/tests/integration/targets/vyos_route_maps/tasks/main.yaml b/tests/integration/targets/vyos_route_maps/tasks/main.yaml new file mode 100644 index 0000000..b957d2f --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tasks/main.yaml @@ -0,0 +1,4 @@ +--- +- include: cli.yaml + tags: + - network_cli diff --git a/tests/integration/targets/vyos_route_maps/tests/cli/_parsed.cfg b/tests/integration/targets/vyos_route_maps/tests/cli/_parsed.cfg new file mode 100644 index 0000000..a4aa4d3 --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tests/cli/_parsed.cfg @@ -0,0 +1,13 @@ +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 continue 2 +set policy route-map test3 rule 1 action permit +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 192.0.2.34 +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 match metric 1 +set policy route-map test3 rule 1 match peer 192.0.2.32
\ No newline at end of file diff --git a/tests/integration/targets/vyos_route_maps/tests/cli/_populate.yaml b/tests/integration/targets/vyos_route_maps/tests/cli/_populate.yaml new file mode 100644 index 0000000..9ef975f --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tests/cli/_populate.yaml @@ -0,0 +1,23 @@ +--- +- name: setup + vyos.vyos.vyos_config: + lines: + - set policy route-map test2 rule 1 action 'permit' + - set policy route-map test2 rule 1 description 'test' + - set policy route-map test2 rule 2 action 'permit' + - set policy route-map test2 rule 2 on-match goto '4' + - set policy route-map test3 rule 1 action 'permit' + - set policy route-map test3 rule 1 match metric '1' + - set policy route-map test3 rule 1 match peer '192.0.2.32' + - set policy route-map test3 rule 1 set community 'internet' + - set policy route-map test3 rule 1 set ip-next-hop '192.0.2.33' + - 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 '192.0.2.34' + - set policy route-map test3 rule 1 set tag '5' + - set policy route-map test3 rule 1 set weight '4' + ignore_errors: true + vars: + ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/vyos_route_maps/tests/cli/_remove_config.yaml b/tests/integration/targets/vyos_route_maps/tests/cli/_remove_config.yaml new file mode 100644 index 0000000..70e9889 --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tests/cli/_remove_config.yaml @@ -0,0 +1,10 @@ +--- +- name: Remove pre-existing route-maps + vyos.vyos.vyos_config: + lines: + - delete policy route-map test1 + - delete policy route-map test2 + - delete policy route-map test3 + ignore_errors: true + vars: + ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/vyos_route_maps/tests/cli/deleted.yaml b/tests/integration/targets/vyos_route_maps/tests/cli/deleted.yaml new file mode 100644 index 0000000..efcb88b --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tests/cli/deleted.yaml @@ -0,0 +1,36 @@ +--- +- debug: + msg: START vyos_route_maps deleted integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Delete the provided configuration + register: result + vyos.vyos.vyos_route_maps: &id001 + config: + state: deleted + + - assert: + that: + - result.commands|length == 2 + - result.changed == true + - result.commands|symmetric_difference(deleted.commands) == [] + + - name: Delete the existing configuration with the provided running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_ospf_interfaces: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_route_maps/tests/cli/empty_config.yaml b/tests/integration/targets/vyos_route_maps/tests/cli/empty_config.yaml new file mode 100644 index 0000000..6d992b8 --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tests/cli/empty_config.yaml @@ -0,0 +1,60 @@ +--- +- debug: + msg: START vyos_route_maps empty_config integration tests on connection={{ + ansible_connection }} + +- name: Merged with empty config should give appropriate error message + register: result + ignore_errors: true + vyos.vyos.vyos_route_maps: + config: + state: merged + +- assert: + that: + - result.msg == 'value of config parameter must not be empty for state merged' + +- name: Replaced with empty config should give appropriate error message + register: result + ignore_errors: true + vyos.vyos.vyos_route_maps: + config: + state: replaced + +- assert: + that: + - result.msg == 'value of config parameter must not be empty for state replaced' + +- name: Overridden with empty config should give appropriate error message + register: result + ignore_errors: true + vyos.vyos.vyos_route_maps: + config: + state: overridden + +- assert: + that: + - result.msg == 'value of config parameter must not be empty for state overridden' + +- name: Parsed with empty running_config should give appropriate error message + register: result + ignore_errors: true + vyos.vyos.vyos_route_maps: + running_config: + state: parsed + +- assert: + that: + - result.msg == 'value of running_config parameter must not be empty for state + parsed' + +- name: Rendered with empty config should give appropriate error message + register: result + ignore_errors: true + vyos.vyos.vyos_route_maps: + config: + state: rendered + +- assert: + that: + - result.msg == 'value of config parameter must not be empty for state rendered' diff --git a/tests/integration/targets/vyos_route_maps/tests/cli/gathered.yaml b/tests/integration/targets/vyos_route_maps/tests/cli/gathered.yaml new file mode 100644 index 0000000..b73fe23 --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tests/cli/gathered.yaml @@ -0,0 +1,24 @@ +--- +- debug: + msg: START vyos_route_maps gathered integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Gather config from the device in structured format. + register: result + vyos.vyos.vyos_route_maps: + state: gathered + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: route_maps + + - assert: + that: + - result.changed == false + - result.gathered|symmetric_difference(ansible_facts['network_resources']['route_maps']) == [] diff --git a/tests/integration/targets/vyos_route_maps/tests/cli/merged.yaml b/tests/integration/targets/vyos_route_maps/tests/cli/merged.yaml new file mode 100644 index 0000000..2b86969 --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tests/cli/merged.yaml @@ -0,0 +1,60 @@ +--- +- debug: + msg: START vyos_route_maps merged integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- block: + + - name: Merge the provided configuration with the exisiting running configuration + register: result + vyos.vyos.vyos_route_maps: &id001 + config: + - route_map: test1 + entries: + - sequence: 1 + description: "test" + action: permit + continue_sequence: 2 + - route_map: test3 + entries: + - sequence: 1 + action: permit + match: + metric: 1 + peer: 192.0.2.32 + set: + local_preference: 4 + metric: 5 + metric_type: "type-1" + origin: egp + originator_id: 192.0.2.34 + tag: 5 + weight: 4 + state: merged + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: route_maps + + - assert: + that: + - result.commands|length == 13 + - result.changed == true + - result.commands|symmetric_difference(merged.commands) == [] + - result.after|symmetric_difference(ansible_facts['network_resources']['route_maps']) == [] + + - name: Merge the provided configuration with the existing running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_route_maps: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_route_maps/tests/cli/overridden.yaml b/tests/integration/targets/vyos_route_maps/tests/cli/overridden.yaml new file mode 100644 index 0000000..f014cf1 --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tests/cli/overridden.yaml @@ -0,0 +1,56 @@ +--- +- debug: + msg: START vyos_route_maps overridden integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Override the existing configuration with the provided running configuration + register: result + vyos.vyos.vyos_route_maps: &id001 + config: + - route_map: test3 + entries: + - sequence: 1 + action: permit + match: + metric: 3 + peer: 192.0.2.35 + set: + local_preference: 6 + metric: 4 + metric_type: "type-1" + origin: egp + originator_id: 192.0.2.34 + tag: 4 + weight: 4 + state: overridden + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: route_maps + + - assert: + that: + - result.commands|length == 8 + - result.changed == true + - result.commands|symmetric_difference(overridden.commands) == [] + - result.after|symmetric_difference(ansible_facts['network_resources']['route_maps']) == [] + + - name: Override the existing configuration with the provided running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_route_maps: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_route_maps/tests/cli/parsed.yaml b/tests/integration/targets/vyos_route_maps/tests/cli/parsed.yaml new file mode 100644 index 0000000..47cb4cc --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tests/cli/parsed.yaml @@ -0,0 +1,16 @@ +--- +- debug: + msg: START vyos_route_maps parsed integration tests on connection={{ ansible_connection + }} + +- name: Provide the running configuration for parsing (config to be parsed) + become: true + register: result + vyos.vyos.vyos_route_maps: + running_config: "{{ lookup('file', '_parsed.cfg') }}" + state: parsed + +- assert: + that: + - result.changed == false + - result.parsed|symmetric_difference(merged.after) == [] diff --git a/tests/integration/targets/vyos_route_maps/tests/cli/rendered.yaml b/tests/integration/targets/vyos_route_maps/tests/cli/rendered.yaml new file mode 100644 index 0000000..7d194d9 --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tests/cli/rendered.yaml @@ -0,0 +1,40 @@ +--- +- debug: + msg: START vyos_route_maps rendered integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- block: + + - name: Structure provided configuration into device specific commands + register: result + vyos.vyos.vyos_route_maps: &id001 + config: + - route_map: test1 + entries: + - sequence: 1 + description: "test" + action: permit + continue_sequence: 2 + - route_map: test3 + entries: + - sequence: 1 + action: permit + match: + metric: 1 + peer: 192.0.2.32 + set: + local_preference: 4 + metric: 5 + metric_type: "type-1" + origin: egp + originator_id: 192.0.2.34 + tag: 5 + weight: 4 + state: rendered + + - assert: + that: + - result.changed == false + - result.rendered|symmetric_difference(merged.commands) == [] diff --git a/tests/integration/targets/vyos_route_maps/tests/cli/replaced.yaml b/tests/integration/targets/vyos_route_maps/tests/cli/replaced.yaml new file mode 100644 index 0000000..1675ec5 --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tests/cli/replaced.yaml @@ -0,0 +1,56 @@ +--- +- debug: + msg: START vyos_route_maps replaced integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Replace the provided configuration with the exisiting running configuration + register: result + vyos.vyos.vyos_route_maps: &id001 + config: + - route_map: test3 + entries: + - sequence: 1 + action: permit + match: + metric: 3 + peer: 192.0.2.35 + set: + local_preference: 6 + metric: 4 + metric_type: "type-1" + origin: egp + originator_id: 192.0.2.34 + tag: 4 + weight: 4 + state: replaced + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: route_maps + + - 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']) == [] + + - name: Replace the provided configuration with the existing running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_route_maps: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_route_maps/tests/cli/rtt.yaml b/tests/integration/targets/vyos_route_maps/tests/cli/rtt.yaml new file mode 100644 index 0000000..2a95f1f --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/tests/cli/rtt.yaml @@ -0,0 +1,81 @@ +--- +- debug: + msg: START vyos_route_maps merged integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- block: + + - name: Merge the provided configuration with the exisiting running configuration + register: baseconfig + vyos.vyos.vyos_route_maps: + config: + - route_map: test1 + entries: + - sequence: 1 + description: "test" + action: permit + continue_sequence: 2 + - route_map: test3 + entries: + - sequence: 1 + action: permit + match: + metric: 1 + peer: 192.0.2.32 + set: + local_preference: 4 + metric: 5 + metric_type: "type-1" + origin: egp + originator_id: 192.0.2.34 + tag: 5 + weight: 4 + state: merged + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: route_maps + + - assert: + that: + - baseconfig.commands|length == 13 + - baseconfig.changed == true + - baseconfig.commands|symmetric_difference(merged.commands) == [] + - baseconfig.after|symmetric_difference(ansible_facts['network_resources']['route_maps']) == [] + + - name: Merge the existing configuration with the provided running configuration + register: result + vyos.vyos.vyos_route_maps: + config: + - route_map: test2 + entries: + - sequence: 1 + action: permit + match: + metric: 3 + peer: 192.0.2.35 + set: + local_preference: 6 + metric: 4 + metric_type: "type-1" + origin: egp + originator_id: 192.0.2.34 + tag: 4 + weight: 4 + + - name: Revert back to base config using facts round trip + become: true + register: revert + vyos.vyos.vyos_route_maps: + config: "{{ ansible_facts['network_resources']['route_maps'] }}" + state: overridden + + - name: Assert that config was reverted + assert: + that: baseconfig.after == revert.after + + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_route_maps/vars/main.yaml b/tests/integration/targets/vyos_route_maps/vars/main.yaml new file mode 100644 index 0000000..5a0d027 --- /dev/null +++ b/tests/integration/targets/vyos_route_maps/vars/main.yaml @@ -0,0 +1,109 @@ +--- +merged: + commands: + - 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 continue 2 + - set policy route-map test3 rule 1 action permit + - 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 192.0.2.34 + - 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 match metric 1 + - set policy route-map test3 rule 1 match peer 192.0.2.32 + + after: + - route_map: "test3" + entries: + - sequence: 1 + action: "permit" + match: + metric: 1 + peer: "192.0.2.32" + set: + local_preference: "4" + metric: "5" + metric_type: "type-1" + origin: egp + originator_id: "192.0.2.34" + tag: "5" + weight: "4" + - route_map: "test1" + entries: + - sequence: 1 + description: "test" + action: "permit" + continue_sequence: 2 + +replaced: + commands: + - delete policy route-map test3 rule 1 set ip-next-hop 192.0.2.33 + - set policy route-map test3 rule 1 set local-preference 6 + - set policy route-map test3 rule 1 set metric 4 + - set policy route-map test3 rule 1 set tag 4 + - delete policy route-map test3 rule 1 set community internet + - set policy route-map test3 rule 1 match metric 3 + - set policy route-map test3 rule 1 match peer 192.0.2.35 + + after: + - route_map: test2 + entries: + - sequence: 1 + description: "test" + action: permit + - sequence: 1 + action: permit + on_match: + goto: 4 + - route_map: test3 + entries: + - sequence: 1 + action: permit + match: + metric: 3 + peer: 192.0.2.35 + set: + local_preference: 6 + metric: 4 + metric_type: "type-1" + origin: egp + originator_id: 192.0.2.34 + tag: 4 + weight: 4 + +overridden: + commands: + - delete policy route-map test2 + - delete policy route-map test3 rule 1 set ip-next-hop 192.0.2.33 + - set policy route-map test3 rule 1 set local-preference 6 + - set policy route-map test3 rule 1 set metric 4 + - set policy route-map test3 rule 1 set tag 4 + - delete policy route-map test3 rule 1 set community internet + - set policy route-map test3 rule 1 match metric 3 + - set policy route-map test3 rule 1 match peer 192.0.2.35 + + after: + - route_map: test3 + entries: + - sequence: 1 + action: permit + match: + metric: 3 + peer: 192.0.2.35 + set: + local_preference: 6 + metric: 4 + metric_type: "type-1" + origin: egp + originator_id: 192.0.2.34 + tag: 4 + weight: 4 + +deleted: + commands: + - delete policy route-map test2 + - delete policy route-map test3 + after: [] diff --git a/tests/sanity/ignore-2.10.txt b/tests/sanity/ignore-2.10.txt index a44822b..69f4063 100644 --- a/tests/sanity/ignore-2.10.txt +++ b/tests/sanity/ignore-2.10.txt @@ -1,3 +1,6 @@ plugins/action/vyos.py action-plugin-docs # base class for deprecated network platform modules using `connection: local` plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py compile-2.6!skip plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py import-2.6!skip +plugins/module_utils/network/vyos/config/route_maps/route_maps.py compile-2.6!skip +plugins/module_utils/network/vyos/config/route_maps/route_maps.py import-2.6!skip +plugins/modules/vyos_route_maps.py import-2.6!skip diff --git a/tests/sanity/ignore-2.11.txt b/tests/sanity/ignore-2.11.txt index a44822b..69f4063 100644 --- a/tests/sanity/ignore-2.11.txt +++ b/tests/sanity/ignore-2.11.txt @@ -1,3 +1,6 @@ plugins/action/vyos.py action-plugin-docs # base class for deprecated network platform modules using `connection: local` plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py compile-2.6!skip plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py import-2.6!skip +plugins/module_utils/network/vyos/config/route_maps/route_maps.py compile-2.6!skip +plugins/module_utils/network/vyos/config/route_maps/route_maps.py import-2.6!skip +plugins/modules/vyos_route_maps.py import-2.6!skip diff --git a/tests/sanity/ignore-2.12.txt b/tests/sanity/ignore-2.12.txt index a44822b..69f4063 100644 --- a/tests/sanity/ignore-2.12.txt +++ b/tests/sanity/ignore-2.12.txt @@ -1,3 +1,6 @@ plugins/action/vyos.py action-plugin-docs # base class for deprecated network platform modules using `connection: local` plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py compile-2.6!skip plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py import-2.6!skip +plugins/module_utils/network/vyos/config/route_maps/route_maps.py compile-2.6!skip +plugins/module_utils/network/vyos/config/route_maps/route_maps.py import-2.6!skip +plugins/modules/vyos_route_maps.py import-2.6!skip diff --git a/tests/sanity/ignore-2.9.txt b/tests/sanity/ignore-2.9.txt index 68064bb..7178a20 100644 --- a/tests/sanity/ignore-2.9.txt +++ b/tests/sanity/ignore-2.9.txt @@ -13,3 +13,6 @@ plugins/modules/vyos_lldp_interface.py validate-modules:invalid-documentation # plugins/action/vyos.py action-plugin-docs # base class for deprecated network platform modules using `connection: local` plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py compile-2.6!skip plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py import-2.6!skip +plugins/module_utils/network/vyos/config/route_maps/route_maps.py compile-2.6!skip +plugins/module_utils/network/vyos/config/route_maps/route_maps.py import-2.6!skip +plugins/modules/vyos_route_maps.py import-2.6!skip diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_route_maps_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_route_maps_config.cfg new file mode 100644 index 0000000..c7fd9ef --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_route_maps_config.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 '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'
\ No newline at end of file diff --git a/tests/unit/modules/network/vyos/test_vyos_route_maps.py b/tests/unit/modules/network/vyos/test_vyos_route_maps.py new file mode 100644 index 0000000..17c605e --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_route_maps.py @@ -0,0 +1,589 @@ +# (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 ansible_collections.vyos.vyos.tests.unit.compat.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() + + 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, transport="cli", filename=None): + if filename is None: + filename = "vyos_route_maps_config.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 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 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_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="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="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 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 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 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 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_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 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 '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) |