diff --git a/changelogs/fragments/132-add-vyos-bgp-address-family.yml b/changelogs/fragments/132-add-vyos-bgp-address-family.yml new file mode 100644 index 0000000..9dc1894 --- /dev/null +++ b/changelogs/fragments/132-add-vyos-bgp-address-family.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Add vyos BGP address_family resource module (https://github.com/ansible-collections/vyos.vyos/pull/132). diff --git a/meta/runtime.yml b/meta/runtime.yml index 1f99057..f8db7cc 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -1,215 +1,221 @@ --- requires_ansible: '>=2.9.10' plugin_routing: action: vyos_banner: redirect: vyos.vyos.vyos banner: redirect: vyos.vyos.vyos vyos_bgp_global: redirect: vyos.vyos.vyos bgp_global: redirect: vyos.vyos.vyos + vyos_bgp_address_family: + redirect: vyos.vyos.vyos + bgp_address_family: + redirect: vyos.vyos.vyos vyos_command: redirect: vyos.vyos.vyos command: redirect: vyos.vyos.vyos vyos_config: redirect: vyos.vyos.vyos config: redirect: vyos.vyos.vyos vyos_facts: redirect: vyos.vyos.vyos facts: redirect: vyos.vyos.vyos vyos_firewall_global: redirect: vyos.vyos.vyos firewall_global: redirect: vyos.vyos.vyos vyos_firewall_interfaces: redirect: vyos.vyos.vyos firewall_interfaces: redirect: vyos.vyos.vyos vyos_firewall_rules: redirect: vyos.vyos.vyos firewall_rules: redirect: vyos.vyos.vyos vyos_interface: redirect: vyos.vyos.vyos interface: redirect: vyos.vyos.vyos vyos_interfaces: redirect: vyos.vyos.vyos interfaces: redirect: vyos.vyos.vyos vyos_l3_interface: redirect: vyos.vyos.vyos l3_interface: redirect: vyos.vyos.vyos vyos_l3_interfaces: redirect: vyos.vyos.vyos l3_interfaces: redirect: vyos.vyos.vyos vyos_lag_interfaces: redirect: vyos.vyos.vyos lag_interfaces: redirect: vyos.vyos.vyos vyos_linkagg: redirect: vyos.vyos.vyos linkagg: redirect: vyos.vyos.vyos vyos_lldp: redirect: vyos.vyos.vyos lldp: redirect: vyos.vyos.vyos vyos_lldp_global: redirect: vyos.vyos.vyos lldp_global: redirect: vyos.vyos.vyos vyos_lldp_interface: redirect: vyos.vyos.vyos lldp_interface: redirect: vyos.vyos.vyos vyos_lldp_interfaces: redirect: vyos.vyos.vyos lldp_interfaces: redirect: vyos.vyos.vyos vyos_logging: redirect: vyos.vyos.vyos logging: redirect: vyos.vyos.vyos vyos_ospfv2: redirect: vyos.vyos.vyos ospfv2: redirect: vyos.vyos.vyos vyos_ospfv3: redirect: vyos.vyos.vyos ospfv3: redirect: vyos.vyos.vyos vyos_ospf_interfaces: redirect: vyos.vyos.vyos ospf_interfaces: redirect: vyos.vyos.vyos vyos_ping: redirect: vyos.vyos.vyos ping: redirect: vyos.vyos.vyos vyos_static_route: redirect: vyos.vyos.vyos static_route: redirect: vyos.vyos.vyos vyos_static_routes: redirect: vyos.vyos.vyos static_routes: redirect: vyos.vyos.vyos vyos_system: redirect: vyos.vyos.vyos system: redirect: vyos.vyos.vyos vyos_user: redirect: vyos.vyos.vyos user: redirect: vyos.vyos.vyos vyos_vlan: redirect: vyos.vyos.vyos vlan: redirect: vyos.vyos.vyos modules: banner: redirect: vyos.vyos.vyos_banner bgp_global: redirect: vyos.vyos.vyos_bgp_global + bgp_address_family: + redirect: vyos.vyos.vyos_bgp_address_family command: redirect: vyos.vyos.vyos_command config: redirect: vyos.vyos.vyos_config facts: redirect: vyos.vyos.vyos_facts firewall_global: redirect: vyos.vyos.vyos_firewall_global firewall_interfaces: redirect: vyos.vyos.vyos_firewall_interfaces firewall_rules: redirect: vyos.vyos.vyos_firewall_rules interface: redirect: vyos.vyos.vyos_interface deprecation: removal_date: '2022-06-01' warning_text: See the plugin documentation for more details vyos_interface: deprecation: removal_date: '2022-06-01' warning_text: See the plugin documentation for more details interfaces: redirect: vyos.vyos.vyos_interfaces l3_interface: redirect: vyos.vyos.vyos_l3_interface deprecation: removal_date: '2022-06-01' warning_text: See the plugin documentation for more details vyos_l3_interface: deprecation: removal_date: '2022-06-01' warning_text: See the plugin documentation for more details l3_interfaces: redirect: vyos.vyos.vyos_l3_interfaces lag_interfaces: redirect: vyos.vyos.vyos_lag_interfaces linkagg: redirect: vyos.vyos.vyos_linkagg deprecation: removal_date: '2022-06-01' warning_text: See the plugin documentation for more details vyos_linkagg: deprecation: removal_date: '2022-06-01' warning_text: See the plugin documentation for more details lldp: redirect: vyos.vyos.vyos_lldp deprecation: removal_date: '2022-06-01' warning_text: See the plugin documentation for more details vyos_lldp: deprecation: removal_date: '2022-06-01' warning_text: See the plugin documentation for more details lldp_global: redirect: vyos.vyos.vyos_lldp_global lldp_interface: redirect: vyos.vyos.vyos_lldp_interface deprecation: removal_date: '2022-06-01' warning_text: See the plugin documentation for more details vyos_lldp_interface: deprecation: removal_date: '2022-06-01' warning_text: See the plugin documentation for more details lldp_interfaces: redirect: vyos.vyos.vyos_lldp_interfaces logging: redirect: vyos.vyos.vyos_logging ospfv2: redirect: vyos.vyos.vyos_ospfv2 ospfv3: redirect: vyos.vyos.vyos_ospfv3 ospf_interfaces: redirect: vyos.vyos.vyos_ospf_interfaces ping: redirect: vyos.vyos.vyos_ping static_route: redirect: vyos.vyos.vyos_static_route deprecation: removal_date: '2022-06-01' warning_text: See the plugin documentation for more details vyos_static_route: deprecation: removal_date: '2022-06-01' warning_text: See the plugin documentation for more details static_routes: redirect: vyos.vyos.vyos_static_routes system: redirect: vyos.vyos.vyos_system user: redirect: vyos.vyos.vyos_user vlan: redirect: vyos.vyos.vyos_vlan diff --git a/plugins/module_utils/network/vyos/argspec/bgp_address_family/__init__.py b/plugins/module_utils/network/vyos/argspec/bgp_address_family/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/module_utils/network/vyos/argspec/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/argspec/bgp_address_family/bgp_address_family.py new file mode 100644 index 0000000..8cbbc50 --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/bgp_address_family/bgp_address_family.py @@ -0,0 +1,200 @@ +# -*- 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 resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the vyos_bgp_address_family module +""" + + +class Bgp_address_familyArgs(object): # pylint: disable=R0903 + """The arg spec for the vyos_bgp_address_family module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "running_config": {}, + "state": { + "default": "merged", + "type": "str", + "choices": [ + "merged", + "replaced", + "deleted", + "gathered", + "parsed", + "rendered", + "purged", + "overridden", + ], + }, + "config": { + "type": "dict", + "options": { + "neighbors": { + "elements": "dict", + "type": "list", + "options": { + "address_family": { + "elements": "dict", + "type": "list", + "options": { + "nexthop_local": {"type": "bool"}, + "soft_reconfiguration": {"type": "bool"}, + "unsupress_map": {"type": "str"}, + "nexthop_self": {"type": "bool"}, + "weight": {"type": "int"}, + "prefix_list": { + "elements": "dict", + "type": "list", + "options": { + "action": { + "type": "str", + "choices": ["export", "import"], + }, + "prefix_list": {"type": "str"}, + }, + }, + "default_originate": {"type": "str"}, + "distribute_list": { + "elements": "dict", + "type": "list", + "options": { + "action": { + "type": "str", + "choices": ["export", "import"], + }, + "acl": {"type": "int"}, + }, + }, + "allowas_in": {"type": "int"}, + "filter_list": { + "elements": "dict", + "type": "list", + "options": { + "action": { + "type": "str", + "choices": ["export", "import"], + }, + "path_list": {"type": "str"}, + }, + }, + "route_server_client": {"type": "bool"}, + "attribute_unchanged": { + "type": "dict", + "options": { + "as_path": {"type": "bool"}, + "med": {"type": "bool"}, + "next_hop": {"type": "bool"}, + }, + }, + "peer_group": {"type": "str"}, + "maximum_prefix": {"type": "int"}, + "route_reflector_client": {"type": "bool"}, + "route_map": { + "elements": "dict", + "type": "list", + "options": { + "action": { + "type": "str", + "choices": ["export", "import"], + }, + "route_map": {"type": "str"}, + }, + }, + "capability": { + "type": "dict", + "options": { + "orf": { + "type": "str", + "choices": ["send", "receive"], + }, + "dynamic": {"type": "bool"}, + }, + }, + "remove_private_as": {"type": "bool"}, + "as_override": {"type": "bool"}, + "afi": { + "type": "str", + "choices": ["ipv4", "ipv6"], + }, + }, + }, + "neighbor_address": {"type": "str"}, + }, + }, + "as_number": {"type": "int"}, + "address_family": { + "elements": "dict", + "type": "list", + "options": { + "afi": {"type": "str", "choices": ["ipv4", "ipv6"]}, + "redistribute": { + "elements": "dict", + "type": "list", + "options": { + "table": {"type": "str"}, + "metric": {"type": "int"}, + "protocol": { + "type": "str", + "choices": [ + "connected", + "kernel", + "ospf", + "ospfv3", + "rip", + "ripng", + "static", + ], + }, + "route_map": {"type": "str"}, + }, + }, + "networks": { + "elements": "dict", + "type": "list", + "options": { + "backdoor": {"type": "bool"}, + "prefix": {"type": "str"}, + "path_limit": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "aggregate_address": { + "elements": "dict", + "type": "list", + "options": { + "summary_only": {"type": "bool"}, + "prefix": {"type": "str"}, + "as_set": {"type": "bool"}, + }, + }, + }, + }, + }, + }, + } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/bgp_address_family/__init__.py b/plugins/module_utils/network/vyos/config/bgp_address_family/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py new file mode 100644 index 0000000..876402f --- /dev/null +++ b/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py @@ -0,0 +1,369 @@ +# +# -*- 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_bgp_address_family 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. +""" + +import re + +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.bgp_address_family import ( + Bgp_address_familyTemplate, +) + + +class Bgp_address_family(ResourceModule): + """ + The vyos_bgp_address_family config class + """ + + def __init__(self, module): + super(Bgp_address_family, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="bgp_address_family", + tmplt=Bgp_address_familyTemplate(), + ) + self.parsers = [] + + 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 = {} + haved = {} + + if ( + self.want.get("as_number") == self.have.get("as_number") + or not self.have + ): + if self.want: + wantd = {self.want["as_number"]: self.want} + if self.have: + haved = {self.have["as_number"]: self.have} + else: + self._module.fail_json( + msg="Only one bgp instance is allowed per device" + ) + + # turn all lists of dicts into dicts prior to merge + for entry in wantd, haved: + self._bgp_af_list_to_dict(entry) + + # 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": + for k, have in iteritems(haved): + self._delete_af(wantd, have) + wantd = {} + + if self.state == "overridden": + for k, have in iteritems(haved): + if k not in wantd: + self._compare(want={}, have=have) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Bgp_address_family network resource. + """ + self._compare_af(want, have) + self._compare_neighbors(want, have) + # Do the negation first + command_set = [] + for cmd in self.commands: + if cmd not in command_set: + if "delete" in cmd: + command_set.insert(0, cmd) + else: + command_set.append(cmd) + self.commands = command_set + + def _compare_af(self, want, have): + waf = want.get("address_family", {}) + haf = have.get("address_family", {}) + for name, entry in iteritems(waf): + self._compare_lists( + entry, + have=haf.get(name, {}), + as_number=want["as_number"], + afi=name, + ) + for name, entry in iteritems(haf): + if name not in waf.keys() and self.state == "replaced": + continue + self._compare_lists( + {}, entry, as_number=have["as_number"], afi=name + ) + + def _delete_af(self, want, have): + for as_num, entry in iteritems(want): + for afi, af_entry in iteritems(entry.get("address_family", {})): + if have.get("address_family"): + for hafi, hentry in iteritems(have["address_family"]): + if hafi == afi: + self.commands.append( + self._tmplt.render( + { + "as_number": as_num, + "address_family": {"afi": afi}, + }, + "address_family", + True, + ) + ) + for neigh, neigh_entry in iteritems(entry.get("neighbors", {})): + if have.get("neighbors"): + for hneigh, hnentry in iteritems(have["neighbors"]): + if hneigh == neigh: + if not neigh_entry.get("address_family"): + self.commands.append( + self._tmplt.render( + { + "as_number": as_num, + "neighbors": { + "neighbor_address": neigh + }, + }, + "neighbors", + True, + ) + ) + else: + for k in neigh_entry["address_family"].keys(): + if ( + hnentry.get("address_family") + and k + in hnentry["address_family"].keys() + ): + self.commands.append( + self._tmplt.render( + { + "as_number": as_num, + "neighbors": { + "neighbor_address": neigh, + "address_family": { + "afi": k + }, + }, + }, + "neighbors.address_family", + True, + ) + ) + + def _compare_neighbors(self, want, have): + parsers = [ + "neighbors.allowas_in", + "neighbors.as_override", + "neighbors.attribute_unchanged.as_path", + "neighbors.attribute_unchanged.med", + "neighbors.attribute_unchanged.next_hop", + "neighbors.capability_dynamic", + "neighbors.capability_orf", + "neighbors.default_originate", + "neighbors.distribute_list", + "neighbors.prefix_list", + "neighbors.filter_list", + "neighbors.maximum_prefix", + "neighbors.nexthop_local", + "neighbors.nexthop_self", + "neighbors.peer_group", + "neighbors.remove_private_as", + "neighbors.route_map", + "neighbors.route_reflector_client", + "neighbors.route_server_client", + "neighbors.soft_reconfiguration", + "neighbors.unsuppress_map", + "neighbors.weight", + ] + wneigh = want.get("neighbors", {}) + hneigh = have.get("neighbors", {}) + for name, entry in iteritems(wneigh): + for afi, af_entry in iteritems(entry.get("address_family")): + for k, val in iteritems(af_entry): + w = { + "as_number": want["as_number"], + "neighbors": { + "neighbor_address": name, + "address_family": {"afi": afi, k: val}, + }, + } + h = {} + if hneigh.get(name): + if hneigh[name]["address_family"].get(afi): + if hneigh[name]["address_family"][afi].get(k): + h = { + "as_number": want["as_number"], + "neighbors": { + "neighbor_address": name, + "address_family": { + "afi": afi, + k: hneigh[name]["address_family"][ + afi + ].pop(k, {}), + }, + }, + } + self.compare( + parsers=parsers, + want=w, + have=h, + ) + for name, entry in iteritems(hneigh): + if name not in wneigh.keys(): + # remove surplus config for overridden and replaced + if self.state != "replaced": + self.commands.append( + self._tmplt.render( + { + "as_number": have["as_number"], + "neighbors": {"neighbor_address": name}, + }, + "neighbors", + True, + ) + ) + continue + + for hafi, haf_entry in iteritems(entry.get("address_family")): + # remove surplus configs for given neighbor - replace and overridden + for k, val in iteritems(haf_entry): + h = { + "as_number": have["as_number"], + "neighbors": { + "neighbor_address": name, + "address_family": {"afi": hafi, k: val}, + }, + } + self.compare(parsers=parsers, want={}, have=h) + + def _compare_lists(self, want, have, as_number, afi): + parsers = [ + "aggregate_address", + "network.backdoor", + "network.path_limit", + "network.route_map", + "redistribute.metric", + "redistribute.route_map", + "redistribute.table", + ] + for attrib in ["redistribute", "networks", "aggregate_address"]: + wdict = want.pop(attrib, {}) + hdict = have.pop(attrib, {}) + for key, entry in iteritems(wdict): + if entry != hdict.get(key, {}): + self.compare( + parsers=parsers, + want={ + "as_number": as_number, + "address_family": {"afi": afi, attrib: entry}, + }, + have={ + "as_number": as_number, + "address_family": { + "afi": afi, + attrib: hdict.pop(key, {}), + }, + }, + ) + hdict.pop(key, {}) + # remove remaining items in have for replaced + if not wdict and hdict: + attrib = re.sub("_", "-", attrib) + attrib = re.sub("networks", "network", attrib) + self.commands.append( + "delete protocols bgp " + + str(as_number) + + " " + + "address-family " + + afi + + " " + + attrib + ) + hdict = {} + for key, entry in iteritems(hdict): + self.compare( + parsers=parsers, + want={}, + have={ + "as_number": as_number, + "address_family": {"afi": afi, attrib: entry}, + }, + ) + + def _bgp_af_list_to_dict(self, entry): + for name, proc in iteritems(entry): + if "address_family" in proc: + af_dict = {} + for entry in proc.get("address_family"): + if "networks" in entry: + network_dict = {} + for n_entry in entry.get("networks", []): + network_dict.update({n_entry["prefix"]: n_entry}) + entry["networks"] = network_dict + + if "aggregate_address" in entry: + agg_dict = {} + for a_entry in entry.get("aggregate_address", []): + agg_dict.update({a_entry["prefix"]: a_entry}) + entry["aggregate_address"] = agg_dict + + if "redistribute" in entry: + redis_dict = {} + for r_entry in entry.get("redistribute", []): + proto_key = r_entry.get("protocol", "table") + redis_dict.update({proto_key: r_entry}) + entry["redistribute"] = redis_dict + + for af in proc.get("address_family"): + af_dict.update({af["afi"]: af}) + proc["address_family"] = af_dict + + if "neighbors" in proc: + neigh_dict = {} + for entry in proc.get("neighbors", []): + neigh_dict.update({entry["neighbor_address"]: entry}) + proc["neighbors"] = neigh_dict + self._bgp_af_list_to_dict(proc["neighbors"]) diff --git a/plugins/module_utils/network/vyos/facts/bgp_address_family/__init__.py b/plugins/module_utils/network/vyos/facts/bgp_address_family/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py new file mode 100644 index 0000000..a7296f2 --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py @@ -0,0 +1,101 @@ +# -*- 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 bgp_address_family 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. +""" + +import re +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family import ( + Bgp_address_familyTemplate, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_address_family.bgp_address_family import ( + Bgp_address_familyArgs, +) + + +class Bgp_address_familyFacts(object): + """The vyos bgp_address_family facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Bgp_address_familyArgs.argument_spec + + def get_device_data(self, connection): + return connection.get( + 'show configuration commands | match "set protocols bgp"' + ) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Bgp_address_family network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + config_lines = [] + + if not data: + data = self.get_device_data(connection) + + for resource in data.splitlines(): + if "address-family" in resource: + config_lines.append(re.sub("'", "", resource)) + + # parse native config using the Bgp_address_family template + bgp_address_family_parser = Bgp_address_familyTemplate( + lines=config_lines + ) + objs = bgp_address_family_parser.parse() + if objs: + if "address_family" in objs: + objs["address_family"] = list(objs["address_family"].values()) + for af in objs["address_family"]: + if "networks" in af: + af["networks"] = sorted( + af["networks"], key=lambda k: k["prefix"] + ) + if "aggregate_address" in af: + af["aggregate_address"] = sorted( + af["aggregate_address"], key=lambda k: k["prefix"] + ) + if "neighbors" in objs: + objs["neighbors"] = list(objs["neighbors"].values()) + objs["neighbors"] = sorted( + objs["neighbors"], key=lambda k: k["neighbor_address"] + ) + for neigh in objs["neighbors"]: + if "address_family" in neigh: + neigh["address_family"] = list( + neigh["address_family"].values() + ) + + ansible_facts["ansible_network_resources"].pop( + "bgp_address_family", None + ) + + params = utils.remove_empties( + utils.validate_config(self.argument_spec, {"config": objs}) + ) + + facts["bgp_address_family"] = params.get("config", []) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index 95eff2e..1a2d786 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -1,107 +1,111 @@ # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The facts class for vyos this file validates each subset of facts and selectively calls the appropriate facts gathering function """ from __future__ import absolute_import, division, print_function __metaclass__ = type from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts import ( FactsBase, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.interfaces.interfaces import ( InterfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.l3_interfaces.l3_interfaces import ( L3_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lag_interfaces.lag_interfaces import ( Lag_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_global.lldp_global import ( Lldp_globalFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_interfaces.lldp_interfaces import ( Lldp_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules import ( Firewall_rulesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes import ( Static_routesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_global.firewall_global import ( Firewall_globalFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_interfaces.firewall_interfaces import ( Firewall_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv3.ospfv3 import ( Ospfv3Facts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv2.ospfv2 import ( Ospfv2Facts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospf_interfaces.ospf_interfaces import ( Ospf_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_global.bgp_global import ( Bgp_globalFacts, ) +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.legacy.base import ( Default, Neighbors, Config, ) FACT_LEGACY_SUBSETS = dict(default=Default, neighbors=Neighbors, config=Config) FACT_RESOURCE_SUBSETS = dict( interfaces=InterfacesFacts, l3_interfaces=L3_interfacesFacts, lag_interfaces=Lag_interfacesFacts, lldp_global=Lldp_globalFacts, lldp_interfaces=Lldp_interfacesFacts, static_routes=Static_routesFacts, firewall_rules=Firewall_rulesFacts, firewall_global=Firewall_globalFacts, firewall_interfaces=Firewall_interfacesFacts, ospfv3=Ospfv3Facts, ospfv2=Ospfv2Facts, ospf_interfaces=Ospf_interfacesFacts, bgp_global=Bgp_globalFacts, + bgp_address_family=Bgp_address_familyFacts, ) class Facts(FactsBase): """The fact class for vyos""" VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys()) VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys()) def __init__(self, module): super(Facts, self).__init__(module) def get_facts( self, legacy_facts_type=None, resource_facts_type=None, data=None ): """Collect the facts for vyos :param legacy_facts_type: List of legacy facts types :param resource_facts_type: List of resource fact types :param data: previously collected conf :rtype: dict :return: the facts gathered """ if self.VALID_RESOURCE_SUBSETS: self.get_network_resources_facts( FACT_RESOURCE_SUBSETS, resource_facts_type, data ) if self.VALID_LEGACY_GATHER_SUBSETS: self.get_network_legacy_facts( FACT_LEGACY_SUBSETS, legacy_facts_type ) return self.ansible_facts, self._warnings diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py new file mode 100644 index 0000000..55e2200 --- /dev/null +++ b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py @@ -0,0 +1,1421 @@ +# -*- 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 Bgp_address_family 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, +) + + +def _tmplt_bgp_af_aggregate_address(config_data): + afi = config_data["address_family"]["afi"] + "-unicast" + command = "protocols bgp {as_number} address-family ".format(**config_data) + config_data = config_data["address_family"] + if config_data["aggregate_address"].get("as_set"): + command += afi + " aggregate-address {prefix} as-set".format( + **config_data["aggregate_address"] + ) + if config_data["aggregate_address"].get("summary_only"): + command += afi + " aggregate-address {prefix} summary-only".format( + **config_data["aggregate_address"] + ) + return command + + +def _tmplt_bgp_af_redistribute_metric(config_data): + if config_data["address_family"]["redistribute"].get("metric"): + afi = config_data["address_family"]["afi"] + "-unicast" + command = "protocols bgp {as_number} address-family ".format( + **config_data + ) + if config_data["address_family"]["redistribute"].get("metric"): + command += afi + " redistribute {protocol} metric {metric}".format( + **config_data["address_family"]["redistribute"] + ) + return command + + +def _tmplt_bgp_af_redistribute_route_map(config_data): + if config_data["address_family"]["redistribute"].get("route_map"): + afi = config_data["address_family"]["afi"] + "-unicast" + command = "protocols bgp {as_number} address-family ".format( + **config_data + ) + if config_data["address_family"]["redistribute"].get("route_map"): + command += ( + afi + + " redistribute {protocol} route-map {route_map}".format( + **config_data["address_family"]["redistribute"] + ) + ) + return command + + +def _tmplt_bgp_af_redistribute_table(config_data): + if config_data["address_family"]["redistribute"].get("table"): + afi = config_data["address_family"]["afi"] + "-unicast" + command = "protocols bgp {as_number} address-family ".format( + **config_data + ) + if config_data["address_family"]["redistribute"].get("table"): + command += afi + " table {table}".format( + **config_data["address_family"]["redistribute"] + ) + return command + + +def _tmplt_bgp_af_delete_redistribute(config_data): + afi = config_data["address_family"]["afi"] + "-unicast" + command = "protocols bgp {as_number} address-family ".format(**config_data) + config_data = config_data["address_family"] + command += afi + " redistribute {protocol}".format( + **config_data["redistribute"] + ) + return command + + +def _tmplt_bgp_af_neighbor_distribute_list(config_data): + command = [] + afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" + cmd = "protocols bgp {as_number} neighbor ".format(**config_data) + cmd += "{neighbor_address} address-family ".format( + **config_data["neighbors"] + ) + config_data = config_data["neighbors"]["address_family"] + for list_el in config_data["distribute_list"]: + command.append( + cmd + + afi + + " distribute-list " + + list_el["action"] + + " " + + str(list_el["acl"]) + ) + return command + + +def _tmplt_bgp_af_neighbor_route_map(config_data): + command = [] + afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" + cmd = "protocols bgp {as_number} neighbor ".format(**config_data) + cmd += "{neighbor_address} address-family ".format( + **config_data["neighbors"] + ) + config_data = config_data["neighbors"]["address_family"] + for list_el in config_data["route_map"]: + command.append( + cmd + + afi + + " route-map " + + list_el["action"] + + " " + + str(list_el["route_map"]) + ) + return command + + +def _tmplt_bgp_af_neighbor_prefix_list(config_data): + command = [] + afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" + cmd = "protocols bgp {as_number} neighbor ".format(**config_data) + cmd += "{neighbor_address} address-family ".format( + **config_data["neighbors"] + ) + config_data = config_data["neighbors"]["address_family"] + for list_el in config_data["prefix_list"]: + command.append( + cmd + + afi + + " prefix-list " + + list_el["action"] + + " " + + str(list_el["prefix_list"]) + ) + return command + + +def _tmplt_bgp_af_neighbor_filter_list(config_data): + command = [] + afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" + cmd = "protocols bgp {as_number} neighbor ".format(**config_data) + cmd += "{neighbor_address} address-family ".format( + **config_data["neighbors"] + ) + config_data = config_data["neighbors"]["address_family"] + for list_el in config_data["filter_list"]: + command.append( + cmd + + afi + + " filter-list " + + list_el["action"] + + " " + + str(list_el["path_list"]) + ) + return command + + +def _tmplt_bgp_af_neighbor_attribute(config_data): + command = [] + afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" + cmd = "protocols bgp {as_number} neighbor ".format(**config_data) + cmd += "{neighbor_address} address-family ".format( + **config_data["neighbors"] + ) + config_data = config_data["neighbors"]["address_family"] + for k in config_data["attribute_unchanged"].keys(): + if config_data["attribute_unchanged"][k]: + k = re.sub("_", "-", k) + c = cmd + afi + " attribute-unchanged " + k + command.append(c) + return command + + +def _tmplt_bgp_af_neighbor_delete(config_data): + afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" + command = "protocols bgp {as_number} ".format(**config_data) + command += ( + "neighbor {neighbor_address} address-family ".format( + **config_data["neighbors"] + ) + + afi + ) + config_data = config_data["neighbors"]["address_family"] + if config_data.get("allowas_in"): + command += " allowas-in" + elif config_data.get("as_override"): + command += " as-override" + elif config_data.get("attribute_unchanged"): + command += " attribute-unchanged" + elif config_data.get("capability"): + command += " capability" + elif config_data.get("default_originate"): + command += " default-originate" + elif config_data.get("maximum_prefix"): + command += " maximum-prefix" + elif config_data.get("nexthop_local"): + command += " nexthop-local" + elif config_data.get("nexthop_self"): + command += " nexthop-self" + elif config_data.get("peer_group"): + command += " peer-group" + elif config_data.get("remote_private_as"): + command += " remote-private-as" + elif config_data.get("route_reflector_client"): + command += " route-reflector-client" + elif config_data.get("route_server_client"): + command += " route-server-client" + elif config_data.get("soft_reconfiguration"): + command += " soft-reconfiguration" + elif config_data.get("unsuppress_map"): + command += " unsuppress-map" + elif config_data.get("weight"): + command += " weight" + elif config_data.get("filter_list"): + command += " filter-list" + elif config_data.get("prefix_list"): + command += " prefix-list" + elif config_data.get("distribute_list"): + command += " distribute-list" + elif config_data.get("route_map"): + command += " route-map" + return command + + +def _tmplt_bgp_af_neighbor(config_data): + afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" + command = "protocols bgp {as_number} ".format(**config_data) + command += ( + "neighbor {neighbor_address} address-family ".format( + **config_data["neighbors"] + ) + + afi + ) + config_data = config_data["neighbors"]["address_family"] + if config_data.get("allowas_in"): + command += " allowas-in number {allowas_in}".format(**config_data) + elif config_data.get("as_override"): + command += " as-override" + elif config_data.get("capability"): + command += " capability " + if config_data["capability"].get("dynamic"): + command += "dynamic" + elif config_data["capability"].get("orf"): + command += " prefix-list {orf}".format(**config_data["capability"]) + elif config_data.get("default_originate"): + command += " default-originate route-map {default_originate}".format( + **config_data + ) + elif config_data.get("maximum_prefix"): + command += " maximum-prefix {maximum_prefix}".format(**config_data) + elif config_data.get("nexthop_local"): + command += " nexthop-local" + elif config_data.get("nexthop_self"): + command += " nexthop-self" + elif config_data.get("peer_group"): + command += " peer-group {peer_group}".format(**config_data) + elif config_data.get("remote_private_as"): + command += " remote-private-as" + elif config_data.get("route_reflector_client"): + command += " route-reflector-client" + elif config_data.get("route_server_client"): + command += " route-server-client" + elif config_data.get("soft_reconfiguration"): + command += " soft-reconfiguration inbound" + elif config_data.get("unsuppress_map"): + command += " unsuppress-map {unsuppress_map}".format(**config_data) + elif config_data.get("weight"): + command += " weight {weight}".format(**config_data) + return command + + +class Bgp_address_familyTemplate(NetworkTemplate): + def __init__(self, lines=None): + prefix = {"set": "set", "remove": "delete"} + super(Bgp_address_familyTemplate, self).__init__( + lines=lines, tmplt=self, prefix=prefix + ) + + # fmt: off + PARSERS = [ + { + "name": "address_family", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+address-family + \s+(?P\S+)-unicast + *$""", + re.VERBOSE, + ), + "setval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast", + "compval": "as_number", + "result": { + "as_number": "{{ as_num }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + } + } + } + }, + { + "name": "aggregate_address", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+address-family + \s+(?P\S+)-unicast + \s+aggregate-address + \s+(?P
\S+) + \s*(?Pas-set)* + \s*(?Psummary-only)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_aggregate_address, + "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast aggregate-address" + + " {{ address_family.aggregate_address.prefix }}", + "compval": "address_family.aggregate_address", + "result": { + "as_number": "{{ as_num }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "aggregate_address": [ + { + "prefix": "{{ address }}", + "as_set": "{{ True if as_set is defined }}", + "summary_only": "{{ True if summary_only is defined }}" + } + ] + } + } + } + }, + { + "name": "network.backdoor", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+address-family + \s+(?P\S+)-unicast + \s+network + \s+(?P
\S+) + \s+backdoor + *$""", + re.VERBOSE, + ), + "setval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network {{ address_family.networks.prefix }} backdoor", + "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network {{ address_family.networks.prefix }}", + "compval": "address_family.networks.backdoor", + "result": { + "as_number": "{{ as_num }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "networks": [ + { + "prefix": "{{ address }}", + "backdoor": "{{ True }}" + } + ] + } + } + } + }, + { + "name": "network.path_limit", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+address-family + \s+(?P\S+)-unicast + \s+network + \s+(?P
\S+) + \s+path-limit + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network" + + "{{ address_family.networks.prefix }} path-limit {{ address_family.networks.path_limit }}", + "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network {{ address_family.networks.address }}", + "compval": "address_family.networks.path_limit", + "result": { + "as_number": "{{ as_num }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "networks": [ + { + "prefix": "{{ address }}", + "path_limit": "{{ limit|int }}" + } + ] + } + } + } + }, + { + "name": "network.route_map", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+address-family + \s+(?P\S+)-unicast + \s+network + \s+(?P
\S+) + \s+route-map + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network" + + " {{ address_family.networks.prefix }} route-map {{ address_family.networks.route_map }}", + "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network {{ address_family.networks.prefix }}", + "compval": "address_family.networks.route_map", + "result": { + "as_number": "{{ as_num }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "networks": [ + { + "prefix": "{{ address }}", + "route_map": "{{ map }}" + } + ] + } + } + } + }, + { + "name": "redistribute.metric", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+address-family + \s+(?P\S+)-unicast + \s+redistribute + \s+(?P\S+) + \s+metric + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_redistribute_metric, + "remval": _tmplt_bgp_af_delete_redistribute, + "compval": "address_family.redistribute.metric", + "result": { + "as_number": "{{ as_num }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "redistribute": [ + { + "protocol": "{{ proto }}", + "metric": "{{ val|int }}" + } + ] + } + } + } + }, + { + "name": "redistribute.route_map", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+address-family + \s+(?P\S+)-unicast + \s+redistribute + \s+(?P\S+) + \s+route-map + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_redistribute_route_map, + "remval": _tmplt_bgp_af_delete_redistribute, + "compval": "address_family.redistribute.route_map", + "result": { + "as_number": "{{ as_num }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "redistribute": [ + { + "protocol": "{{ proto }}", + "route_map": "{{ map }}" + } + ] + } + } + } + }, + { + "name": "redistribute.table", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+address-family + \s+(?P\S+)-unicast + \s+redistribute + \s+table + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_redistribute_table, + "remval": _tmplt_bgp_af_delete_redistribute, + "compval": "address_family.redistribute.table", + "result": { + "as_number": "{{ as_num }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "redistribute": [ + { + "table": "{{ tab }}" + } + ] + } + } + } + }, + { + "name": "neighbors", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + *$""", + re.VERBOSE, + ), + "setval": "protocols bgp {{ as_number }} neighbor {{ neighbors.neighbor_address }} address-family", + "compval": "neighbors", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + } + } + } + }, + { + "name": "neighbors.address_family", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + *$""", + re.VERBOSE, + ), + "setval": "protocols bgp {{ as_number }} neighbor {{ neighbors.neighbor_address }} address-family {{ neighbors.address_family.afi }}-unicast", + "compval": "neighbors", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + } + } + } + } + } + }, + { + "name": "neighbors.allowas_in", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+allowas-in + \s+number + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.allowas_in", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "allowas_in": "{{ num }}" + } + } + } + } + } + }, + { + "name": "neighbors.as_override", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+as-override + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.as_override", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "as_override": "{{ True }}" + } + } + } + } + } + }, + { + "name": "neighbors.attribute_unchanged.as_path", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+attribute-unchanged + \s+(?Pas-path) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor_attribute, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.attribute_unchanged.as_path", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "attribute_unchanged": { + "as_path": "{{ True }}" + } + } + } + } + } + } + }, + { + "name": "neighbors.attribute_unchanged.med", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+attribute-unchanged + \s+(?Pmed) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor_attribute, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.attribute_unchanged.med", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "attribute_unchanged": { + "med": "{{ True }}" + } + } + } + } + } + } + }, + { + "name": "neighbors.attribute_unchanged.next_hop", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+attribute-unchanged + \s+(?Pnext-hop) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor_attribute, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.attribute_unchanged.next_hop", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "attribute_unchanged": { + "next_hop": "{{ True }}" + } + } + } + } + } + } + }, + { + "name": "neighbors.capability_dynamic", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+capability + \s+dynamic + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.capability.dynamic", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "capability": { + "dynamic": "{{ true }}" + } + } + } + } + } + } + }, + { + "name": "neighbors.capability_orf", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+capability + \s+prefix-list + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.capability.orf", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "capability": { + "orf": "{{ orf }}" + } + } + } + } + } + } + }, + { + "name": "neighbors.default_originate", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+default-originate + \s+route-map + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.default_originate", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "default_originate": "{{ map }}" + } + } + } + } + } + }, + { + "name": "neighbors.distribute_list", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+distribute-list + \s+(?Pexport|import) + \s+(?P\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor_distribute_list, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.distribute_list", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "distribute_list": [ + { + "action": "{{ action }}", + "acl": "{{ list }}" + } + ] + } + } + } + } + } + }, + { + "name": "neighbors.prefix_list", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+prefix-list + \s+(?Pexport|import) + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor_prefix_list, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.prefix_list", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "prefix_list": [ + { + "action": "{{ action }}", + "prefix_list": "{{ list }}" + } + ] + } + } + } + } + } + }, + { + "name": "neighbors.filter_list", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+filter-list + \s+(?Pexport|import) + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor_filter_list, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.filter_list", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "filter_list": [ + { + "action": "{{ action }}", + "path_list": "{{ list }}" + } + ] + } + } + } + } + } + }, + { + "name": "neighbors.maximum_prefix", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+maximum-prefix + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.maximum_prefix", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "maximum_prefix": "{{ num }}" + } + } + } + } + } + }, + { + "name": "neighbors.nexthop_local", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+nexthop-local + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.nexthop_local", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "nexthop_local": "{{ True }}" + } + } + } + } + } + }, + { + "name": "neighbors.nexthop_self", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+nexthop-self + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.nexthop_self", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "nexthop_self": "{{ True }}" + } + } + } + } + } + }, + { + "name": "neighbors.peer_group", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+peer-group + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.peer_group", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "peer_group": "{{ name }}" + } + } + } + } + } + }, + { + "name": "neighbors.remove_private_as", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+remove-private-as + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.remove_private_as", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "remove_private_as": "{{ True }}" + } + } + } + } + } + }, + { + "name": "neighbors.route_map", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+route-map + \s+(?Pexport|import) + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor_route_map, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.route_map", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "route_map": [ + { + "action": "{{ action }}", + "route_map": "{{ map }}" + } + ] + } + } + } + } + } + }, + { + "name": "neighbors.route_reflector_client", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+route-reflector-client + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.route_reflector_client", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "route_reflector_client": "{{ True }}" + } + } + } + } + } + }, + { + "name": "neighbors.route_server_client", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+route-server-client + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.route_server_client", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "route_server_client": "{{ True }}" + } + } + } + } + } + }, + { + "name": "neighbors.soft_reconfiguration", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+soft-reconfiguration + \s+inbound + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.soft_reconfiguration", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "soft_reconfiguration": "{{ True }}" + } + } + } + } + } + }, + { + "name": "neighbors.unsuppress_map", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+unsuppress-map + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.unsuppress_map", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "unsuppress_map": "{{ map }}" + } + } + } + } + } + }, + { + "name": "neighbors.weight", + "getval": re.compile( + r""" + ^set + \s+protocols + \s+bgp + \s+(?P\d+) + \s+neighbor + \s+(?P
\S+) + \s+address-family + \s+(?P\S+)-unicast + \s+weight + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_af_neighbor, + "remval": _tmplt_bgp_af_neighbor_delete, + "compval": "neighbors.address_family.weight", + "result": { + "as_number": "{{ as_num }}", + "neighbors": { + "{{ address }}": { + "neighbor_address": "{{ address }}", + "address_family": { + "{{ afi }}": { + "afi": "{{ afi }}", + "weight": "{{ num }}" + } + } + } + } + } + }, + ] + # fmt: on diff --git a/plugins/modules/vyos_bgp_address_family.py b/plugins/modules/vyos_bgp_address_family.py new file mode 100644 index 0000000..80c6807 --- /dev/null +++ b/plugins/modules/vyos_bgp_address_family.py @@ -0,0 +1,1192 @@ +#!/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) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for vyos_bgp_address_family +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +module: vyos_bgp_address_family +version_added: 2.1.0 +short_description: BGP Address Family Resource Module. +description: +- This module manages BGP address family configuration of interfaces on devices running VYOS. +author: Gomathi Selvi Srinivasan (@GomathiselviS) +options: + config: + description: A dict of BGP global configuration for interfaces. + type: dict + suboptions: + as_number: + description: + - AS number. + type: int + address_family: + description: BGP address-family parameters. + type: list + elements: dict + suboptions: + afi: + description: BGP address family settings. + type: str + choices: ['ipv4', 'ipv6'] + aggregate_address: + description: + - BGP aggregate network. + type: list + elements: dict + suboptions: + prefix: + description: BGP aggregate network. + type: str + as_set: + description: Generate AS-set path information for this aggregate address. + type: bool + summary_only: + description: Announce the aggregate summary network only. + type: bool + networks: + description: BGP network + type: list + elements: dict + suboptions: + prefix: + description: BGP network address + type: str + path_limit: + description: AS path hop count limit + type: int + backdoor: + description: Network as a backdoor route. + type: bool + route_map: + description: Route-map to modify route attributes + type: str + redistribute: + description: Redistribute routes from other protocols into BGP + type: list + elements: dict + suboptions: + protocol: + description: types of routes to be redistributed. + type: str + choices: ['connected', 'kernel', 'ospf', 'ospfv3', 'rip', 'ripng', 'static'] + table: + description: Redistribute non-main Kernel Routing Table. + type: str + route_map: + description: Route map to filter redistributed routes + type: str + metric: + description: Metric for redistributed routes. + type: int + neighbors: + description: BGP neighbor + type: list + elements: dict + suboptions: + neighbor_address: + description: BGP neighbor address (v4/v6). + type: str + address_family: + description: address family. + type: list + elements: dict + suboptions: + afi: + description: BGP neighbor parameters. + type: str + choices: ['ipv4', 'ipv6'] + allowas_in: + description: Number of occurrences of AS number. + type: int + as_override: + description: AS for routes sent to this neighbor to be the local AS. + type: bool + attribute_unchanged: + description: BGP attributes are sent unchanged. + type: dict + suboptions: + as_path: + description: as_path attribute + type: bool + med: + description: med attribute + type: bool + next_hop: + description: next_hop attribute + type: bool + capability: + description: Advertise capabilities to this neighbor. + type: dict + suboptions: + dynamic: + description: Advertise dynamic capability to this neighbor. + type: bool + orf: + description: Advertise ORF capability to this neighbor. + type: str + choices: ['send', 'receive'] + default_originate: + description: Send default route to this neighbor + type: str + distribute_list: + description: Access-list to filter route updates to/from this neighbor. + type: list + elements: dict + suboptions: + action: + description: Access-list to filter outgoing/incoming route updates to this neighbor + type: str + choices: ['export', 'import'] + acl: + description: Acess-list number. + type: int + filter_list: + description: As-path-list to filter route updates to/from this neighbor. + type: list + elements: dict + suboptions: + action: + description: filter outgoing/incoming route updates + type: str + choices: ['export', 'import'] + path_list: + description: As-path-list to filter + type: str + maximum_prefix: + description: Maximum number of prefixes to accept from this neighbor + nexthop-self Nexthop for routes sent to this neighbor to be the local router. + type: int + nexthop_local: + description: Nexthop attributes. + type: bool + nexthop_self: + description: Nexthop for routes sent to this neighbor to be the local router. + type: bool + peer_group: + description: IPv4 peer group for this peer + type: str + prefix_list: + description: Prefix-list to filter route updates to/from this neighbor. + type: list + elements: dict + suboptions: + action: + description: filter outgoing/incoming route updates + type: str + choices: ['export', 'import'] + prefix_list: + description: Prefix-list to filter + type: str + remove_private_as: + description: Remove private AS numbers from AS path in outbound route updates + type: bool + route_map: + description: Route-map to filter route updates to/from this neighbor. + type: list + elements: dict + suboptions: + action: + description: filter outgoing/incoming route updates + type: str + choices: ['export', 'import'] + route_map: + description: route-map to filter + type: str + route_reflector_client: + description: Neighbor as a route reflector client + type: bool + route_server_client: + description: Neighbor is route server client + type: bool + soft_reconfiguration: + description: Soft reconfiguration for neighbor + type: bool + unsupress_map: + description: Route-map to selectively unsuppress suppressed routes + type: str + weight: + description: Default weight for routes from this neighbor + type: int + running_config: + type: str + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by + executing the command B(show configuration command | match bgp). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + state: + description: + - The state the configuration should be left in. + type: str + choices: + - merged + - replaced + - deleted + - gathered + - parsed + - rendered + - purged + - overridden + default: merged +""" +EXAMPLES = """ +# Using merged +# Before state +# vyos@vyos:~$ show configuration commands | match "set protocols bgp" +# vyos@vyos:~$ + + - name: Merge provided configuration with device configuration + vyos.vyos.vyos_bgp_address_family: + config: + as_number: "100" + address_family: + - afi: "ipv4" + redistribute: + - protocol: "static" + metric: 50 + neighbors: + - neighbor_address: "20.33.1.1/24" + address_family: + - afi: "ipv4" + allowas_in: 4 + as_override: True + attribute_unchanged: + med: True + - afi: "ipv6" + default_originate: "map01" + distribute_list: + - action: "export" + acl: 10 + - neighbor_address: "100.11.34.12" + address_family: + - afi: "ipv4" + maximum_prefix: 45 + nexthop_self: True + route_map: + - action: "export" + route_map: "map01" + - action: "import" + route_map: "map01" + weight: 50 + +# After State: +# vyos@vyos:~$ show configuration commands | match "set protocols bgp" +# set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map 'map01' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export '10' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix '45' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export 'map01' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import 'map01' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight '50' +# vyos@vyos:~$ +# +# Module Execution: +# +# "after": { +# "address_family": [ +# { +# "afi": "ipv4", +# "redistribute": [ +# { +# "metric": 50, +# "protocol": "static" +# } +# ] +# } +# ], +# "as_number": 100, +# "neighbors": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "maximum_prefix": 45, +# "nexthop_self": true, +# "route_map": [ +# { +# "action": "export", +# "route_map": "map01" +# }, +# { +# "action": "import", +# "route_map": "map01" +# } +# ], +# "weight": 50 +# } +# ], +# "neighbor_address": "100.11.34.12" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "allowas_in": 4, +# "as_override": true, +# "attribute_unchanged": { +# "med": true +# } +# }, +# { +# "afi": "ipv6", +# "default_originate": "map01", +# "distribute_list": [ +# { +# "acl": 10, +# "action": "export" +# } +# ] +# } +# ], +# "neighbor_address": "20.33.1.1/24" +# } +# ] +# }, +# "before": {}, +# "changed": true, +# "commands": [ +# "set protocols bgp 100 address-family ipv4-unicast redistribute static metric 50", +# "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number 4", +# "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override", +# "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med", +# "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map map01", +# "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export 10", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix 45", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export map01", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import map01", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight 50" +# ], +# + +# Using replaced: + +# Before state: + +# vyos@vyos:~$ show configuration commands | match "set protocols bgp" +# set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map 'map01' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export '10' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix '45' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export 'map01' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import 'map01' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight '50' +# vyos@vyos:~$ + + - name: Replace provided configuration with device configuration + vyos.vyos.vyos_bgp_address_family: + config: + as_number: "100" + neighbors: + - neighbor_address: "100.11.34.12" + address_family: + - afi: "ipv4" + allowas_in: 4 + as_override: True + attribute_unchanged: + med: True + - afi: "ipv6" + default_originate: "map01" + distribute_list: + - action: "export" + acl: 10 + - neighbor_address: "20.33.1.1/24" + address_family: + - afi: "ipv6" + maximum_prefix: 45 + nexthop_self: True + + state: replaced + +# After State: + +# vyos@vyos:~$ show configuration commands | match "set protocols bgp" +# set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast maximum-prefix '45' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast nexthop-self +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast allowas-in number '4' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast as-override +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged med +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast default-originate route-map 'map01' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast distribute-list export '10' +# vyos@vyos:~$ +# +# +# # Module Execution: +# "after": { +# "address_family": [ +# { +# "afi": "ipv4", +# "redistribute": [ +# { +# "metric": 50, +# "protocol": "static" +# } +# ] +# } +# ], +# "as_number": 100, +# "neighbors": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "allowas_in": 4, +# "as_override": true, +# "attribute_unchanged": { +# "med": true +# } +# }, +# { +# "afi": "ipv6", +# "default_originate": "map01", +# "distribute_list": [ +# { +# "acl": 10, +# "action": "export" +# } +# ] +# } +# ], +# "neighbor_address": "100.11.34.12" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4" +# }, +# { +# "afi": "ipv6", +# "maximum_prefix": 45, +# "nexthop_self": true +# } +# ], +# "neighbor_address": "20.33.1.1/24" +# } +# ] +# }, +# "before": { +# "address_family": [ +# { +# "afi": "ipv4", +# "redistribute": [ +# { +# "metric": 50, +# "protocol": "static" +# } +# ] +# } +# ], +# "as_number": 100, +# "neighbors": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "maximum_prefix": 45, +# "nexthop_self": true, +# "route_map": [ +# { +# "action": "export", +# "route_map": "map01" +# }, +# { +# "action": "import", +# "route_map": "map01" +# } +# ], +# "weight": 50 +# } +# ], +# "neighbor_address": "100.11.34.12" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "allowas_in": 4, +# "as_override": true, +# "attribute_unchanged": { +# "med": true +# } +# }, +# { +# "afi": "ipv6", +# "default_originate": "map01", +# "distribute_list": [ +# { +# "acl": 10, +# "action": "export" +# } +# ] +# } +# ], +# "neighbor_address": "20.33.1.1/24" +# } +# ] +# }, +# "changed": true, +# "commands": [ +# "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list", +# "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate", +# "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged", +# "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override", +# "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in", +# "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight", +# "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map", +# "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self", +# "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast allowas-in number 4", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast as-override", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged med", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast default-originate route-map map01", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast distribute-list export 10", +# "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast maximum-prefix 45", +# "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast nexthop-self" +# ], + + +# Using overridden +# vyos@vyos:~$ show configuration commands | match "set protocols bgp" +# set protocols bgp 100 address-family ipv4-unicast network 35.1.1.0/24 backdoor +# set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50' +# set protocols bgp 100 address-family ipv6-unicast aggregate-address 6601:1:1:1::/64 summary-only +# set protocols bgp 100 address-family ipv6-unicast network 5001:1:1:1::/64 route-map 'map01' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast maximum-prefix '45' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast nexthop-self +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast allowas-in number '4' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast as-override +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged med +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast default-originate route-map 'map01' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast distribute-list export '10' +# vyos@vyos:~$ + + - name: Override + vyos.vyos.vyos_bgp_address_family: + config: + as_number: "100" + neighbors: + - neighbor_address: "100.11.34.12" + address_family: + - afi: "ipv6" + maximum_prefix: 45 + nexthop_self: True + route_map: + - action: "import" + route_map: "map01" + address_family: + - afi: "ipv4" + aggregate_address: + - prefix: "60.9.2.0/24" + summary_only: True + - afi: "ipv6" + redistribute: + - protocol: "static" + metric: 50 + state: overridden + +# Aft=validate-moduleser State + +# vyos@vyos:~$ show configuration commands | match "set protocols bgp" +# set protocols bgp 100 address-family ipv4-unicast aggregate-address 60.9.2.0/24 summary-only +# set protocols bgp 100 address-family ipv6-unicast redistribute static metric '50' +# set protocols bgp 100 neighbor 20.33.1.1/24 +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast maximum-prefix '45' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast nexthop-self +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast route-map import 'map01' +# vyos@vyos:~$ + + +# Module Execution: + +# "after": { +# "address_family": [ +# { +# "afi": "ipv4", +# "aggregate_address": [ +# { +# "prefix": "60.9.2.0/24", +# "summary_only": true +# } +# ] +# }, +# { +# "afi": "ipv6", +# "redistribute": [ +# { +# "metric": 50, +# "protocol": "static" +# } +# ] +# } +# ], +# "as_number": 100, +# "neighbors": [ +# { +# "address_family": [ +# { +# "afi": "ipv4" +# }, +# { +# "afi": "ipv6", +# "maximum_prefix": 45, +# "nexthop_self": true, +# "route_map": [ +# { +# "action": "import", +# "route_map": "map01" +# } +# ] +# } +# ], +# "neighbor_address": "100.11.34.12" +# } +# ] +# }, +# "before": { +# "address_family": [ +# { +# "afi": "ipv4", +# "networks": [ +# { +# "backdoor": true, +# "prefix": "35.1.1.0/24" +# } +# ], +# "redistribute": [ +# { +# "metric": 50, +# "protocol": "static" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "aggregate_address": [ +# { +# "prefix": "6601:1:1:1::/64", +# "summary_only": true +# } +# ], +# "networks": [ +# { +# "prefix": "5001:1:1:1::/64", +# "route_map": "map01" +# } +# ] +# } +# ], +# "as_number": 100, +# "neighbors": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "allowas_in": 4, +# "as_override": true, +# "attribute_unchanged": { +# "med": true +# } +# }, +# { +# "afi": "ipv6", +# "default_originate": "map01", +# "distribute_list": [ +# { +# "acl": 10, +# "action": "export" +# } +# ] +# } +# ], +# "neighbor_address": "100.11.34.12" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4" +# }, +# { +# "afi": "ipv6", +# "maximum_prefix": 45, +# "nexthop_self": true +# } +# ], +# "neighbor_address": "20.33.1.1/24" +# } +# ] +# }, +# "changed": true, +# "commands": [ +# "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family", +# "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast distribute-list", +# "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast default-originate", +# "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged", +# "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast as-override", +# "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast allowas-in", +# "delete protocols bgp 100 address-family ipv6 aggregate-address", +# "delete protocols bgp 100 address-family ipv6 network", +# "delete protocols bgp 100 address-family ipv4 network", +# "delete protocols bgp 100 address-family ipv4 redistribute", +# "set protocols bgp 100 address-family ipv4-unicast aggregate-address 60.9.2.0/24 summary-only", +# "set protocols bgp 100 address-family ipv6-unicast redistribute static metric 50", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast maximum-prefix 45", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast nexthop-self", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast route-map import map01" +# ], +# + +# Using deleted: + +# Before State: + +# vyos@vyos:~$ show configuration commands | match "set protocols bgp" +# set protocols bgp 100 address-family ipv4-unicast aggregate-address 60.9.2.0/24 summary-only +# set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50' +# set protocols bgp 100 address-family ipv6-unicast redistribute static metric '50' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map 'map01' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export '10' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix '45' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export 'map01' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import 'map01' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight '50' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast maximum-prefix '45' +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast nexthop-self +# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast route-map import 'map01' +# vyos@vyos:~$ + + - name: Delete + vyos.vyos.vyos_bgp_address_family: + config: + as_number: "100" + neighbors: + - neighbor_address: "20.33.1.1/24" + address_family: + - afi: "ipv6" + - neighbor_address: "100.11.34.12" + address_family: + - afi: "ipv4" + state: deleted + + +# After State: + +# vyos@vyos:~$ show configuration commands | match "set protocols bgp" +# set protocols bgp 100 address-family ipv6-unicast redistribute static metric '50' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med +# set protocols bgp 100 neighbor 100.11.34.12 +# vyos@vyos:~$ +# +# +# Module Execution: +# +# "after": { +# "address_family": [ +# { +# "afi": "ipv6", +# "redistribute": [ +# { +# "metric": 50, +# "protocol": "static" +# } +# ] +# } +# ], +# "as_number": 100, +# "neighbors": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "allowas_in": 4, +# "as_override": true, +# "attribute_unchanged": { +# "med": true +# } +# } +# ], +# "neighbor_address": "20.33.1.1/24" +# } +# ] +# }, +# "before": { +# "address_family": [ +# { +# "afi": "ipv4", +# "aggregate_address": [ +# { +# "prefix": "60.9.2.0/24", +# "summary_only": true +# } +# ], +# "redistribute": [ +# { +# "metric": 50, +# "protocol": "static" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "redistribute": [ +# { +# "metric": 50, +# "protocol": "static" +# } +# ] +# } +# ], +# "as_number": 100, +# "neighbors": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "maximum_prefix": 45, +# "nexthop_self": true, +# "route_map": [ +# { +# "action": "export", +# "route_map": "map01" +# }, +# { +# "action": "import", +# "route_map": "map01" +# } +# ], +# "weight": 50 +# }, +# { +# "afi": "ipv6", +# "maximum_prefix": 45, +# "nexthop_self": true, +# "route_map": [ +# { +# "action": "import", +# "route_map": "map01" +# } +# ] +# } +# ], +# "neighbor_address": "100.11.34.12" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "allowas_in": 4, +# "as_override": true, +# "attribute_unchanged": { +# "med": true +# } +# }, +# { +# "afi": "ipv6", +# "default_originate": "map01", +# "distribute_list": [ +# { +# "acl": 10, +# "action": "export" +# } +# ] +# } +# ], +# "neighbor_address": "20.33.1.1/24" +# } +# ] +# }, +# "changed": true, +# "commands": [ +# "delete protocols bgp 100 address-family ipv4-unicast", +# "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast", +# "delete protocols bgp 100 neighbor 100.11.34.12 address-family" +# ], +# + +# using parsed: + +# parsed.cfg +# set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-set +# set protocols bgp 65536 address-family ipv4-unicast network 192.1.13.0/24 route-map 'map01' +# set protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24 backdoor +# set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric '20' +# set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast route-map export 'map01' +# set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration inbound +# set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop + + + - name: parse configs + vyos.vyos.vyos_bgp_address_family: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed + +# Module Execution: +# "parsed": { +# "address_family": [ +# { +# "afi": "ipv4", +# "aggregate_address": [ +# { +# "as_set": true, +# "prefix": "192.0.2.0/24" +# } +# ], +# "networks": [ +# { +# "prefix": "192.1.13.0/24", +# "route_map": "map01" +# }, +# { +# "backdoor": true, +# "prefix": "192.2.13.0/24" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "redistribute": [ +# { +# "metric": 20, +# "protocol": "ripng" +# } +# ] +# } +# ], +# "as_number": 65536, +# "neighbors": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "route_map": [ +# { +# "action": "export", +# "route_map": "map01" +# } +# ], +# "soft_reconfiguration": true +# } +# ], +# "neighbor_address": "192.0.2.25" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv6", +# "attribute_unchanged": { +# "next_hop": true +# } +# } +# ], +# "neighbor_address": "203.0.113.5" +# } +# ] +# + +# Using gathered: + +# Native config: + +# vyos@vyos:~$ show configuration commands | match "set protocols bgp" +# set protocols bgp 100 address-family ipv4-unicast network 35.1.1.0/24 backdoor +# set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50' +# set protocols bgp 100 address-family ipv6-unicast aggregate-address 6601:1:1:1::/64 summary-only +# set protocols bgp 100 address-family ipv6-unicast network 5001:1:1:1::/64 route-map 'map01' +# set protocols bgp 100 address-family ipv6-unicast redistribute static metric '50' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override +# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med +# set protocols bgp 100 neighbor 100.11.34.12 + + - name: gather configs + vyos.vyos.vyos_bgp_address_family: + state: gathered + +# Module Execution: + +# "gathered": { +# "address_family": [ +# { +# "afi": "ipv4", +# "networks": [ +# { +# "backdoor": true, +# "prefix": "35.1.1.0/24" +# } +# ], +# "redistribute": [ +# { +# "metric": 50, +# "protocol": "static" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "aggregate_address": [ +# { +# "prefix": "6601:1:1:1::/64", +# "summary_only": true +# } +# ], +# "networks": [ +# { +# "prefix": "5001:1:1:1::/64", +# "route_map": "map01" +# } +# ], +# "redistribute": [ +# { +# "metric": 50, +# "protocol": "static" +# } +# ] +# } +# ], +# "as_number": 100, +# "neighbors": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "allowas_in": 4, +# "as_override": true, +# "attribute_unchanged": { +# "med": true +# } +# } +# ], +# "neighbor_address": "20.33.1.1/24" +# } +# ] + +# Using rendered: + + - name: Render + vyos.vyos.vyos_bgp_address_family: + config: + as_number: "100" + address_family: + - afi: "ipv4" + redistribute: + - protocol: "static" + metric: 50 + neighbors: + - neighbor_address: "20.33.1.1/24" + address_family: + - afi: "ipv4" + allowas_in: 4 + as_override: True + attribute_unchanged: + med: True + - afi: "ipv6" + default_originate: "map01" + distribute_list: + - action: "export" + acl: 10 + - neighbor_address: "100.11.34.12" + address_family: + - afi: "ipv4" + maximum_prefix: 45 + nexthop_self: True + route_map: + - action: "export" + route_map: "map01" + - action: "import" + route_map: "map01" + weight: 50 + state: rendered + +# Module Execution: + +# "rendered": [ +# "set protocols bgp 100 address-family ipv4-unicast redistribute static metric 50", +# "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number 4", +# "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override", +# "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med", +# "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map map01", +# "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export 10", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix 45", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export map01", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import map01", +# "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight 50" +# ] + + +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_address_family.bgp_address_family import ( + Bgp_address_familyArgs, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_address_family.bgp_address_family import ( + Bgp_address_family, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Bgp_address_familyArgs.argument_spec, + mutually_exclusive=[], + required_if=[], + supports_check_mode=False, + ) + + result = Bgp_address_family(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/vyos_bgp_address_family/defaults/main.yaml b/tests/integration/targets/vyos_bgp_address_family/defaults/main.yaml new file mode 100644 index 0000000..852a6be --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: '[^_].*' +test_items: [] diff --git a/tests/integration/targets/vyos_bgp_address_family/meta/main.yaml b/tests/integration/targets/vyos_bgp_address_family/meta/main.yaml new file mode 100644 index 0000000..7413320 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/meta/main.yaml @@ -0,0 +1,3 @@ +--- +dependencies: + - prepare_vyos_tests diff --git a/tests/integration/targets/vyos_bgp_address_family/tasks/cli.yaml b/tests/integration/targets/vyos_bgp_address_family/tasks/cli.yaml new file mode 100644 index 0000000..93eb2fe --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/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_bgp_address_family/tasks/main.yaml b/tests/integration/targets/vyos_bgp_address_family/tasks/main.yaml new file mode 100644 index 0000000..b957d2f --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tasks/main.yaml @@ -0,0 +1,4 @@ +--- +- include: cli.yaml + tags: + - network_cli diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/_parsed_config.cfg b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_parsed_config.cfg new file mode 100644 index 0000000..2aecc80 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_parsed_config.cfg @@ -0,0 +1,9 @@ +set policy access-list 20 description 'acl20' +set policy access-list 40 description 'acl40' +set policy route-map map01 description 'map01' +set protocols bgp 65536 address-family ipv6-unicast aggregate-address 5000:1:1::/64 summary-only +set protocols bgp 65536 address-family ipv6-unicast network 21e0:1:1::/64 route-map 'map01' +set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric '20' +set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast route-map export 'map01' +set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast soft-reconfiguration inbound +set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/_populate.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_populate.yaml new file mode 100644 index 0000000..3aca916 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_populate.yaml @@ -0,0 +1,17 @@ +--- +- name: setup + vyos.vyos.vyos_config: + lines: + - set policy access-list 20 description 'acl20' + - set policy access-list 40 description 'acl40' + - set policy access-list6 10 description 'acl10' + - set policy route-map map01 description 'map01' + - set protocols bgp 65536 address-family ipv6-unicast aggregate-address 5000:1:1::/64 summary-only + - set protocols bgp 65536 address-family ipv6-unicast network 21e0:1:1::/64 route-map 'map01' + - set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric '20' + - set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast route-map export 'map01' + - set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast soft-reconfiguration inbound + - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop + ignore_errors: true + vars: + ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/_preconfig.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_preconfig.yaml new file mode 100644 index 0000000..5024a70 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_preconfig.yaml @@ -0,0 +1,11 @@ +--- +- name: setup + vyos.vyos.vyos_config: + lines: + - set policy access-list 20 description 'acl20' + - set policy access-list 40 description 'acl40' + - set policy access-list6 10 description 'acl10' + - set policy route-map map01 description 'map01' + ignore_errors: true + vars: + ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/_remove_config.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_remove_config.yaml new file mode 100644 index 0000000..39d8bd0 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_remove_config.yaml @@ -0,0 +1,12 @@ +--- +- name: Remove pre-existing bgp processes + vyos.vyos.vyos_config: + lines: + - delete protocols bgp 65536 + - delete policy access-list 20 + - delete policy access-list 40 + - delete policy access-list6 10 + - delete policy route-map map01 + ignore_errors: true + vars: + ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/deleted.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/deleted.yaml new file mode 100644 index 0000000..7fb8e10 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/deleted.yaml @@ -0,0 +1,49 @@ +--- +- debug: + msg: START vyos_bgp_address_family 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_bgp_address_family: &id001 + config: + as_number: "65536" + address_family: + - afi: "ipv6" + neighbors: + - neighbor_address: "203.0.113.5" + - neighbor_address: "192.0.2.25" + address_family: + - afi: "ipv6" + state: deleted + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: bgp_address_family + + - assert: + that: + - result.commands|length == 3 + - result.changed == true + - result.commands|symmetric_difference(deleted.commands) == [] + - result.after == ansible_facts['network_resources']['bgp_address_family'] + + - name: Delete the existing configuration with the provided running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_bgp_address_family: *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_bgp_address_family/tests/cli/empty_config.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/empty_config.yaml new file mode 100644 index 0000000..3a12be9 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/empty_config.yaml @@ -0,0 +1,60 @@ +--- +- debug: + msg: START vyos_bgp_address_family 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_bgp_address_family: + 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_bgp_address_family: + 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_bgp_address_family: + 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_bgp_address_family: + 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_bgp_address_family: + 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_bgp_address_family/tests/cli/gathered.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/gathered.yaml new file mode 100644 index 0000000..7176a48 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/gathered.yaml @@ -0,0 +1,24 @@ +--- +- debug: + msg: START vyos_bgp_address_family 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_bgp_address_family: + state: gathered + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: bgp_address_family + + - assert: + that: + - result.changed == false + - result.gathered == ansible_facts['network_resources']['bgp_address_family'] diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/merged.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/merged.yaml new file mode 100644 index 0000000..45870c6 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/merged.yaml @@ -0,0 +1,70 @@ +--- +- debug: + msg: START vyos_bgp_address_family merged integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _preconfig.yaml + +- block: + + - name: Merge the provided configuration with the exisiting running configuration + register: result + vyos.vyos.vyos_bgp_address_family: &id001 + config: + as_number: "65536" + address_family: + - afi: "ipv6" + redistribute: + - protocol: "static" + metric: 50 + neighbors: + - neighbor_address: "203.0.113.5" + address_family: + - afi: "ipv6" + allowas_in: 4 + attribute_unchanged: + med: true + default_originate: "map01" + distribute_list: + - action: "export" + acl: 10 + - neighbor_address: "192.0.2.25" + address_family: + - afi: "ipv6" + maximum_prefix: 45 + nexthop_self: true + route_map: + - action: "export" + route_map: "map01" + - action: "import" + route_map: "map01" + state: merged + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: bgp_address_family + + - assert: + that: + - result.commands|length == 9 + - result.changed == true + - result.commands|symmetric_difference(merged.commands) == [] + - result.after == ansible_facts['network_resources']['bgp_address_family'] + - result.before == {} + - result.after == merged.after + + - name: Merge the provided configuration with the existing running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_bgp_address_family: *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_bgp_address_family/tests/cli/overridden.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/overridden.yaml new file mode 100644 index 0000000..77ba5b4 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/overridden.yaml @@ -0,0 +1,58 @@ +--- +- debug: + msg: START vyos_bgp_address_family overridden integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Replace the existing configuration with the provided running configuration + register: result + vyos.vyos.vyos_bgp_address_family: &id001 + config: + as_number: "65536" + address_family: + - afi: "ipv6" + redistribute: + - protocol: "static" + metric: 50 + neighbors: + - neighbor_address: "203.0.113.5" + address_family: + - afi: "ipv6" + allowas_in: 4 + attribute_unchanged: + med: true + default_originate: "map01" + distribute_list: + - action: "export" + acl: 10 + state: overridden + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: bgp_address_family + + - assert: + that: + - result.commands|length == 10 + - result.changed == true + - result.commands|symmetric_difference(overridden.commands) == [] + - result.after == ansible_facts['network_resources']['bgp_address_family'] + + - name: Replace the existing configuration with the provided running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_bgp_address_family: *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_bgp_address_family/tests/cli/parsed.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/parsed.yaml new file mode 100644 index 0000000..d20684f --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/parsed.yaml @@ -0,0 +1,16 @@ +--- +- debug: + msg: START vyos_bgp_address_family 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_bgp_address_family: + running_config: "{{ lookup('file', '_parsed_config.cfg') }}" + state: parsed + +- assert: + that: + - result.changed == false + - result.parsed == populate.config diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/rendered.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/rendered.yaml new file mode 100644 index 0000000..1071657 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/rendered.yaml @@ -0,0 +1,44 @@ +--- +- debug: + msg: START vyos_bgp_address_family rendered integration tests on connection={{ + ansible_connection }} + +- block: + + - name: Render the given config in the form of native config. + register: result + vyos.vyos.vyos_bgp_address_family: &id001 + config: + as_number: "65536" + address_family: + - afi: "ipv6" + redistribute: + - protocol: "static" + metric: 50 + neighbors: + - neighbor_address: "203.0.113.5" + address_family: + - afi: "ipv6" + allowas_in: 4 + attribute_unchanged: + med: true + default_originate: "map01" + distribute_list: + - action: "export" + acl: 10 + - neighbor_address: "192.0.2.25" + address_family: + - afi: "ipv6" + maximum_prefix: 45 + nexthop_self: true + route_map: + - action: "export" + route_map: "map01" + - action: "import" + route_map: "map01" + state: rendered + + - assert: + that: + - result.changed == false + - result.rendered|symmetric_difference(merged.commands) == [] diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/replaced.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/replaced.yaml new file mode 100644 index 0000000..001c997 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/replaced.yaml @@ -0,0 +1,58 @@ +--- +- debug: + msg: START vyos_bgp_address_family replaced integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Replace the existing configuration with the provided running configuration + register: result + vyos.vyos.vyos_bgp_address_family: &id001 + config: + as_number: "65536" + address_family: + - afi: "ipv6" + redistribute: + - protocol: "static" + metric: 50 + neighbors: + - neighbor_address: "203.0.113.5" + address_family: + - afi: "ipv6" + allowas_in: 4 + attribute_unchanged: + med: true + default_originate: "map01" + distribute_list: + - action: "export" + acl: 10 + state: replaced + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: bgp_address_family + + - assert: + that: + - result.commands|length == 9 + - result.changed == true + - result.commands|symmetric_difference(replaced.commands) == [] + - result.after == ansible_facts['network_resources']['bgp_address_family'] + + - name: Replace the existing configuration with the provided running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_bgp_address_family: *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_bgp_address_family/tests/cli/rtt.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/rtt.yaml new file mode 100644 index 0000000..3ef93c4 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/rtt.yaml @@ -0,0 +1,90 @@ +--- +- debug: + msg: START vyos_bgp_address_family rtt integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _preconfig.yaml + +- block: + + - name: Merge the provided configuration with the exisiting running configuration + register: baseconfig + vyos.vyos.vyos_bgp_address_family: + config: + as_number: "65536" + address_family: + - afi: "ipv6" + redistribute: + - protocol: "static" + metric: 50 + neighbors: + - neighbor_address: "203.0.113.5" + address_family: + - afi: "ipv6" + allowas_in: 4 + attribute_unchanged: + med: true + default_originate: "map01" + distribute_list: + - action: "export" + acl: 10 + - neighbor_address: "192.0.2.25" + address_family: + - afi: "ipv6" + maximum_prefix: 45 + nexthop_self: true + route_map: + - action: "export" + route_map: "map01" + - action: "import" + route_map: "map01" + state: merged + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: bgp_address_family + + - assert: + that: + - baseconfig.commands|length == 9 + - baseconfig.changed == true + - baseconfig.commands|symmetric_difference(merged.commands) == [] + - baseconfig.after == ansible_facts['network_resources']['bgp_address_family'] + + - name: Apply the provided configuration (config to be reverted) + become: true + register: result + vyos.vyos.vyos_bgp_address_family: + config: + as_number: "65536" + address_family: + - afi: "ipv6" + aggregate_address: + - summary_only: true + prefix: "21e0:1:1::/64" + networks: + - prefix: "21e0:1:1::/64" + route_map: "map01" + neighbors: + - address_family: + - afi: "ipv6" + remove_private_as: true + neighbor_address: "203.0.113.5" + + - name: Revert back to base config using facts round trip + become: true + register: revert + vyos.vyos.vyos_bgp_address_family: + config: "{{ ansible_facts['network_resources']['bgp_address_family'] }}" + 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_bgp_address_family/vars/main.yaml b/tests/integration/targets/vyos_bgp_address_family/vars/main.yaml new file mode 100644 index 0000000..aed6de6 --- /dev/null +++ b/tests/integration/targets/vyos_bgp_address_family/vars/main.yaml @@ -0,0 +1,99 @@ +--- +merged: + commands: + - set protocols bgp 65536 address-family ipv6-unicast redistribute static metric 50 + - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast allowas-in number 4 + - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged med + - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast default-originate route-map map01 + - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast distribute-list export 10 + - set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast maximum-prefix 45 + - set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast nexthop-self + - set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast route-map export map01 + - set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast route-map import map01 + after: + address_family: + - afi: "ipv6" + redistribute: + - metric: 50 + protocol: "static" + as_number: 65536 + neighbors: + - address_family: + - afi: "ipv6" + maximum_prefix: 45 + nexthop_self: true + route_map: + - action: "export" + route_map: "map01" + - action: "import" + route_map: "map01" + neighbor_address: "192.0.2.25" + - address_family: + - afi: "ipv6" + allowas_in: 4 + attribute_unchanged: + med: true + default_originate: "map01" + distribute_list: + - acl: 10 + action: "export" + neighbor_address: "203.0.113.5" + +replaced: + commands: + - delete protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged + - delete protocols bgp 65536 address-family ipv6 aggregate-address + - delete protocols bgp 65536 address-family ipv6 network + - delete protocols bgp 65536 address-family ipv6-unicast redistribute ripng + - set protocols bgp 65536 address-family ipv6-unicast redistribute static metric 50 + - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast allowas-in number 4 + - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged med + - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast default-originate route-map map01 + - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast distribute-list export 10 + +overridden: + commands: + - delete protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged + - delete protocols bgp 65536 neighbor 192.0.2.25 address-family + - delete protocols bgp 65536 address-family ipv6-unicast redistribute ripng + - delete protocols bgp 65536 address-family ipv6 aggregate-address + - delete protocols bgp 65536 address-family ipv6 network + - set protocols bgp 65536 address-family ipv6-unicast redistribute static metric 50 + - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast allowas-in number 4 + - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged med + - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast default-originate route-map map01 + - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast distribute-list export 10 + +deleted: + commands: + - delete protocols bgp 65536 address-family ipv6-unicast + - delete protocols bgp 65536 neighbor 203.0.113.5 address-family + - delete protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast + +populate: + config: + address_family: + - afi: "ipv6" + aggregate_address: + - summary_only: true + prefix: "5000:1:1::/64" + networks: + - prefix: "21e0:1:1::/64" + route_map: "map01" + redistribute: + - metric: 20 + protocol: "ripng" + as_number: 65536 + neighbors: + - address_family: + - afi: "ipv6" + route_map: + - action: "export" + route_map: "map01" + soft_reconfiguration: true + neighbor_address: "192.0.2.25" + - address_family: + - afi: "ipv6" + attribute_unchanged: + next_hop: true + neighbor_address: "203.0.113.5" diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_bgp_address_family_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_bgp_address_family_config.cfg new file mode 100644 index 0000000..a59878f --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_bgp_address_family_config.cfg @@ -0,0 +1,7 @@ +set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-set +set protocols bgp 65536 address-family ipv4-unicast network 192.1.13.0/24 route-map 'map01' +set protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24 backdoor +set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric '20' +set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast route-map export 'map01' +set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration inbound +set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop diff --git a/tests/unit/modules/network/vyos/test_vyos_bgp_address_family.py b/tests/unit/modules/network/vyos/test_vyos_bgp_address_family.py new file mode 100644 index 0000000..5fc7e35 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_bgp_address_family.py @@ -0,0 +1,684 @@ +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# 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_bgp_address_family, +) +from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( + set_module_args, +) +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosBgpafModule(TestVyosModule): + + module = vyos_bgp_address_family + + def setUp(self): + super(TestVyosBgpafModule, 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_execute_show_command = patch( + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts." + + "bgp_address_family.bgp_address_family.Bgp_address_familyFacts.get_device_data" + ) + self.execute_show_command = self.mock_execute_show_command.start() + + def tearDown(self): + super(TestVyosBgpafModule, self).tearDown() + self.mock_get_resource_connection_config.stop() + self.mock_execute_show_command.stop() + + def load_fixtures(self, commands=None, transport="cli", filename=None): + if filename is None: + filename = "vyos_bgp_address_family_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_bgp_address_family_merged_idempotent(self): + set_module_args( + dict( + config=dict( + as_number=65536, + address_family=[ + dict( + afi="ipv4", + aggregate_address=[ + dict(prefix="192.0.2.0/24", as_set=True) + ], + networks=[ + dict( + prefix="192.1.13.0/24", route_map="map01" + ), + dict(prefix="192.2.13.0/24", backdoor=True), + ], + ), + dict( + afi="ipv6", + redistribute=[dict(protocol="ripng", metric=20)], + ), + ], + neighbors=[ + dict( + neighbor_address="192.0.2.25", + address_family=[ + dict( + afi="ipv4", + route_map=[ + dict( + action="export", route_map="map01" + ) + ], + soft_reconfiguration=True, + ), + ], + ), + dict( + neighbor_address="203.0.113.5", + address_family=[ + dict( + afi="ipv6", + attribute_unchanged=dict(next_hop=True), + ) + ], + ), + ], + ) + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_bgp_address_family_merged(self): + set_module_args( + dict( + config=dict( + as_number=65536, + address_family=[ + dict( + afi="ipv4", + aggregate_address=[ + dict(prefix="192.0.2.0/24", summary_only=True) + ], + networks=[ + dict( + prefix="192.1.13.0/24", route_map="map01" + ), + ], + ), + dict( + afi="ipv6", + redistribute=[dict(protocol="ospfv3", metric=20)], + ), + ], + neighbors=[ + dict( + neighbor_address="192.10.21.25", + address_family=[ + dict( + afi="ipv6", + distribute_list=[ + dict(action="export", acl=10) + ], + route_server_client=True, + ), + ], + ), + dict( + neighbor_address="203.0.113.5", + address_family=[ + dict( + afi="ipv4", + filter_list=[ + dict( + action="export", path_list="list01" + ), + ], + capability=dict(orf="send"), + ) + ], + ), + ], + ) + ) + ) + commands = [ + "set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-setipv4-unicast aggregate-address 192.0.2.0/24 summary-only", + "set protocols bgp 65536 address-family ipv6-unicast redistribute ospfv3 metric 20", + "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv4-unicast filter-list export list01", + "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv4-unicast capability prefix-list send", + "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast distribute-list export 10", + "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast route-server-client", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_bgp_address_family_replaced_idempotent(self): + set_module_args( + dict( + state="replaced", + config=dict( + as_number=65536, + address_family=[ + dict( + afi="ipv4", + aggregate_address=[ + dict(prefix="192.0.2.0/24", as_set=True) + ], + networks=[ + dict( + prefix="192.1.13.0/24", route_map="map01" + ), + dict(prefix="192.2.13.0/24", backdoor=True), + ], + ), + dict( + afi="ipv6", + redistribute=[dict(protocol="ripng", metric=20)], + ), + ], + neighbors=[ + dict( + neighbor_address="192.0.2.25", + address_family=[ + dict( + afi="ipv4", + route_map=[ + dict( + action="export", route_map="map01" + ) + ], + soft_reconfiguration=True, + ), + ], + ), + dict( + neighbor_address="203.0.113.5", + address_family=[ + dict( + afi="ipv6", + attribute_unchanged=dict(next_hop=True), + ) + ], + ), + ], + ), + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_bgp_address_family_replaced(self): + set_module_args( + dict( + state="replaced", + config=dict( + as_number=65536, + address_family=[ + dict( + afi="ipv4", + aggregate_address=[ + dict(prefix="192.0.2.0/24", summary_only=True) + ], + networks=[ + dict( + prefix="192.1.13.0/24", route_map="map01" + ), + ], + ), + dict( + afi="ipv6", + redistribute=[dict(protocol="ospfv3", metric=20)], + ), + ], + neighbors=[ + dict( + neighbor_address="192.10.21.25", + address_family=[ + dict( + afi="ipv4", + route_map=[ + dict( + action="import", route_map="map01" + ) + ], + ), + dict( + afi="ipv6", + distribute_list=[ + dict(action="export", acl=10) + ], + route_server_client=True, + ), + ], + ), + dict( + neighbor_address="192.0.2.25", + address_family=[ + dict( + afi="ipv4", + route_map=[ + dict( + action="export", route_map="map01" + ) + ], + ), + ], + ), + dict( + neighbor_address="203.0.113.5", + address_family=[ + dict( + afi="ipv4", + filter_list=[ + dict( + action="export", path_list="list01" + ), + ], + capability=dict(orf="send"), + ) + ], + ), + ], + ), + ) + ) + commands = [ + "delete protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged", + "delete protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration", + "delete protocols bgp 65536 address-family ipv6-unicast redistribute ripng", + "delete protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24", + "set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 summary-only", + "set protocols bgp 65536 address-family ipv6-unicast redistribute ospfv3 metric 20", + "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv4-unicast route-map import map01", + "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast distribute-list export 10", + "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast route-server-client", + "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv4-unicast filter-list export list01", + "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv4-unicast capability prefix-list send", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_bgp_address_family_overridden_idempotent(self): + set_module_args( + dict( + state="overridden", + config=dict( + as_number=65536, + address_family=[ + dict( + afi="ipv4", + aggregate_address=[ + dict(prefix="192.0.2.0/24", as_set=True) + ], + networks=[ + dict( + prefix="192.1.13.0/24", route_map="map01" + ), + dict(prefix="192.2.13.0/24", backdoor=True), + ], + ), + dict( + afi="ipv6", + redistribute=[dict(protocol="ripng", metric=20)], + ), + ], + neighbors=[ + dict( + neighbor_address="192.0.2.25", + address_family=[ + dict( + afi="ipv4", + route_map=[ + dict( + action="export", route_map="map01" + ) + ], + soft_reconfiguration=True, + ), + ], + ), + dict( + neighbor_address="203.0.113.5", + address_family=[ + dict( + afi="ipv6", + attribute_unchanged=dict(next_hop=True), + ) + ], + ), + ], + ), + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_bgp_address_family_overridden(self): + set_module_args( + dict( + state="overridden", + config=dict( + as_number=65536, + address_family=[ + dict( + afi="ipv4", + networks=[ + dict( + prefix="192.1.13.0/24", route_map="map01" + ), + ], + ), + dict( + afi="ipv6", + redistribute=[dict(protocol="ospfv3", metric=20)], + ), + ], + neighbors=[ + dict( + neighbor_address="192.10.21.25", + address_family=[ + dict( + afi="ipv4", + route_map=[ + dict( + action="import", route_map="map01" + ) + ], + ), + dict( + afi="ipv6", + distribute_list=[ + dict(action="export", acl=10) + ], + route_server_client=True, + ), + ], + ), + ], + ), + ) + ) + commands = [ + "delete protocols bgp 65536 neighbor 203.0.113.5 address-family", + "delete protocols bgp 65536 neighbor 192.0.2.25 address-family", + "delete protocols bgp 65536 address-family ipv6-unicast redistribute ripng", + "delete protocols bgp 65536 address-family ipv4 aggregate-address", + "delete protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24", + "set protocols bgp 65536 address-family ipv6-unicast redistribute ospfv3 metric 20", + "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv4-unicast route-map import map01", + "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast distribute-list export 10", + "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast route-server-client", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_bgp_address_family_deleted(self): + set_module_args( + dict( + state="deleted", + config=dict( + as_number=65536, + address_family=[ + dict( + afi="ipv4", + ), + ], + neighbors=[ + dict( + neighbor_address="192.0.2.25", + address_family=[ + dict( + afi="ipv4", + ), + ], + ), + dict( + neighbor_address="203.0.113.5", + ), + ], + ), + ) + ) + commands = [ + "delete protocols bgp 65536 address-family ipv4-unicast", + "delete protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast", + "delete protocols bgp 65536 neighbor 203.0.113.5 address-family", + ] + + self.execute_module(changed=True, commands=commands) + + def test_vyos_bgp_address_family_incorrect_instance(self): + set_module_args( + dict( + state="overridden", + config=dict( + as_number=100, + address_family=[ + dict( + afi="ipv4", + networks=[ + dict( + prefix="192.1.13.0/24", route_map="map01" + ), + ], + ), + dict( + afi="ipv6", + redistribute=[dict(protocol="ospfv3", metric=20)], + ), + ], + neighbors=[ + dict( + neighbor_address="192.10.21.25", + address_family=[ + dict( + afi="ipv4", + route_map=[ + dict( + action="import", route_map="map01" + ) + ], + ), + dict( + afi="ipv6", + distribute_list=[ + dict(action="export", acl=10) + ], + route_server_client=True, + ), + ], + ), + ], + ), + ) + ) + result = self.execute_module(failed=True) + self.assertIn( + "Only one bgp instance is allowed per device", result["msg"] + ) + + def test_vyos_bgp_address_family_rendered(self): + set_module_args( + dict( + state="rendered", + config=dict( + as_number=65536, + address_family=[ + dict( + afi="ipv4", + aggregate_address=[ + dict(prefix="192.0.2.0/24", as_set=True) + ], + networks=[ + dict( + prefix="192.1.13.0/24", route_map="map01" + ), + dict(prefix="192.2.13.0/24", backdoor=True), + ], + ), + dict( + afi="ipv6", + redistribute=[dict(protocol="ripng", metric=20)], + ), + ], + neighbors=[ + dict( + neighbor_address="192.0.2.25", + address_family=[ + dict( + afi="ipv4", + route_map=[ + dict( + action="export", route_map="map01" + ) + ], + soft_reconfiguration=True, + ), + ], + ), + dict( + neighbor_address="203.0.113.5", + address_family=[ + dict( + afi="ipv6", + attribute_unchanged=dict(next_hop=True), + ) + ], + ), + ], + ), + ) + ) + + rendered_cmds = [ + "set protocols bgp 65536 address-family ipv4-unicast network 192.1.13.0/24 route-map map01", + "set protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24 backdoor", + "set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-set", + "set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric 20", + "set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast route-map export map01", + "set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration inbound", + "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop", + ] + result = self.execute_module(changed=False) + self.assertEqual( + sorted(result["rendered"]), + sorted(rendered_cmds), + result["rendered"], + ) + + def test_vyos_bgp_address_family_parsed(self): + commands = [ + "set protocols bgp 65536 address-family ipv4-unicast network 192.1.13.0/24 route-map map01", + "set protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24 backdoor", + "set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-set", + "set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric 20", + "set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast route-map export map01", + "set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration inbound", + "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop", + ] + + parsed_str = "\n".join(commands) + set_module_args(dict(running_config=parsed_str, state="parsed")) + result = self.execute_module(changed=False) + parsed_list = { + "as_number": 65536, + "address_family": [ + { + "afi": "ipv4", + "networks": [ + {"prefix": "192.1.13.0/24", "route_map": "map01"}, + {"prefix": "192.2.13.0/24", "backdoor": True}, + ], + "aggregate_address": [ + {"prefix": "192.0.2.0/24", "as_set": True} + ], + }, + { + "afi": "ipv6", + "redistribute": [{"protocol": "ripng", "metric": 20}], + }, + ], + "neighbors": [ + { + "neighbor_address": "192.0.2.25", + "address_family": [ + {"afi": "ipv4", "soft_reconfiguration": True}, + ], + }, + { + "neighbor_address": "203.0.113.5", + "address_family": [ + { + "afi": "ipv6", + "attribute_unchanged": {"next_hop": True}, + } + ], + }, + ], + } + self.assertEqual(sorted(parsed_list), sorted(result["parsed"])) + + def test_vyos_bgp_address_family_gathered(self): + set_module_args(dict(state="gathered")) + result = self.execute_module(changed=False) + gather_list = { + "as_number": 65536, + "address_family": [ + { + "afi": "ipv4", + "networks": [ + {"prefix": "192.1.13.0/24", "route_map": "map01"}, + {"prefix": "192.2.13.0/24", "backdoor": True}, + ], + "aggregate_address": [ + {"prefix": "192.0.2.0/24", "as_set": True} + ], + }, + { + "afi": "ipv6", + "redistribute": [{"protocol": "ripng", "metric": 20}], + }, + ], + "neighbors": [ + { + "neighbor_address": "192.0.2.25", + "address_family": [ + {"afi": "ipv4", "soft_reconfiguration": True}, + ], + }, + { + "neighbor_address": "203.0.113.5", + "address_family": [ + { + "afi": "ipv6", + "attribute_unchanged": {"next_hop": True}, + } + ], + }, + ], + } + self.assertEqual(sorted(gather_list), sorted(result["gathered"])) diff --git a/tox.ini b/tox.ini index 3da0118..49e9b1d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,32 +1,32 @@ [tox] minversion = 1.4.2 envlist = linters skipsdist = True [testenv] deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt [testenv:black] install_command = pip install {opts} {packages} commands = black -v -l79 {toxinidir} [testenv:linters] install_command = pip install {opts} {packages} commands = black -v -l79 --diff --check {toxinidir} flake8 {posargs} yamllint -s . [testenv:venv] commands = {posargs} [flake8] # E123, E125 skipped as they are invalid PEP-8. show-source = True -ignore = E123,E125,E402,W503 +ignore = E123,E125,E402,W503,W504 max-line-length = 160 builtins = _ exclude = .git,.tox,tests/unit/compat/