Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vyos route maps #156

Merged
merged 24 commits into from
May 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelogs/fragments/vyos-route-maps.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
minor_changes:
- Add vyos_route_maps resource module (https://github.com/ansible-collections/vyos.vyos/pull/156.).
Empty file.
230 changes: 230 additions & 0 deletions plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py
Original file line number Diff line number Diff line change
@@ -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
Empty file.
160 changes: 160 additions & 0 deletions plugins/module_utils/network/vyos/config/route_maps/route_maps.py
Original file line number Diff line number Diff line change
@@ -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)
ashwini-mhatre marked this conversation as resolved.
Show resolved Hide resolved

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
Loading