diff --git a/changelogs/fragments/vyos_ospf_interfaces_rm.yaml b/changelogs/fragments/vyos_ospf_interfaces_rm.yaml new file mode 100644 index 0000000..c2dcdc2 --- /dev/null +++ b/changelogs/fragments/vyos_ospf_interfaces_rm.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Added ospf_interfaces resource module. diff --git a/meta/runtime.yml b/meta/runtime.yml index 949a04f..00bbaca 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -1,203 +1,209 @@ --- requires_ansible: '>=2.9.10,<2.11' plugin_routing: action: vyos_banner: redirect: vyos.vyos.vyos banner: 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 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/filter/__init__.py b/plugins/filter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/inventory/__init__.py b/plugins/inventory/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/module_utils/network/__init__.py b/plugins/module_utils/network/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/module_utils/network/vyos/argspec/ospf_interfaces/__init__.py b/plugins/module_utils/network/vyos/argspec/ospf_interfaces/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/module_utils/network/vyos/argspec/ospf_interfaces/ospf_interfaces.py b/plugins/module_utils/network/vyos/argspec/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 0000000..e7dd10c --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 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_ospf_interfaces module +""" + + +class Ospf_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the vyos_ospf_interfaces module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "running_config": {}, + "state": { + "default": "merged", + "type": "str", + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + }, + "config": { + "elements": "dict", + "type": "list", + "options": { + "name": {"type": "str"}, + "address_family": { + "elements": "dict", + "type": "list", + "options": { + "passive": {"type": "bool"}, + "retransmit_interval": {"type": "int"}, + "cost": {"type": "int"}, + "afi": { + "required": True, + "type": "str", + "choices": ["ipv4", "ipv6"], + }, + "authentication": { + "type": "dict", + "options": { + "plaintext_password": {"type": "str"}, + "md5_key": { + "type": "dict", + "options": { + "key_id": {"type": "int"}, + "key": {"type": "str"}, + }, + }, + }, + }, + "mtu_ignore": {"type": "bool"}, + "priority": {"type": "int"}, + "instance": {"type": "str"}, + "bandwidth": {"type": "int"}, + "dead_interval": {"type": "int"}, + "ifmtu": {"type": "int"}, + "hello_interval": {"type": "int"}, + "transmit_delay": {"type": "int"}, + "network": {"type": "str"}, + }, + }, + }, + }, + } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/ospf_interfaces/__init__.py b/plugins/module_utils/network/vyos/config/ospf_interfaces/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py b/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 0000000..d01b1e0 --- /dev/null +++ b/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,164 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2020 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_ospf_interfaces config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import ( + ResourceModule, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( + Facts, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ospf_interfaces import ( + Ospf_interfacesTemplate, +) + + +class Ospf_interfaces(ResourceModule): + """ + The vyos_ospf_interfaces config class + """ + + def __init__(self, module): + super(Ospf_interfaces, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="ospf_interfaces", + tmplt=Ospf_interfacesTemplate(), + ) + self.parsers = [ + "authentication_password", + "authentication_md5", + "bandwidth", + "cost", + "hello_interval", + "dead_interval", + "mtu_ignore", + "network", + "priority", + "retransmit_interval", + "transmit_delay", + "ifmtu", + "instance", + "passive", + ] + + 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 = {entry["name"]: entry for entry in self.want} + haved = {entry["name"]: entry for entry in self.have} + + # turn all lists of dicts into dicts prior to merge + for entry in wantd, haved: + self._ospf_int_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": + haved = { + k: v for k, v in iteritems(haved) if k in wantd or not wantd + } + have_int = [] + for k, have in iteritems(haved): + if k in wantd: + have_int.append(k) + self._remove_ospf_int(have) + wantd = {} + + if self.state == "overridden": + have_int = [] + for k, have in iteritems(haved): + if k not in wantd: + have_int.append(k) + self._remove_ospf_int(have) + + # remove superfluous config for overridden and deleted + if self.state in ["overridden", "deleted"]: + # removing the interfaces from haved that are already negated + for interface in have_int: + haved.pop(interface) + 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 _remove_ospf_int(self, entry): + int_name = entry.get("name", {}) + int_addr = entry.get("address_family", {}) + for k, addr in iteritems(int_addr): + rem_entry = {"name": int_name, "address_family": {"afi": k}} + self.addcmd(rem_entry, "ip_ospf", True) + + 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 Ospf_interfaces network resource. + """ + self._compare_addr_family(want=want, have=have) + + def _compare_addr_family(self, want, have): + wdict = want.get("address_family", {}) + hdict = have.get("address_family", {}) + wname = want.get("name") + hname = have.get("name") + for name, entry in iteritems(wdict): + for key, param in iteritems(entry): + w_addr = {"afi": name, key: param} + h_addr = {} + if hdict.get(name): + h_addr = {"afi": name, key: hdict[name].pop(key, {})} + w = {"name": wname, "address_family": w_addr} + h = {"name": hname, "address_family": h_addr} + self.compare(parsers=self.parsers, want=w, have=h) + for name, entry in iteritems(hdict): + for key, param in iteritems(entry): + h_addr = {"afi": name, key: param} + w_addr = {} + w = {"name": wname, "address_family": w_addr} + h = {"name": hname, "address_family": h_addr} + self.compare(parsers=self.parsers, want=w, have=h) + + def _ospf_int_list_to_dict(self, entry): + for name, family in iteritems(entry): + if "address_family" in family: + family["address_family"] = { + entry["afi"]: entry + for entry in family.get("address_family", []) + } + self._ospf_int_list_to_dict(family["address_family"]) diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index caea5fe..c2766de 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -1,99 +1,103 @@ # 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.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, ) 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/facts/ospf_interfaces/__init__.py b/plugins/module_utils/network/vyos/facts/ospf_interfaces/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py b/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 0000000..15ac92a --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 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 ospf_interfaces 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.ospf_interfaces import ( + Ospf_interfacesTemplate, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospf_interfaces.ospf_interfaces import ( + Ospf_interfacesArgs, +) + + +class Ospf_interfacesFacts(object): + """The vyos ospf_interfaces facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Ospf_interfacesArgs.argument_spec + + def get_device_data(self, connection): + return connection.get( + 'show configuration commands | match "set interfaces"' + ) + + def get_config_set(self, data): + """ To classify the configurations beased on interface """ + interface_list = [] + config_set = [] + int_string = "" + for config_line in data.splitlines(): + ospf_int = re.search(r"set interfaces \S+ (\S+) .*", config_line) + if ospf_int: + if ospf_int.group(1) not in interface_list: + if int_string: + config_set.append(int_string) + interface_list.append(ospf_int.group(1)) + int_string = "" + int_string = int_string + config_line + "\n" + if int_string: + config_set.append(int_string) + return config_set + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Ospf_interfaces network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + + if not data: + data = self.get_device_data(connection) + + # parse native config using the Ospf_interfaces template + ospf_interfaces_facts = [] + resources = self.get_config_set(data) + for resource in resources: + ospf_interfaces_parser = Ospf_interfacesTemplate( + lines=resource.split("\n") + ) + objs = ospf_interfaces_parser.parse() + for key, sortv in [("address_family", "afi")]: + if key in objs and objs[key]: + objs[key] = list(objs[key].values()) + ospf_interfaces_facts.append(objs) + + ansible_facts["ansible_network_resources"].pop("ospf_interfaces", None) + facts = {"ospf_interfaces": []} + params = utils.remove_empties( + utils.validate_config( + self.argument_spec, {"config": ospf_interfaces_facts} + ) + ) + if params.get("config"): + for cfg in params["config"]: + facts["ospf_interfaces"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/plugins/module_utils/network/vyos/rm_templates/__init__.py b/plugins/module_utils/network/vyos/rm_templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py new file mode 100644 index 0000000..460e6b0 --- /dev/null +++ b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py @@ -0,0 +1,743 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 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 Ospf_interfaces 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, +) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( + get_interface_type, +) + + +def _get_parameters(data): + if data["afi"] == "ipv6": + val = ["ospfv3", "ipv6"] + else: + val = ["ospf", "ip"] + return val + + +def _tmplt_ospf_int_delete(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + ) + + return command + + +def _tmplt_ospf_int_cost(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " cost {cost}".format(**config_data["address_family"]) + ) + + return command + + +def _tmplt_ospf_int_auth_password(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " authentication plaintext-password {plaintext_password}".format( + **config_data["address_family"]["authentication"] + ) + ) + return command + + +def _tmplt_ospf_int_auth_md5(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " authentication md5 key-id {key_id} ".format( + **config_data["address_family"]["authentication"]["md5_key"] + ) + + "md5-key {key}".format( + **config_data["address_family"]["authentication"]["md5_key"] + ) + ) + + return command + + +def _tmplt_ospf_int_auth_md5_delete(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " authentication" + ) + + return command + + +def _tmplt_ospf_int_bw(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " bandwidth {bandwidth}".format(**config_data["address_family"]) + ) + + return command + + +def _tmplt_ospf_int_hello_interval(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " hello-interval {hello_interval}".format( + **config_data["address_family"] + ) + ) + + return command + + +def _tmplt_ospf_int_dead_interval(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " dead-interval {dead_interval}".format( + **config_data["address_family"] + ) + ) + + return command + + +def _tmplt_ospf_int_mtu_ignore(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " mtu-ignore" + ) + + return command + + +def _tmplt_ospf_int_network(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " network {network}".format(**config_data["address_family"]) + ) + + return command + + +def _tmplt_ospf_int_priority(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " priority {priority}".format(**config_data["address_family"]) + ) + + return command + + +def _tmplt_ospf_int_retransmit_interval(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " retransmit-interval {retransmit_interval}".format( + **config_data["address_family"] + ) + ) + + return command + + +def _tmplt_ospf_int_transmit_delay(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " transmit-delay {transmit_delay}".format( + **config_data["address_family"] + ) + ) + + return command + + +def _tmplt_ospf_int_ifmtu(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " ifmtu {ifmtu}".format(**config_data["address_family"]) + ) + + return command + + +def _tmplt_ospf_int_instance(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " instance-id {instance}".format(**config_data["address_family"]) + ) + + return command + + +def _tmplt_ospf_int_passive(config_data): + int_type = get_interface_type(config_data["name"]) + params = _get_parameters(config_data["address_family"]) + command = ( + "interfaces " + + int_type + + " {name} ".format(**config_data) + + params[1] + + " " + + params[0] + + " passive" + ) + + return command + + +class Ospf_interfacesTemplate(NetworkTemplate): + def __init__(self, lines=None): + prefix = {"set": "set", "remove": "delete"} + super(Ospf_interfacesTemplate, self).__init__( + lines=lines, tmplt=self, prefix=prefix + ) + + # fmt: off + PARSERS = [ + { + "name": "ip_ospf", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + *$""", + re.VERBOSE, + ), + "remval": _tmplt_ospf_int_delete, + "compval": "address_family", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + } + } + } + }, + { + "name": "authentication_password", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+authentication + \s+plaintext-password + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_auth_password, + "compval": "address_family.authentication", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "authentication": { + "plaintext_password": "{{ text }}" + } + } + } + } + }, + { + "name": "authentication_md5", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+authentication + \s+md5 + \s+key-id + \s+(?P\d+) + \s+md5-key + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_auth_md5, + "remval": _tmplt_ospf_int_auth_md5_delete, + "compval": "address_family.authentication", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "authentication": { + "md5_key": { + "key_id": "{{ id }}", + "key": "{{ text }}" + } + } + } + } + } + }, + { + "name": "bandwidth", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+bandwidth + \s+(?P\'\d+\') + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_bw, + "compval": "address_family.bandwidth", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "bandwidth": "{{ bw }}" + } + } + } + }, + { + "name": "cost", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+cost + \s+(?P\'\d+\') + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_cost, + "compval": "address_family.cost", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "cost": "{{ val }}" + } + } + } + }, + { + "name": "hello_interval", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+hello-interval + \s+(?P\'\d+\') + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_hello_interval, + "compval": "address_family.hello_interval", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "hello_interval": "{{ val }}" + } + } + } + }, + { + "name": "dead_interval", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+dead-interval + \s+(?P\'\d+\') + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_dead_interval, + "compval": "address_family.dead_interval", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "dead_interval": "{{ val }}" + } + } + } + }, + { + "name": "mtu_ignore", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+(?P\'mtu-ignore\') + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_mtu_ignore, + "compval": "address_family.mtu_ignore", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "mtu_ignore": "{{ True if mtu is defined }}" + } + } + } + }, + { + "name": "network", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+network + \s+(?P\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_network, + "compval": "address_family.network", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "network": "{{ val }}" + } + } + } + }, + { + "name": "priority", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+priority + \s+(?P\'\d+\') + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_priority, + "compval": "address_family.priority", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "priority": "{{ val }}" + } + } + } + }, + { + "name": "retransmit_interval", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+retransmit-interval + \s+(?P\'\d+\') + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_retransmit_interval, + "compval": "address_family.retransmit_interval", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "retransmit_interval": "{{ val }}" + } + } + } + }, + { + "name": "transmit_delay", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+transmit-delay + \s+(?P\'\d+\') + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_transmit_delay, + "compval": "address_family.transmit_delay", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "transmit_delay": "{{ val }}" + } + } + } + }, + { + "name": "ifmtu", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+ifmtu + \s+(?P\'\d+\') + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_ifmtu, + "compval": "address_family.ifmtu", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "ifmtu": "{{ val }}" + } + } + } + }, + { + "name": "instance", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+instance-id + \s+(?P\'\d+\') + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_instance, + "compval": "address_family.instance", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "instance": "{{ val }}" + } + } + } + }, + { + "name": "passive", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + \s+(?Pip|ipv6) + \s+(?Pospf|ospfv3) + \s+(?P\'passive\') + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_passive, + "compval": "address_family.passive", + "result": { + "name": "{{ name }}", + "address_family": { + "{{ afi }}": { + "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', + "passive": "{{ True if pass is defined }}" + } + } + } + }, + { + "name": "interface_name", + "getval": re.compile( + r""" + ^set + \s+interfaces + \s+(?P\S+) + \s+(?P\S+) + .*$""", + re.VERBOSE, + ), + "setval": "set interface {{ type }} {{ name }}", + "result": { + "name": "{{ name }}", + } + }, + ] + # fmt: on diff --git a/plugins/modules/vyos_ospf_interfaces.py b/plugins/modules/vyos_ospf_interfaces.py new file mode 100644 index 0000000..732a5e7 --- /dev/null +++ b/plugins/modules/vyos_ospf_interfaces.py @@ -0,0 +1,917 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2020 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_ospf_interfaces +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +module: vyos_ospf_interfaces +version_added: 1.2.0 +short_description: OSPF Interfaces Resource Module. +description: +- This module manages OSPF configuration of interfaces on devices running VYOS. +author: Gomathi Selvi Srinivasan (@GomathiselviS) +options: + config: + description: A list of OSPF configuration for interfaces. + type: list + elements: dict + suboptions: + name: + description: + - Name/Identifier of the interface. + type: str + address_family: + description: + - OSPF settings on the interfaces in address-family context. + type: list + elements: dict + suboptions: + afi: + description: + - Address Family Identifier (AFI) for OSPF settings on the interfaces. + type: str + choices: ['ipv4', 'ipv6'] + required: True + authentication: + description: + - Authentication settings on the interface. + type: dict + suboptions: + plaintext_password: + description: + - Plain Text password. + type: str + md5_key: + description: + - md5 parameters. + type: dict + suboptions: + key_id: + description: + - key id. + type: int + key: + description: + - md5 key. + type: str + bandwidth: + description: + - Bandwidth of interface (kilobits/sec) + type: int + cost: + description: + - metric associated with interface. + type: int + dead_interval: + description: + - Time interval to detect a dead router. + type: int + hello_interval: + description: + - Timer interval between transmission of hello packets. + type: int + mtu_ignore: + description: + - if True, Disable MTU check for Database Description packets. + type: bool + network: + description: + - Interface type. + type: str + priority: + description: + - Interface priority. + type: int + retransmit_interval: + description: + - LSA retransmission interval. + type: int + transmit_delay: + description: + - LSA transmission delay. + type: int + ifmtu: + description: + - interface MTU. + type: int + instance: + description: + - Instance ID. + type: str + passive: + description: + - If True, disables forming adjacency. + type: bool + running_config: + 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(sh running-config | section ^interface). + - 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. + type: str + state: + description: + - The state the configuration should be left in. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# + +# @vyos:~$ show configuration commands | match "ospf" + + - name: Merge provided configuration with device configuration + vyos.vyos.vyos_ospf_interfaces: + config: + - name: "eth1" + address_family: + - afi: "ipv4" + transmit_delay: 50 + priority: 26 + network: "point-to-point" + - afi: "ipv6" + dead_interval: 39 + - name: "bond2" + address_family: + - afi: "ipv4" + transmit_delay: 45 + bandwidth: 70 + authentication: + md5_key: + key_id: 10 + key: "1111111111232345" + - afi: "ipv6" + passive: True + state: merged + +# After State: +# -------------- + +# vyos@vyos:~$ show configuration commands | match "ospf" +# set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' +# set interfaces bonding bond2 ip ospf bandwidth '70' +# set interfaces bonding bond2 ip ospf transmit-delay '45' +# set interfaces bonding bond2 ipv6 ospfv3 'passive' +# set interfaces ethernet eth1 ip ospf network 'point-to-point' +# set interfaces ethernet eth1 ip ospf priority '26' +# set interfaces ethernet eth1 ip ospf transmit-delay '50' +# set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' + +# "after": [ +# " +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "md5_key": { +# "key": "1111111111232345", +# "key_id": 10 +# } +# }, +# "bandwidth": 70, +# "transmit_delay": 45 +# }, +# { +# "afi": "ipv6", +# "passive": true +# } +# ], +# "name": "bond2" +# }, +# { +# "name": "eth0" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "network": "point-to-point", +# "priority": 26, +# "transmit_delay": 50 +# }, +# { +# "afi": "ipv6", +# "dead_interval": 39 +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "name": "eth3" +# } +# ], +# "before": [ +# { +# "name": "eth0" +# }, +# { +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "name": "eth3" +# } +# ], +# "changed": true, +# "commands": [ +# "set interfaces ethernet eth1 ip ospf transmit-delay 50", +# "set interfaces ethernet eth1 ip ospf priority 26", +# "set interfaces ethernet eth1 ip ospf network point-to-point", +# "set interfaces ethernet eth1 ipv6 ospfv3 dead-interval 39", +# "set interfaces bonding bond2 ip ospf transmit-delay 45", +# "set interfaces bonding bond2 ip ospf bandwidth 70", +# "set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key 1111111111232345", +# "set interfaces bonding bond2 ipv6 ospfv3 passive" +# ], + + + + +# Using replaced: + +# Before State: +# ------------ + +# vyos@vyos:~$ show configuration commands | match "ospf" +# set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' +# set interfaces bonding bond2 ip ospf bandwidth '70' +# set interfaces bonding bond2 ip ospf transmit-delay '45' +# set interfaces bonding bond2 ipv6 ospfv3 'passive' +# set interfaces ethernet eth1 ip ospf network 'point-to-point' +# set interfaces ethernet eth1 ip ospf priority '26' +# set interfaces ethernet eth1 ip ospf transmit-delay '50' +# set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' + + - name: Replace provided configuration with device configuration + vyos.vyos.vyos_ospf_interfaces: + config: + - name: "eth1" + address_family: + - afi: "ipv4" + cost: 100 + - afi: "ipv6" + ifmtu: 33 + - name: "bond2" + address_family: + - afi: "ipv4" + transmit_delay: 45 + - afi: "ipv6" + passive: True + state: replaced + +# After State: +# ----------- + +# vyos@vyos:~$ show configuration commands | match "ospf" +# set interfaces bonding bond2 ip ospf transmit-delay '45' +# set interfaces bonding bond2 ipv6 ospfv3 'passive' +# set interfaces ethernet eth1 ip ospf cost '100' +# set interfaces ethernet eth1 ipv6 ospfv3 ifmtu '33' +# vyos@vyos:~$ + +# Module Execution +# ---------------- +# "after": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "transmit_delay": 45 +# }, +# { +# "afi": "ipv6", +# "passive": true +# } +# ], +# "name": "bond2" +# }, +# { +# "name": "eth0" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "cost": 100 +# }, +# { +# "afi": "ipv6", +# "ifmtu": 33 +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "name": "eth3" +# } +# ], +# "before": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "md5_key": { +# "key": "1111111111232345", +# "key_id": 10 +# } +# }, +# "bandwidth": 70, +# "transmit_delay": 45 +# }, +# { +# "afi": "ipv6", +# "passive": true +# } +# ], +# "name": "bond2" +# }, +# { +# "name": "eth0" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "network": "point-to-point", +# "priority": 26, +# "transmit_delay": 50 +# }, +# { +# "afi": "ipv6", +# "dead_interval": 39 +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "name": "eth3" +# } +# ], +# "changed": true, +# "commands": [ +# "set interfaces ethernet eth1 ip ospf cost 100", +# "set interfaces ethernet eth1 ipv6 ospfv3 ifmtu 33", +# "delete interfaces ethernet eth1 ip ospf network point-to-point", +# "delete interfaces ethernet eth1 ip ospf priority 26", +# "delete interfaces ethernet eth1 ip ospf transmit-delay 50", +# "delete interfaces ethernet eth1 ipv6 ospfv3 dead-interval 39", +# "delete interfaces bonding bond2 ip ospf authentication", +# "delete interfaces bonding bond2 ip ospf bandwidth 70" +# ], +# + +# Using Overridden: +# ----------------- + +# Before State: +# ------------ + +# vyos@vyos:~$ show configuration commands | match "ospf" +# set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' +# set interfaces bonding bond2 ip ospf bandwidth '70' +# set interfaces bonding bond2 ip ospf transmit-delay '45' +# set interfaces bonding bond2 ipv6 ospfv3 'passive' +# set interfaces ethernet eth1 ip ospf cost '100' +# set interfaces ethernet eth1 ip ospf network 'point-to-point' +# set interfaces ethernet eth1 ip ospf priority '26' +# set interfaces ethernet eth1 ip ospf transmit-delay '50' +# set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' +# set interfaces ethernet eth1 ipv6 ospfv3 ifmtu '33' +# vyos@vyos:~$ + + - name: Override device configuration with provided configuration + vyos.vyos.vyos_ospf_interfaces: + config: + - name: "eth0" + address_family: + - afi: "ipv4" + cost: 100 + - afi: "ipv6" + ifmtu: 33 + passive: True + state: overridden +# After State: +# ----------- + +# 200~vyos@vyos:~$ show configuration commands | match "ospf" +# set interfaces ethernet eth0 ip ospf cost '100' +# set interfaces ethernet eth0 ipv6 ospfv3 ifmtu '33' +# set interfaces ethernet eth0 ipv6 ospfv3 'passive' +# vyos@vyos:~$ +# +# +# "after": [ +# { +# "name": "bond2" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "cost": 100 +# }, +# { +# "afi": "ipv6", +# "ifmtu": 33, +# "passive": true +# } +# ], +# "name": "eth0" +# }, +# { +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "name": "eth3" +# } +# ], +# "before": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "md5_key": { +# "key": "1111111111232345", +# "key_id": 10 +# } +# }, +# "bandwidth": 70, +# "transmit_delay": 45 +# }, +# { +# "afi": "ipv6", +# "passive": true +# } +# ], +# "name": "bond2" +# }, +# { +# "name": "eth0" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "cost": 100, +# "network": "point-to-point", +# "priority": 26, +# "transmit_delay": 50 +# }, +# { +# "afi": "ipv6", +# "dead_interval": 39, +# "ifmtu": 33 +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "name": "eth3" +# } +# ], +# "changed": true, +# "commands": [ +# "delete interfaces bonding bond2 ip ospf", +# "delete interfaces bonding bond2 ipv6 ospfv3", +# "delete interfaces ethernet eth1 ip ospf", +# "delete interfaces ethernet eth1 ipv6 ospfv3", +# "set interfaces ethernet eth0 ip ospf cost 100", +# "set interfaces ethernet eth0 ipv6 ospfv3 ifmtu 33", +# "set interfaces ethernet eth0 ipv6 ospfv3 passive" +# ], +# + +# Using deleted: +# ------------- + +# before state: +# ------------- + +# vyos@vyos:~$ show configuration commands | match "ospf" +# set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' +# set interfaces bonding bond2 ip ospf bandwidth '70' +# set interfaces bonding bond2 ip ospf transmit-delay '45' +# set interfaces bonding bond2 ipv6 ospfv3 'passive' +# set interfaces ethernet eth0 ip ospf cost '100' +# set interfaces ethernet eth0 ipv6 ospfv3 ifmtu '33' +# set interfaces ethernet eth0 ipv6 ospfv3 'passive' +# set interfaces ethernet eth1 ip ospf network 'point-to-point' +# set interfaces ethernet eth1 ip ospf priority '26' +# set interfaces ethernet eth1 ip ospf transmit-delay '50' +# set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' +# vyos@vyos:~$ + + - name: Delete device configuration + vyos.vyos.vyos_ospf_interfaces: + config: + - name: "eth0" + state: deleted + +# After State: +# ----------- + +# vyos@vyos:~$ show configuration commands | match "ospf" +# set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' +# set interfaces bonding bond2 ip ospf bandwidth '70' +# set interfaces bonding bond2 ip ospf transmit-delay '45' +# set interfaces bonding bond2 ipv6 ospfv3 'passive' +# set interfaces ethernet eth1 ip ospf network 'point-to-point' +# set interfaces ethernet eth1 ip ospf priority '26' +# set interfaces ethernet eth1 ip ospf transmit-delay '50' +# set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' +# vyos@vyos:~$ +# +# +# "after": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "md5_key": { +# "key": "1111111111232345", +# "key_id": 10 +# } +# }, +# "bandwidth": 70, +# "transmit_delay": 45 +# }, +# { +# "afi": "ipv6", +# "passive": true +# } +# ], +# "name": "bond2" +# }, +# { +# "name": "eth0" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "network": "point-to-point", +# "priority": 26, +# "transmit_delay": 50 +# }, +# { +# "afi": "ipv6", +# "dead_interval": 39 +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "name": "eth3" +# } +# ], +# "before": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "md5_key": { +# "key": "1111111111232345", +# "key_id": 10 +# } +# }, +# "bandwidth": 70, +# "transmit_delay": 45 +# }, +# { +# "afi": "ipv6", +# "passive": true +# } +# ], +# "name": "bond2" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "cost": 100 +# }, +# { +# "afi": "ipv6", +# "ifmtu": 33, +# "passive": true +# } +# ], +# "name": "eth0" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "network": "point-to-point", +# "priority": 26, +# "transmit_delay": 50 +# }, +# { +# "afi": "ipv6", +# "dead_interval": 39 +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "name": "eth3" +# } +# ], +# "changed": true, +# "commands": [ +# "delete interfaces ethernet eth0 ip ospf", +# "delete interfaces ethernet eth0 ipv6 ospfv3" +# ], +# +# Using parsed: +# parsed.cfg: + +# set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' +# set interfaces bonding bond2 ip ospf bandwidth '70' +# set interfaces bonding bond2 ip ospf transmit-delay '45' +# set interfaces bonding bond2 ipv6 ospfv3 'passive' +# set interfaces ethernet eth0 ip ospf cost '50' +# set interfaces ethernet eth0 ip ospf priority '26' +# set interfaces ethernet eth0 ipv6 ospfv3 instance-id '33' +# set interfaces ethernet eth0 ipv6 ospfv3 'mtu-ignore' +# set interfaces ethernet eth1 ip ospf network 'point-to-point' +# set interfaces ethernet eth1 ip ospf priority '26' +# set interfaces ethernet eth1 ip ospf transmit-delay '50' +# set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' +# + + - name: parse configs + vyos.vyos.vyos_ospf_interfaces: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed + +# Module Execution: +# ---------------- + +# "parsed": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "md5_key": { +# "key": "1111111111232345", +# "key_id": 10 +# } +# }, +# "bandwidth": 70, +# "transmit_delay": 45 +# }, +# { +# "afi": "ipv6", +# "passive": true +# } +# ], +# "name": "bond2" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "cost": 50, +# "priority": 26 +# }, +# { +# "afi": "ipv6", +# "instance": "33", +# "mtu_ignore": true +# } +# ], +# "name": "eth0" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "network": "point-to-point", +# "priority": 26, +# "transmit_delay": 50 +# }, +# { +# "afi": "ipv6", +# "dead_interval": 39 +# } +# ], +# "name": "eth1" +# } +# ] + +# Using rendered: +# -------------- + + - name: Render + vyos.vyos.vyos_ospf_interfaces: + config: + - name: "eth1" + address_family: + - afi: "ipv4" + transmit_delay: 50 + priority: 26 + network: "point-to-point" + - afi: "ipv6" + dead_interval: 39 + - name: "bond2" + address_family: + - afi: "ipv4" + transmit_delay: 45 + bandwidth: 70 + authentication: + md5_key: + key_id: 10 + key: "1111111111232345" + - afi: "ipv6" + passive: True + state: rendered + +# Module Execution: +# ---------------- + +# "rendered": [ +# "set interfaces ethernet eth1 ip ospf transmit-delay 50", +# "set interfaces ethernet eth1 ip ospf priority 26", +# "set interfaces ethernet eth1 ip ospf network point-to-point", +# "set interfaces ethernet eth1 ipv6 ospfv3 dead-interval 39", +# "set interfaces bonding bond2 ip ospf transmit-delay 45", +# "set interfaces bonding bond2 ip ospf bandwidth 70", +# "set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key 1111111111232345", +# "set interfaces bonding bond2 ipv6 ospfv3 passive" +# ] +# + +# Using Gathered: +# -------------- + +# Native Config: + +# vyos@vyos:~$ show configuration commands | match "ospf" +# set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' +# set interfaces bonding bond2 ip ospf bandwidth '70' +# set interfaces bonding bond2 ip ospf transmit-delay '45' +# set interfaces bonding bond2 ipv6 ospfv3 'passive' +# set interfaces ethernet eth1 ip ospf network 'point-to-point' +# set interfaces ethernet eth1 ip ospf priority '26' +# set interfaces ethernet eth1 ip ospf transmit-delay '50' +# set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' +# vyos@vyos:~$ + + - name: gather configs + vyos.vyos.vyos_ospf_interfaces: + state: gathered + +# Module Execution: +# ----------------- + +# "gathered": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "md5_key": { +# "key": "1111111111232345", +# "key_id": 10 +# } +# }, +# "bandwidth": 70, +# "transmit_delay": 45 +# }, +# { +# "afi": "ipv6", +# "passive": true +# } +# ], +# "name": "bond2" +# }, +# { +# "name": "eth0" +# }, +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "network": "point-to-point", +# "priority": 26, +# "transmit_delay": 50 +# }, +# { +# "afi": "ipv6", +# "dead_interval": 39 +# } +# ], +# "name": "eth1" +# }, +# { +# "name": "eth2" +# }, +# { +# "name": "eth3" +# } +# ], + + + + + +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospf_interfaces.ospf_interfaces import ( + Ospf_interfacesArgs, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospf_interfaces.ospf_interfaces import ( + Ospf_interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Ospf_interfacesArgs.argument_spec, + mutually_exclusive=[], + required_if=[], + supports_check_mode=False, + ) + + result = Ospf_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/vyos_ospf_interfaces/defaults/main.yaml b/tests/integration/targets/vyos_ospf_interfaces/defaults/main.yaml new file mode 100644 index 0000000..852a6be --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: '[^_].*' +test_items: [] diff --git a/tests/integration/targets/vyos_ospf_interfaces/meta/main.yaml b/tests/integration/targets/vyos_ospf_interfaces/meta/main.yaml new file mode 100644 index 0000000..7413320 --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/meta/main.yaml @@ -0,0 +1,3 @@ +--- +dependencies: + - prepare_vyos_tests diff --git a/tests/integration/targets/vyos_ospf_interfaces/tasks/cli.yaml b/tests/integration/targets/vyos_ospf_interfaces/tasks/cli.yaml new file mode 100644 index 0000000..93eb2fe --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/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_ospf_interfaces/tasks/main.yaml b/tests/integration/targets/vyos_ospf_interfaces/tasks/main.yaml new file mode 100644 index 0000000..b957d2f --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tasks/main.yaml @@ -0,0 +1,4 @@ +--- +- include: cli.yaml + tags: + - network_cli diff --git a/tests/integration/targets/vyos_ospf_interfaces/tests/cli/_parsed.cfg b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/_parsed.cfg new file mode 100644 index 0000000..aa57677 --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/_parsed.cfg @@ -0,0 +1,6 @@ +set interfaces ethernet eth0 ip ospf cost '50' +set interfaces ethernet eth0 ip ospf priority '26' +set interfaces ethernet eth0 ipv6 ospfv3 'mtu-ignore' +set interfaces ethernet eth0 ipv6 ospfv3 instance-id '33' +set interfaces bonding bond2 ip ospf transmit-delay '45' +set interfaces bonding bond2 ipv6 ospfv3 'passive' diff --git a/tests/integration/targets/vyos_ospf_interfaces/tests/cli/_parsed_config.cfg b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/_parsed_config.cfg new file mode 100644 index 0000000..54696e8 --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/_parsed_config.cfg @@ -0,0 +1,10 @@ +set interfaces ethernet eth1 firewall in name 'INBOUND' +set interfaces ethernet eth1 firewall out name 'OUTBOUND' +set interfaces ethernet eth1 firewall local name 'LOCAL' +set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' +set interfaces ethernet eth2 firewall in name 'INBOUND' +set interfaces ethernet eth2 firewall out name 'OUTBOUND' +set interfaces ethernet eth2 firewall local name 'LOCAL' +set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL' +set interfaces ethernet eth0 + diff --git a/tests/integration/targets/vyos_ospf_interfaces/tests/cli/_populate.yaml b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/_populate.yaml new file mode 100644 index 0000000..c4749d3 --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/_populate.yaml @@ -0,0 +1,13 @@ +--- +- name: setup + vyos.vyos.vyos_config: + lines: + - set interfaces ethernet eth0 ip ospf cost 50 + - set interfaces ethernet eth0 ip ospf priority 26 + - set interfaces ethernet eth0 ipv6 ospfv3 mtu-ignore + - set interfaces ethernet eth0 ipv6 ospfv3 instance-id 33 + - set interfaces bonding bond2 ip ospf transmit-delay 45 + - set interfaces bonding bond2 ipv6 ospfv3 passive + ignore_errors: true + vars: + ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/vyos_ospf_interfaces/tests/cli/_remove_config.yaml b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/_remove_config.yaml new file mode 100644 index 0000000..9a2e699 --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/_remove_config.yaml @@ -0,0 +1,15 @@ +--- +- name: Remove pre-existing OSPF processes + vyos.vyos.vyos_config: + lines: + - delete interfaces ethernet eth0 ip ospf + - delete interfaces ethernet eth0 ipv6 ospfv3 + - delete interfaces ethernet eth1 ip ospf + - delete interfaces ethernet eth1 ipv6 ospfv3 + - delete interfaces bonding bond1 ip ospf + - delete interfaces bonding bond1 ipv6 ospfv3 + - delete interfaces bonding bond2 ip ospf + - delete interfaces bonding bond2 ipv6 ospfv3 + ignore_errors: true + vars: + ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/vyos_ospf_interfaces/tests/cli/deleted.yaml b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/deleted.yaml new file mode 100644 index 0000000..573e611 --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/deleted.yaml @@ -0,0 +1,42 @@ +--- +- debug: + msg: START vyos_ospf_interfaces 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_ospf_interfaces: &id001 + config: + - name: "bond2" + state: deleted + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: ospf_interfaces + + - assert: + that: + - result.commands|length == 2 + - result.changed == true + - result.commands|symmetric_difference(deleted.commands) == [] + - result.after|symmetric_difference(ansible_facts['network_resources']['ospf_interfaces']) == [] + + - name: Delete the existing configuration with the provided running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_ospf_interfaces: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_ospf_interfaces/tests/cli/empty_config.yaml b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/empty_config.yaml new file mode 100644 index 0000000..aff0f66 --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/empty_config.yaml @@ -0,0 +1,60 @@ +--- +- debug: + msg: START vyos_ospf_interfaces 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_ospf_interfaces: + 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_ospf_interfaces: + 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_ospf_interfaces: + 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_ospf_interfaces: + 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_ospf_interfaces: + 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_ospf_interfaces/tests/cli/gathered.yaml b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/gathered.yaml new file mode 100644 index 0000000..ec320c3 --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/gathered.yaml @@ -0,0 +1,24 @@ +--- +- debug: + msg: START vyos_ospf_interfaces 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_ospf_interfaces: + state: gathered + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: ospf_interfaces + + - assert: + that: + - result.changed == false + - result.gathered|symmetric_difference(ansible_facts['network_resources']['ospf_interfaces']) == [] diff --git a/tests/integration/targets/vyos_ospf_interfaces/tests/cli/merged.yaml b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/merged.yaml new file mode 100644 index 0000000..294b6f1 --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/merged.yaml @@ -0,0 +1,53 @@ +--- +- debug: + msg: START vyos_ospf_interfaces merged integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- block: + + - name: Merge the provided configuration with the exisiting running configuration + register: result + vyos.vyos.vyos_ospf_interfaces: &id001 + config: + - name: "eth0" + address_family: + - afi: "ipv4" + cost: 50 + priority: 26 + - afi: "ipv6" + mtu_ignore: true + instance: 33 + - name: "bond2" + address_family: + - afi: "ipv4" + transmit_delay: 45 + - afi: "ipv6" + passive: true + state: merged + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: ospf_interfaces + + - assert: + that: + - result.commands|length == 6 + - result.changed == true + - result.commands|symmetric_difference(merged.commands) == [] + - result.after|symmetric_difference(ansible_facts['network_resources']['ospf_interfaces']) == [] + + - name: Merge the provided configuration with the existing running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_ospf_interfaces: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_ospf_interfaces/tests/cli/overridden.yaml b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/overridden.yaml new file mode 100644 index 0000000..67bbca1 --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/overridden.yaml @@ -0,0 +1,49 @@ +--- +- debug: + msg: START vyos_ospf_interfaces overridden integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Override the existing configuration with the provided running configuration + register: result + vyos.vyos.vyos_ospf_interfaces: &id001 + config: + - name: "eth0" + address_family: + - afi: "ipv4" + transmit_delay: 50 + priority: 26 + network: "point-to-point" + - afi: "ipv6" + dead_interval: 39 + state: overridden + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: ospf_interfaces + + - assert: + that: + - result.commands|length == 8 + - result.changed == true + - result.commands|symmetric_difference(overridden.commands) == [] + - result.after|symmetric_difference(ansible_facts['network_resources']['ospf_interfaces']) == [] + + - name: Override the existing configuration with the provided running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_ospf_interfaces: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_ospf_interfaces/tests/cli/parsed.yaml b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/parsed.yaml new file mode 100644 index 0000000..a065a00 --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/parsed.yaml @@ -0,0 +1,16 @@ +--- +- debug: + msg: START vyos_ospfv3_interfaces 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_ospf_interfaces: + running_config: "{{ lookup('file', '_parsed.cfg') }}" + state: parsed + +- assert: + that: + - result.changed == false + - result.parsed|symmetric_difference(merged.after) == [] diff --git a/tests/integration/targets/vyos_ospf_interfaces/tests/cli/rendered.yaml b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/rendered.yaml new file mode 100644 index 0000000..328406b --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/rendered.yaml @@ -0,0 +1,33 @@ +--- +- debug: + msg: START vyos_ospf_interfaces rendered integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- block: + + - name: Structure provided configuration into device specific commands + register: result + vyos.vyos.vyos_ospf_interfaces: + config: + - name: "eth0" + address_family: + - afi: "ipv4" + cost: 50 + priority: 26 + - afi: "ipv6" + mtu_ignore: true + instance: 33 + - name: "bond2" + address_family: + - afi: "ipv4" + transmit_delay: 45 + - afi: "ipv6" + passive: true + state: rendered + + - assert: + that: + - result.changed == false + - result.rendered|symmetric_difference(merged.commands) == [] diff --git a/tests/integration/targets/vyos_ospf_interfaces/tests/cli/replaced.yaml b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/replaced.yaml new file mode 100644 index 0000000..ff32012 --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/replaced.yaml @@ -0,0 +1,60 @@ +--- +- debug: + msg: START vyos_ospf_interfaces 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_ospf_interfaces: &id001 + config: + - name: "eth0" + address_family: + - afi: "ipv4" + transmit_delay: 50 + priority: 26 + network: "point-to-point" + - afi: "ipv6" + dead_interval: 39 + - name: "bond2" + address_family: + - afi: "ipv4" + transmit_delay: 45 + bandwidth: 70 + authentication: + md5_key: + key_id: 10 + key: "1111111111232345" + - afi: "ipv6" + passive: true + state: replaced + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: ospf_interfaces + + - assert: + that: + - result.commands|length == 8 + - result.changed == true + - result.commands|symmetric_difference(replaced.commands) == [] + - result.after|symmetric_difference(ansible_facts['network_resources']['ospf_interfaces']) == [] + + - name: Replace the existing configuration with the provided running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_ospf_interfaces: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_ospf_interfaces/tests/cli/rtt.yaml b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/rtt.yaml new file mode 100644 index 0000000..46eb79d --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/tests/cli/rtt.yaml @@ -0,0 +1,70 @@ +--- +- debug: + msg: START vyos_ospf_interfaces rtt integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + + +- block: + + - name: Merge the provided configuration with the exisiting running configuration + register: baseconfig + vyos.vyos.vyos_ospf_interfaces: &id001 + config: + - name: "eth0" + address_family: + - afi: "ipv4" + cost: 50 + priority: 26 + - afi: "ipv6" + mtu_ignore: true + instance: 33 + - name: "bond2" + address_family: + - afi: "ipv4" + transmit_delay: 45 + - afi: "ipv6" + passive: true + state: merged + + - become: true + vyos.vyos.vyos_facts: + gather_network_resources: ospf_interfaces + + - assert: + that: + - baseconfig.commands|length == 6 + - baseconfig.changed == true + - baseconfig.commands|symmetric_difference(merged.commands) == [] + - baseconfig.after|symmetric_difference(ansible_facts['network_resources']['ospf_interfaces']) == [] + + - name: Apply the provided configuration (config to be reverted) + become: true + register: result + vyos.vyos.vyos_ospf_interfaces: + config: + - name: "eth0" + address_family: + - afi: "ipv4" + transmit_delay: 50 + priority: 26 + network: "point-to-point" + - afi: "ipv6" + dead_interval: 39 + + + - name: Revert back to base config using facts round trip + become: true + register: revert + vyos.vyos.vyos_ospf_interfaces: + config: "{{ ansible_facts['network_resources']['ospf_interfaces'] }}" + 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_ospf_interfaces/vars/main.yaml b/tests/integration/targets/vyos_ospf_interfaces/vars/main.yaml new file mode 100644 index 0000000..a98d351 --- /dev/null +++ b/tests/integration/targets/vyos_ospf_interfaces/vars/main.yaml @@ -0,0 +1,133 @@ +--- +merged: + commands: + - set interfaces ethernet eth0 ip ospf cost 50 + - set interfaces ethernet eth0 ip ospf priority 26 + - set interfaces ethernet eth0 ipv6 ospfv3 mtu-ignore + - set interfaces ethernet eth0 ipv6 ospfv3 instance-id 33 + - set interfaces bonding bond2 ip ospf transmit-delay 45 + - set interfaces bonding bond2 ipv6 ospfv3 passive + after: + - address_family: + - afi: "ipv4" + transmit_delay: 45 + - afi: "ipv6" + passive: true + name: "bond2" + - address_family: + - afi: "ipv4" + cost: 50 + priority: 26 + - afi: "ipv6" + instance: "33" + mtu_ignore: true + name: "eth0" + +replaced: + commands: + - set interfaces ethernet eth0 ip ospf transmit-delay 50 + - set interfaces ethernet eth0 ip ospf network point-to-point + - set interfaces ethernet eth0 ipv6 ospfv3 dead-interval 39 + - delete interfaces ethernet eth0 ip ospf cost 50 + - delete interfaces ethernet eth0 ipv6 ospfv3 instance-id 33 + - delete interfaces ethernet eth0 ipv6 ospfv3 mtu-ignore + - set interfaces bonding bond2 ip ospf bandwidth 70 + - set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key 1111111111232345 + + after: + - address_family: + - afi: "ipv4" + authentication: + md5_key: + key: "1111111111232345" + key_id: 10 + bandwidth: 70 + transmit_delay: 45 + - afi: "ipv6" + passive: true + name: "bond2" + - address_family: + - afi: "ipv4" + network: "point-to-point" + priority: 26 + transmit_delay: 50 + - afi: "ipv6" + dead_interval: 39 + name: "eth0" + +overridden: + commands: + - delete interfaces bonding bond2 ip ospf + - delete interfaces bonding bond2 ipv6 ospfv3 + - set interfaces ethernet eth0 ip ospf transmit-delay 50 + - set interfaces ethernet eth0 ip ospf network point-to-point + - set interfaces ethernet eth0 ipv6 ospfv3 dead-interval 39 + - delete interfaces ethernet eth0 ip ospf cost 50 + - delete interfaces ethernet eth0 ipv6 ospfv3 instance-id 33 + - delete interfaces ethernet eth0 ipv6 ospfv3 mtu-ignore + + after: + - address_family: + - afi: "ipv4" + network: "point-to-point" + priority: 26 + transmit_delay: 50 + - afi: "ipv6" + dead_interval: 39 + name: "eth0" + +deleted: + commands: + - delete interfaces bonding bond2 ip ospf + - delete interfaces bonding bond2 ipv6 ospfv3 + after: + - address_family: + - afi: "ipv4" + cost: 50 + priority: 26 + - afi: "ipv6" + instance: "33" + mtu_ignore: true + name: "eth0" + +rendered: + commands: + - set interfaces ethernet eth1 firewall in name 'INBOUND' + - set interfaces ethernet eth1 firewall out name 'OUTBOUND' + - set interfaces ethernet eth1 firewall local name 'LOCAL' + - set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' + - set interfaces ethernet eth2 firewall in name 'INBOUND' + - set interfaces ethernet eth2 firewall out name 'OUTBOUND' + - set interfaces ethernet eth2 firewall local name 'LOCAL' + - set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL' +round_trip: + after: + - name: eth0 + - access_rules: + - afi: ipv4 + rules: + - direction: in + name: INBOUND + - direction: local + name: LOCAL + - direction: out + name: OUTBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL + name: eth1 + - name: eth2 + access_rules: + - afi: ipv4 + rules: + - direction: in + name: INBOUND + - direction: local + name: LOCAL + - direction: out + name: OUTBOUND + - afi: ipv6 + rules: + - direction: local + name: V6-LOCAL diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_ospf_interfaces_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_ospf_interfaces_config.cfg new file mode 100644 index 0000000..1fab55e --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_ospf_interfaces_config.cfg @@ -0,0 +1,4 @@ +set interfaces ethernet eth0 ipv6 ospfv3 instance-id '33' +set interfaces ethernet eth0 ipv6 ospfv3 'mtu-ignore' +set interfaces ethernet eth1 ip ospf cost '100' +set interfaces ethernet eth1 ipv6 ospfv3 ifmtu '33' diff --git a/tests/unit/modules/network/vyos/test_vyos_ospf_interfaces.py b/tests/unit/modules/network/vyos/test_vyos_ospf_interfaces.py new file mode 100644 index 0000000..3250d11 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_ospf_interfaces.py @@ -0,0 +1,461 @@ +# (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_ospf_interfaces +from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( + set_module_args, +) +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosOspfInterfacesModule(TestVyosModule): + + module = vyos_ospf_interfaces + + def setUp(self): + super(TestVyosOspfInterfacesModule, self).setUp() + self.mock_get_resource_connection_config = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module.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.ospf_interfaces.ospf_interfaces.Ospf_interfacesFacts.get_device_data" + ) + self.execute_show_command = self.mock_execute_show_command.start() + + def tearDown(self): + super(TestVyosOspfInterfacesModule, 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_ospf_interfaces_config.cfg" + + def load_from_file(*args, **kwargs): + output = load_fixture(filename) + return output + + self.execute_show_command.side_effect = load_from_file + + def sort_address_family(self, entry_list): + for entry in entry_list: + if entry.get("address_family"): + entry["address_family"].sort(key=lambda i: i.get("afi")) + + def test_vyos_ospf_interfaces_merged_new_config(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + address_family=[ + dict( + afi="ipv4", + cost=100, + authentication=dict( + plaintext_password="abcdefg!" + ), + priority=55, + ), + dict(afi="ipv6", mtu_ignore=True, instance=20), + ], + ), + dict( + name="bond2", + address_family=[ + dict( + afi="ipv4", + transmit_delay=9, + ), + dict(afi="ipv6", passive=True), + ], + ), + ], + state="merged", + ) + ) + commands = [ + "set interfaces bonding bond2 ip ospf transmit-delay 9", + "set interfaces bonding bond2 ipv6 ospfv3 passive", + "set interfaces ethernet eth0 ip ospf cost 100", + "set interfaces ethernet eth0 ip ospf priority 55", + "set interfaces ethernet eth0 ip ospf authentication plaintext-password abcdefg!", + "set interfaces ethernet eth0 ipv6 ospfv3 instance-id 20", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_ospf_interfaces_merged_idempotent(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + address_family=[ + dict(afi="ipv6", mtu_ignore=True, instance=33), + ], + ), + dict( + name="eth1", + address_family=[ + dict( + afi="ipv4", + cost=100, + ), + dict(afi="ipv6", ifmtu=33), + ], + ), + ], + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_ospf_interfaces_existing_config_merged(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + address_family=[ + dict(afi="ipv6", cost=500), + ], + ), + dict( + name="eth1", + address_family=[ + dict( + afi="ipv4", + priority=100, + ), + dict(afi="ipv6", ifmtu=25), + ], + ), + ], + ) + ) + commands = [ + "set interfaces ethernet eth0 ipv6 ospfv3 cost 500", + "set interfaces ethernet eth1 ip ospf priority 100", + "set interfaces ethernet eth1 ipv6 ospfv3 ifmtu 25", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_ospf_interfaces_replaced(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + address_family=[ + dict( + afi="ipv4", + cost=100, + authentication=dict( + plaintext_password="abcdefg!" + ), + priority=55, + ), + ], + ), + dict( + name="bond2", + address_family=[ + dict( + afi="ipv4", + transmit_delay=9, + ), + dict(afi="ipv6", passive=True), + ], + ), + ], + state="replaced", + ) + ) + commands = [ + "set interfaces bonding bond2 ip ospf transmit-delay 9", + "set interfaces bonding bond2 ipv6 ospfv3 passive", + "set interfaces ethernet eth0 ip ospf cost 100", + "set interfaces ethernet eth0 ip ospf priority 55", + "set interfaces ethernet eth0 ip ospf authentication plaintext-password abcdefg!", + "delete interfaces ethernet eth0 ipv6 ospfv3 instance-id 33", + "delete interfaces ethernet eth0 ipv6 ospfv3 mtu-ignore", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_ospf_interfaces_replaced_idempotent(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + address_family=[ + dict(afi="ipv6", mtu_ignore=True, instance=33), + ], + ), + dict( + name="eth1", + address_family=[ + dict( + afi="ipv4", + cost=100, + ), + dict(afi="ipv6", ifmtu=33), + ], + ), + ], + state="replaced", + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_ospf_interfaces_overridden(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + address_family=[ + dict( + afi="ipv4", + cost=100, + authentication=dict( + plaintext_password="abcdefg!" + ), + priority=55, + ), + ], + ), + dict( + name="bond2", + address_family=[ + dict( + afi="ipv4", + transmit_delay=9, + ), + dict(afi="ipv6", passive=True), + ], + ), + ], + state="overridden", + ) + ) + commands = [ + "set interfaces bonding bond2 ip ospf transmit-delay 9", + "set interfaces bonding bond2 ipv6 ospfv3 passive", + "set interfaces ethernet eth0 ip ospf cost 100", + "set interfaces ethernet eth0 ip ospf priority 55", + "set interfaces ethernet eth0 ip ospf authentication plaintext-password abcdefg!", + "delete interfaces ethernet eth1 ip ospf", + "delete interfaces ethernet eth1 ipv6 ospfv3", + "delete interfaces ethernet eth0 ipv6 ospfv3 mtu-ignore", + "delete interfaces ethernet eth0 ipv6 ospfv3 instance-id 33", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_ospf_interfaces_overridden_idempotent(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + address_family=[ + dict(afi="ipv6", mtu_ignore=True, instance=33), + ], + ), + dict( + name="eth1", + address_family=[ + dict( + afi="ipv4", + cost=100, + ), + dict(afi="ipv6", ifmtu=33), + ], + ), + ], + state="overridden", + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_ospf_interfaces_deleted(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + ), + ], + state="deleted", + ) + ) + commands = ["delete interfaces ethernet eth0 ipv6 ospfv3"] + self.execute_module(changed=True, commands=commands) + + def test_vyos_ospf_interfaces_notpresent_deleted(self): + set_module_args( + dict( + config=[ + dict( + name="eth3", + ), + ], + state="deleted", + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_ospf_interfaces_rendered(self): + set_module_args( + dict( + config=[ + dict( + name="eth0", + address_family=[ + dict( + afi="ipv4", + cost=100, + authentication=dict( + plaintext_password="abcdefg!" + ), + priority=55, + ), + dict(afi="ipv6", mtu_ignore=True, instance=20), + ], + ), + dict( + name="bond2", + address_family=[ + dict( + afi="ipv4", + transmit_delay=9, + ), + dict(afi="ipv6", passive=True), + ], + ), + ], + state="rendered", + ) + ) + commands = [ + "set interfaces ethernet eth0 ip ospf cost 100", + "set interfaces ethernet eth0 ip ospf authentication plaintext-password abcdefg!", + "set interfaces ethernet eth0 ip ospf priority 55", + "set interfaces ethernet eth0 ipv6 ospfv3 mtu-ignore", + "set interfaces ethernet eth0 ipv6 ospfv3 instance-id 20", + "set interfaces bonding bond2 ip ospf transmit-delay 9", + "set interfaces bonding bond2 ipv6 ospfv3 passive", + ] + result = self.execute_module(changed=False) + self.assertEqual( + sorted(result["rendered"]), sorted(commands), result["rendered"] + ) + + def test_vyos_ospf_interfaces_parsed(self): + commands = [ + "set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345'", + "set interfaces bonding bond2 ip ospf bandwidth '70'", + "set interfaces bonding bond2 ip ospf transmit-delay '45'", + "set interfaces bonding bond2 ipv6 ospfv3 'passive'", + "set interfaces ethernet eth0 ip ospf cost '50'", + "set interfaces ethernet eth0 ip ospf priority '26'", + "set interfaces ethernet eth0 ipv6 ospfv3 instance-id '33'", + "set interfaces ethernet eth0 ipv6 ospfv3 'mtu-ignore'", + "set interfaces ethernet eth1 ip ospf network 'point-to-point'", + "set interfaces ethernet eth1 ip ospf priority '26'", + "set interfaces ethernet eth1 ip ospf transmit-delay '50'", + "set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39'", + ] + + parsed_str = "\n".join(commands) + set_module_args(dict(running_config=parsed_str, state="parsed")) + result = self.execute_module(changed=False) + parsed_list = [ + { + "address_family": [ + { + "afi": "ipv4", + "authentication": { + "md5_key": { + "key": "1111111111232345", + "key_id": 10, + } + }, + "bandwidth": 70, + "transmit_delay": 45, + }, + {"afi": "ipv6", "passive": True}, + ], + "name": "bond2", + }, + { + "address_family": [ + {"afi": "ipv4", "cost": 50, "priority": 26}, + {"afi": "ipv6", "instance": "33", "mtu_ignore": True}, + ], + "name": "eth0", + }, + { + "address_family": [ + { + "afi": "ipv4", + "network": "point-to-point", + "priority": 26, + "transmit_delay": 50, + }, + {"afi": "ipv6", "dead_interval": 39}, + ], + "name": "eth1", + }, + ] + result_list = self.sort_address_family(result["parsed"]) + given_list = self.sort_address_family(parsed_list) + self.assertEqual(result_list, given_list) + + def test_vyos_ospf_interfaces_gathered(self): + set_module_args(dict(state="gathered")) + result = self.execute_module( + changed=False, filename="vyos_ospf_interfaces_config.cfg" + ) + gathered_list = [ + { + "address_family": [ + {"afi": "ipv6", "instance": "33", "mtu_ignore": True} + ], + "name": "eth0", + }, + { + "address_family": [ + {"afi": "ipv4", "cost": 100}, + {"afi": "ipv6", "ifmtu": 33}, + ], + "name": "eth1", + }, + ] + + result_list = self.sort_address_family(result["gathered"]) + given_list = self.sort_address_family(gathered_list) + self.assertEqual(result_list, given_list)