From 37289b45840129f2296fbc9cff9a3eab97bdb2a5 Mon Sep 17 00:00:00 2001 From: Rohit Thakur Date: Thu, 2 Apr 2020 00:37:06 +0530 Subject: vyos ospfv2 rm added Signed-off-by: Rohit Thakur --- .../network/vyos/argspec/ospfv2/__init__.py | 0 .../network/vyos/argspec/ospfv2/ospfv2.py | 359 +++++ .../network/vyos/config/ospfv2/__init__.py | 0 .../network/vyos/config/ospfv2/ospfv2.py | 570 ++++++++ plugins/module_utils/network/vyos/facts/facts.py | 3 + .../network/vyos/facts/ospfv2/__init__.py | 0 .../network/vyos/facts/ospfv2/ospfv2.py | 395 ++++++ plugins/module_utils/network/vyos/utils/utils.py | 9 +- plugins/modules/vyos_facts.py | 2 +- plugins/modules/vyos_ospfv2.py | 1369 ++++++++++++++++++++ 10 files changed, 2699 insertions(+), 8 deletions(-) create mode 100644 plugins/module_utils/network/vyos/argspec/ospfv2/__init__.py create mode 100644 plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py create mode 100644 plugins/module_utils/network/vyos/config/ospfv2/__init__.py create mode 100644 plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py create mode 100644 plugins/module_utils/network/vyos/facts/ospfv2/__init__.py create mode 100644 plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py create mode 100644 plugins/modules/vyos_ospfv2.py (limited to 'plugins') diff --git a/plugins/module_utils/network/vyos/argspec/ospfv2/__init__.py b/plugins/module_utils/network/vyos/argspec/ospfv2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py new file mode 100644 index 00000000..1b11d3c9 --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py @@ -0,0 +1,359 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 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 arg spec for the vyos_ospfv2 module +""" + + +class Ospfv2Args(object): # pylint: disable=R0903 + """The arg spec for the vyos_ospfv2 module + """ + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'elements': 'dict', + 'options': { + 'auto_cost': { + 'options': { + 'reference_bandwidth': { + 'type': 'int' + } + }, + 'type': 'dict' + }, + 'default_information': { + 'options': { + 'originate': { + 'options': { + 'always': { + 'type': 'bool' + }, + 'metric': { + 'type': 'int' + }, + 'metric_type': { + 'type': 'int' + }, + 'route_map': { + 'type': 'str' + } + }, + 'type': 'dict' + } + }, + 'type': 'dict' + }, + 'default_metric': { + 'type': 'int' + }, + 'distance': { + 'options': { + 'global': { + 'type': 'int' + }, + 'ospf': { + 'options': { + 'external': { + 'type': 'int' + }, + 'inter_area': { + 'type': 'int' + }, + 'intra_area': { + 'type': 'int' + } + }, + 'type': 'dict' + } + }, + 'type': 'dict' + }, + 'log_adjacency_changes': { + 'choices': ['detail'], + 'type': 'str' + }, + 'max_metric': { + 'options': { + 'router_lsa': { + 'options': { + 'administrative': { + 'type': 'bool' + }, + 'on_shutdown': { + 'type': 'int' + }, + 'on_startup': { + 'type': 'int' + } + }, + 'type': 'dict' + } + }, + 'type': 'dict' + }, + 'mpls_te': { + 'options': { + 'enabled': { + 'type': 'bool' + }, + 'router_address': { + 'type': 'str' + } + }, + 'type': 'dict' + }, + 'neighbor': { + 'elements': 'dict', + 'options': { + 'neighbor_id': { + 'type': 'str' + }, + 'poll_interval': { + 'type': 'int' + }, + 'priority': { + 'type': 'int' + } + }, + 'type': 'list' + }, + 'ospf_area': { + 'elements': 'dict', + 'options': { + 'area': { + 'type': 'str' + }, + 'area_type': { + 'options': { + 'normal': { + 'type': 'bool' + }, + 'nssa': { + 'options': { + 'default_cost': { + 'type': 'int' + }, + 'no_summary': { + 'type': 'bool' + }, + 'translate': { + 'choices': + ['always', 'candidate', 'never'], + 'type': + 'str' + } + }, + 'type': 'dict' + }, + 'stub': { + 'options': { + 'default_cost': { + 'type': 'int' + }, + 'no_summary': { + 'type': 'bool' + } + }, + 'type': 'dict' + } + }, + 'type': 'dict' + }, + 'authentication': { + 'choices': ['plaintext-password', 'md5'], + 'type': 'str' + }, + 'network': { + 'elements': 'dict', + 'options': { + 'address': { + 'required': True, + 'type': 'str' + } + }, + 'type': 'list' + }, + 'range': { + 'elements': 'dict', + 'options': { + 'address': { + 'type': 'str' + }, + 'cost': { + 'type': 'int' + }, + 'not_advertise': { + 'type': 'bool' + }, + 'substitute': { + 'type': 'str' + } + }, + 'type': 'list' + }, + 'shortcut': { + 'choices': ['default', 'disable', 'enable'], + 'type': 'str' + }, + 'virtual_link': { + 'elements': 'dict', + 'options': { + 'address': { + 'type': 'str' + }, + 'authentication': { + 'options': { + 'md5': { + 'options': { + 'key_id': { + 'type': 'int' + }, + 'md5_key': { + 'type': 'str' + } + }, + 'type': 'dict' + }, + 'plaintext_password': { + 'type': 'str' + } + }, + 'type': 'dict' + }, + 'dead_interval': { + 'type': 'int' + }, + 'hello_interval': { + 'type': 'int' + }, + 'retransmit_interval': { + 'type': 'int' + }, + 'transmit_delay': { + 'type': 'int' + } + }, + 'type': 'list' + } + }, + 'type': 'list' + }, + 'parameters': { + 'options': { + 'abr_type': { + 'choices': + ['cisco', 'ibm', 'shortcut', 'standard'], + 'type': 'str' + }, + 'opaque_lsa': { + 'type': 'bool' + }, + 'rfc1583_compatibility': { + 'type': 'bool' + }, + 'router_id': { + 'type': 'str' + } + }, + 'type': 'dict' + }, + 'passive_interface': { + 'type': 'list' + }, + 'passive_interface_exclude': { + 'type': 'list' + }, + 'redistribute': { + 'elements': 'dict', + 'options': { + 'metric': { + 'type': 'int' + }, + 'metric_type': { + 'type': 'int' + }, + 'route_map': { + 'type': 'str' + }, + 'route_type': { + 'choices': + ['bgp', 'connected', 'kernel', 'rip', 'static'], + 'type': + 'str' + } + }, + 'type': 'list' + }, + 'route_map': { + 'type': 'list' + }, + 'timers': { + 'options': { + 'refresh': { + 'options': { + 'timers': { + 'type': 'int' + } + }, + 'type': 'dict' + }, + 'throttle': { + 'options': { + 'spf': { + 'options': { + 'delay': { + 'type': 'int' + }, + 'initial_holdtime': { + 'type': 'int' + }, + 'max_holdtime': { + 'type': 'int' + } + }, + 'type': 'dict' + } + }, + 'type': 'dict' + } + }, + 'type': 'dict' + } + }, + 'type': 'list' + }, + "running_config": {"type": "str"}, + 'state': { + 'choices': [ + 'merged', 'replaced', 'deleted', 'parsed', 'gathered', + 'rendered' + ], + 'default': + 'merged', + 'type': + 'str' + } + } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/ospfv2/__init__.py b/plugins/module_utils/network/vyos/config/ospfv2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py new file mode 100644 index 00000000..0109ca1a --- /dev/null +++ b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py @@ -0,0 +1,570 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos_ospfv2 class +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 it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + remove_empties, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts +from ansible.module_utils.six import iteritems + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( + list_diff_want_only, _in_target, _is_w_same, _bool_to_str +) + +class Ospfv2(ConfigBase): + """ + The vyos_ospfv2 class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'ospfv2', + ] + + def __init__(self, module): + super(Ospfv2, self).__init__(module) + + def get_ospfv2_facts(self, data=None): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources, data=data) + ospfv2_facts = facts['ansible_network_resources'].get('ospfv2') + if not ospfv2_facts: + return [] + return ospfv2_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {'changed': False} + warnings = list() + commands = list() + + if self.state in self.ACTION_STATES: + existing_ospfv2_facts = self.get_ospfv2_facts() + else: + existing_ospfv2_facts = [] + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_ospfv2_facts)) + + if commands and self.state in self.ACTION_STATES: + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True + + if self.state in self.ACTION_STATES: + result["commands"] = commands + + if self.state in self.ACTION_STATES or self.state == "gathered": + changed_ospfv2_facts = self.get_ospfv2_facts() + elif self.state == "rendered": + result["rendered"] = commands + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed" + ) + result["parsed"] = self.get_ospfv2_facts( + data=running_config + ) + else: + changed_ospfv2_facts = [] + + if self.state in self.ACTION_STATES: + result["before"] = existing_ospfv2_facts + if result["changed"]: + result["after"] = changed_ospfv2_facts + elif self.state == "gathered": + result["gathered"] = changed_ospfv2_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_ospfv2_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params['config'] + have = existing_ospfv2_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, w, h): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + if self.state in ("merged", "replaced", "overridden", "rendered") and not w: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + self.state + ) + ) + if self.state == "overridden": + commands.extend(self._state_overridden(w, h)) + elif self.state == "deleted": + commands.extend(self._state_deleted(w, h)) + elif w: + if self.state == "merged" or self.state == "rendered": + for w_item in w: + commands.extend(self._state_merged(w_item, h)) + elif self.state == "replaced": + for w_item in w: + commands.extend(self._state_replaced(w_item, h)) + return commands + + def search_obj_in_have(self, have, w_name, key): + """ + This function returns the rule-set/rule if it is present in target config. + :param have: target config. + :param w_name: rule-set name. + :param type: rule_sets/rule/r_list. + :return: rule-set/rule. + """ + if have: + for item in have: + if item[key] == w_name[key]: + return item + return None + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + h_item = {} + if have: + h_item = have[0] + commands.extend(self._render_ospf_param(h_item, want, opr=False)) + commands.extend(self._render_ospf_param(want, h_item)) + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + h_item = {} + if have: + h_item = have[0] + commands.extend(self._render_ospf_param(want, h_item)) + return commands + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + if want: + for w in want: + if have: + h = have[0] + if h: + for key, val in iteritems(w): + if key in h: + if key == 'ospf_area': + key = 'area' + commands.append(self._compute_command(attr=key, opr=False)) + elif have and have[0]: + commands.append('delete protocols ospf') + return commands + + def _render_ospf_param(self, want, have, opr=True): + """ + This function forms the set/delete commands for ospf leaf attributes + and triggers the process for other child attributes. + for firewall_global attributes. + :param w: the desired config. + :param h: the target config. + :param opr: True/False. + :return: generated commands list. + """ + commands = [] + w = deepcopy(remove_empties(want)) + leaf = ( + "default_metric", + "log_adjacency_changes" + ) + if w: + for key, val in iteritems(w): + if opr and key in leaf and not _is_w_same(w, have, key): + commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) + elif not opr and key in leaf and not _in_target(have, key): + commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) + else: + commands.extend(self._render_child_param(w, have, key, opr)) + return commands + + def _render_child_param(self, w, h, key, opr=True): + """ + This function invoke the function to extend commands + based on the key. + :param w: the desired configuration. + :param h: the current configuration. + :param key: attribute name. + :param opr: operation. + :return: list of commands. + """ + commands = [] + if key in ("neighbor", "redistribute"): + commands.extend(self._render_list_dict_param(key, w, h, opr=opr)) + elif key in ("default_information", 'max_metric'): + commands.extend(self._render_nested_dict_param(key, w, h, opr=opr)) + elif key in ("mpls_te", "auto_cost", "parameters", "auto_cost"): + commands.extend(self._render_dict_param(key, w, h, opr=opr)) + elif key in ("route_map", "passive_interface", "passive_interface_exclude"): + commands.extend(self._render_list_param(key, w, h, opr=opr)) + elif key == "ospf_area": + commands.extend(self._render_ospf_area(key, w, h, opr=opr)) + elif key == "timers": + commands.extend(self._render_timers(key, w, h, opr=opr, remove=remove)) + elif key == "distance": + commands.extend(self._render_distance(key, w, h, opr=opr, remove=remove)) + return commands + + def _render_dict_param(self, attr, want, have, opr=True): + """ + This function generate the commands for dictionary elements. + :param attr: attribute name. + :param w: the desired configuration. + :param h: the target config. + :param opr: True/False. + :return: generated list of commands. + """ + commands = [] + h = {} + if have: + h = have.get(attr) or {} + if not opr and not h: + commands.append(self._form_attr_cmd(attr=attr, opr=opr)) + elif want[attr]: + leaf_dict = {'auto_cost': 'reference_bandwidth', + 'mpls_te': ('enabled', 'router_address'), + 'parameters': ("router_id", "abr_type", "opaque_lsa", "rfc1583_compatibility")} + leaf = leaf_dict[attr] + for item, value in iteritems(want[attr]): + if opr and item in leaf and not _is_w_same(want[attr], h, item): + if item == "enabled": + item = 'enable' + if item in ("opaque_lsa", "enable", "rfc1583_compatibility"): + commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) + else: + commands.append(self._form_attr_cmd(key=attr, attr=item, val=value, opr=opr)) + elif not opr and item in leaf and not _in_target(h, item): + if item == "enabled": + commands.append(self._form_attr_cmd(key=attr, attr='enable', opr=opr)) + else: + commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) + return commands + + def _render_list_param(self, attr, want, have, cmd=None, opr=True): + """ + This function forms the commands for passed target list attributes'. + :param attr: attribute name. + :param w: the desired config. + :param h: the target config. + :param cmd: commands to be prepend. + :param opr: True/False. + :return: generated list of commands. + """ + commands = [] + h = [] + if want: + w = want.get(attr) or [] + if have: + h = have.get(attr) or [] + if not cmd: + cmd = self._compute_command(opr=opr) + if w: + if opr: + members = list_diff_want_only(w, h) + for member in members: + command = cmd + attr.replace("_","-") + " " + if attr == 'network': + command += member['address'] + else: + command += member + commands.append(command) + elif not opr: + if h: + for member in w: + if attr == 'network': + if not self.search_obj_in_have(h, member, 'address'): + commands.append(cmd + attr.replace("_","-") + ' ' + member['address']) + elif member not in h: + commands.append(cmd + attr.replace("_","-") + ' ' + member) + else: + commands.append(cmd + " " + attr.replace("_","-")) + return commands + + def _render_list_dict_param(self, attr, want, have, cmd=None, opr=True): + """ + This function forms the set/delete commands based on the 'opr' type + for attributes with in desired list of dictionary. + :param attr: attribute name. + :param w: the desired config. + :param h: the target config. + :param cmd: commands to be prepend. + :param opr: True/False. + :return: generated commands list. + """ + commands = [] + h = [] + name = {'redistribute': 'route_type', + 'neighbor': 'neighbor_id', + 'range': 'address', + 'vlink': 'address'} + leaf_dict = {'redistribute': ("metric", "route_map", "route_type", "metric_type"), + 'neighbor': ("priority", "poll_interval", "neighbor_id"), + 'range': ("cost", "address", "substitute", "not_advertise"), + 'vlink': ("address", "dead_interval", "transmit_delay", "hello_interval", "retransmit_interval") + } + leaf = leaf_dict[attr] + w = want.get(attr) or [] + if have: + h = have.get(attr) or [] + if not opr and not h: + commands.append(self._compute_command(attr=attr, opr=opr)) + elif w: + for w_item in w: + for key, val in iteritems(w_item): + if not cmd: + cmd = self._compute_command(opr=opr) + h_item = self.search_obj_in_have(h, w_item, name[attr]) + if opr and key in leaf and not _is_w_same(w_item, h_item, key): + if key == 'cost': + commands.append(cmd + attr + ' ' + w_item[name[attr]] + ' ' + key + ' ' + str(val)) + elif key == 'not_advertise': + commands.append(cmd + attr + ' ' + w_item[name[attr]] + ' ' + key.replace("_","-")) + elif key in ('route_type', 'neighbor_id', 'address'): + commands.append(cmd + attr + ' ' + str(val)) + elif key == 'authentication': + commands.append(self._render_vlink(key, w_item, h_item, cmd, opr)) + else: + commands.append(cmd + attr + ' ' + w_item[name[attr]] + ' ' + key.replace("_", "-") + ' ' + str(val)) + elif not opr and key in leaf and not _in_target(h_item, key): + if key in ('route_type', 'neighbor_id', 'address'): + commands.append(cmd + attr + ' ' + str(val)) + else: + commands.append(cmd + (attr + ' ' + w_item[name[attr]] + ' ' + key)) + return commands + + def _render_nested_dict_param(self, attr, want, have, opr=True): + """ + This function forms the set/delete commands based on the 'opr' type + for attributes with in desired nested dicts. + :param attr: attribute name. + :param w: the desired config. + :param h: the target config. + :param cmd: commands to be prepend. + :param opr: True/False. + :return: generated commands list. + """ + commands = [] + attr_dict = {'default_information': 'originate', + 'max_metric': 'router_lsa', + } + leaf_dict = {'default_information': ("always", "metric", "metric_type", "route_map"), + 'max_metric': ("always", "metric", "metric_type", "route_map"), + } + h = {} + w = want.get(attr) or {} + if have: + h = have.get(attr) or {} + if not opr and not h: + commands.append(self._form_attr_cmd(attr=attr, opr=opr)) + elif w: + key = attr_dict[attr] + w_attrib = want[attr].get(key) or {} + cmd = self._compute_command(opr=opr) + h_attrib = {} + if w_attrib: + leaf = leaf_dict[attr] + if h and key in h.keys(): + h_attrib = h.get(key) or {} + for item, val in iteritems(w[key]): + if opr and item in leaf and not _is_w_same(w[key], h_attrib, item): + if item in ('administrative', 'always'): + commands.append(cmd + (attr.replace("_","-") + " " + key.replace("_","-") + " " + item.replace("_","-"))) + else: + commands.append(cmd + (attr.replace("_","-") + " " + key.replace("_","-") + " " + item.replace("_","-") + " " + str(val))) + + elif not opr and item in leaf and not _in_target(h_attrib, item): + commands.append(cmd + (attr + " " + item)) + return commands + + def _render_ospf_area(self, attr, want, have, opr=True): + """ + This function forms the set/delete commands based on the 'opr' type + for ospf area attributes. + :param attr: attribute name. + :param w: the desired config. + :param h: the target config. + :param opr: True/False. + :return: generated commands list. + """ + commands = [] + h_lst = {} + w_lst = want.get(attr) or [] + l_set = ("area", "shortcut", "authentication") + if have: + h_lst = have.get(attr) or [] + if not opr and not h_lst: + commands.append(self._form_attr_cmd(attr='area', opr=opr)) + elif w_lst: + for w_area in w_lst: + cmd = self._compute_command(key='area', attr=_bool_to_str(w_area['area']), opr=opr) + ' ' + h_area = self.search_obj_in_have(h_lst, w_area, 'area') + if not opr and not h_area: + commands.append(self._form_attr_cmd(key='area', attr=w_area['area'], opr=opr)) + else: + for key, val in iteritems(w_area): + if opr and key in l_set and not _is_w_same(w_area, h_area, key): + if key == 'area': + commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) + else: + commands.append(cmd + key + ' ' + _bool_to_str(val).replace("_","-")) + elif not opr and key in l_set: + if key == 'area' and not _in_target(h_area, key): + commands.append(cmd) + continue + elif key != 'area' and not _in_target(h_area, key): + commands.append(cmd + val + ' ' + key) + elif key == 'area_type': + commands.extend(self._render_area_type(w_area, h_area, key, cmd, opr)) + elif key == 'network': + commands.extend(self._render_list_param(key, w_area, h_area, cmd, opr)) + elif key == 'range': + commands.extend(self._render_list_dict_param(key, w_area, h_area, cmd, opr)) + elif key == 'virtual_link': + commands.extend(self._render_vlink(key, w_area, h_area, cmd, opr)) + return commands + + def _render_area_type(self, want, have, attr, cmd, opr=True): + """ + This function forms the set/delete commands based on the 'opr' type + for area_types attributes. + :param attr: attribute name. + :param w: the desired config. + :param h: the target config. + :param cmd: command to prepend. + :param opr: True/False. + :return: generated commands list. + """ + commands = [] + h_type = {} + w_type = want.get(attr) or [] + if have: + h_type = have.get(attr) or {} + if not opr and not h_type: + commands.append(cmd + attr.replace("_","-")) + elif w_type: + key = "normal" + if opr and key in w_type.keys() and not _is_w_same(w_type, h_type, key): + commands.append(cmd + attr.replace("_","-") + ' ' + key) + elif not opr and key in w_type.keys() and not (h_type and key in h_type.keys()): + commands.append(cmd + want['area'] + ' ' + attr.replace("_","-")) + + a_type = {'nssa': ("default_cost", "no_summary", "translate"), + 'stub': ("default_cost", "no_summary")} + for key in a_type: + w_area = want[attr].get(key) or {} + h_area = {} + if w_area: + if h_type and key in h_type.keys(): + h_area = h_type.get(key) or {} + for item, val in iteritems(w_type[key]): + if opr and item in a_type[key] and not _is_w_same(w_type[key], h_area, item): + commands.append(cmd + (attr.replace("_","-") + " " + key + " " + item.replace("_","-") + " " + str(val))) + elif not opr and item in a_type[key] and not (h_type and key in h_type): + commands.append(cmd + (want['area'] + ' ' + attr.replace("_","-") + " " + key + " " + item.replace("_","-"))) + return commands + + def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True): + """ + This function forms the command for leaf attribute. + :param key: parent key. + :param attr: attribute name + :param value: value + :param opr: True/False. + :return: generated command. + """ + return self._compute_command(key, attr=self._map_attrib(attr), val=val, opr=opr) + + def _compute_command(self, key=None, attr=None, val=None, remove=False, opr=True): + """ + This function construct the add/delete command based on passed attributes. + :param key: parent key. + :param attr: attribute name + :param value: value + :param opr: True/False. + :return: generated command. + """ + if remove or not opr: + cmd = "delete protocols ospf " + else: + cmd = "set protocols ospf " + if key: + cmd += key.replace("_", "-") + " " + if attr: + cmd += attr.replace("_", "-") + if val and opr: + cmd += " '" + str(val) + "'" + return cmd + + def _map_attrib(self, attrib): + """ + - This function construct the regex string. + - replace the underscore with hyphen. + :param attrib: attribute + :return: regex string + """ + return 'disable' if attrib =='disabled' else attrib.replace("_","-") diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index 3c87be6b..17c7aa38 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -42,6 +42,8 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firew ) 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.legacy.base import ( Default, @@ -62,6 +64,7 @@ FACT_RESOURCE_SUBSETS = dict( firewall_global=Firewall_globalFacts, firewall_interfaces=Firewall_interfacesFacts, ospfv3=Ospfv3Facts, + ospfv2=Ospfv2Facts, ) diff --git a/plugins/module_utils/network/vyos/facts/ospfv2/__init__.py b/plugins/module_utils/network/vyos/facts/ospfv2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py new file mode 100644 index 00000000..3457fac8 --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py @@ -0,0 +1,395 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos ospfv2 fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from re import findall, search, M +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import Ospfv2Args + + +class Ospfv2Facts(object): + """ The vyos ospfv2 fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = Ospfv2Args.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_device_data(self, connection): + return connection.get_config() + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for ospfv2 + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + data = self.get_device_data(connection) + # typically data is populated from the current device configuration + # data = connection.get('show running-config | section ^interface') + # using mock data instead + objs = [] + ospfv2 = findall(r"^set protocols ospf (.+)", data, M) + if ospfv2: + config = self.render_config(ospfv2) + if config: + objs.append(config) + ansible_facts["ansible_network_resources"].pop("ospfv2", None) + facts = {} + if objs: + facts["ospfv2"] = [] + params = utils.validate_config(self.argument_spec, {"config": objs}) + for cfg in params["config"]: + facts["ospfv2"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def render_config(self, conf): + """ + Render config as dictionary structure + + :param conf: The configuration + :returns: The generated config + """ + conf = "\n".join(filter(lambda x: x, conf)) + a_lst = ["default_metric", "log_adjacency_changes"] + config = self.parse_attr(conf, a_lst) + + if not config: + config = {} + config["timers"] = self.parse_timers(conf) + config["auto_cost"] = self.parse_auto_cost(conf) + config["distance"] = self.parse_distance(conf) + config["max_metric"] = self.parse_max_metric(conf) + config["mpls_te"] = self.parse_attrib(conf, "mpls_te", "mpls-te") + config["default_information"] = self.parse_def_info(conf) + config["parameters"] = self.parse_attrib(conf, "parameters", "parameters") + config["route_map"] = self.parse_leaf_list(conf, "route-map") + config["ospf_area"] = self.parse_attrib_list(conf, "area", "area") + config["neighbor"] = self.parse_attrib_list(conf, "neighbor", "neighbor_id") + config["passive_interface"] = self.parse_leaf_list(conf, "passive-interface") + config["redistribute"] = self.parse_attrib_list(conf, "redistribute", "route_type") + config["passive_interface_exclude"] = self.parse_leaf_list(conf, "passive-interface-exclude") + return config + + def parse_timers(self, conf, attrib=None): + """ + This function triggers the parsing of 'timers' attributes + :param conf: configuration + :param attrib: attribute name + :return: generated config dictionary + """ + cfg_dict = {} + cfg_dict["refresh"] = self.parse_refresh(conf, "refresh") + cfg_dict["throttle"] = self.parse_throttle(conf, "spf") + return cfg_dict + + def parse_throttle(self, conf, attrib=None): + """ + This function triggers the parsing of 'throttle' attributes + :param conf: configuration + :param attrib: 'spf' + :return: generated config dictionary + """ + cfg_dict = {} + cfg_dict[attrib] = self.parse_attrib(conf, attrib, match=attrib) + return cfg_dict + + def parse_refresh(self, conf, attrib=None): + """ + This function triggers the parsing of 'refresh' attributes + :param conf: configuration + :param attrib: 'refresh' + :return: generated config dictionary + """ + cfg_dict = self.parse_attr(conf, ["timers"], match=attrib) + return cfg_dict + + def parse_attrib_list(self, conf, attrib, param): + """ + This function forms the regex to fetch the listed attributes + from config + :param conf: configuration data + :param attrib: attribute name + :param param: parameter data + :return: generated rule list configuration + """ + r_lst = [] + if attrib == "area": + items = findall(r"^" + attrib + " (?:\'*)(\S+)(?:\'*)", conf, M) + else: + items = findall(r"" + attrib + " (?:\'*)(\S+)(?:\'*)", conf, M) + if items: + a_lst = [] + for item in set(items): + i_regex = r" %s .+$" % item + cfg = "\n".join(findall(i_regex, conf, M)) + if attrib == 'area': + obj = self.parse_area(cfg, item) + elif attrib == 'virtual-link': + obj = self.parse_vlink(cfg) + else: + obj = self.parse_attrib(cfg, attrib) + obj[param] = item.strip("'") + if obj: + a_lst.append(obj) + r_lst = sorted(a_lst, key=lambda i: i[param]) + return r_lst + + def parse_leaf_list(self, conf, attrib): + """ + This function forms the regex to fetch the listed attributes + from the configuration data + :param conf: configuration data + :param attrib: attribute name + :return: generated rule list configuration + """ + lst = [] + items = findall(r"^" + attrib + " (?:\'*)(\S+)(?:\'*)", conf, M) + if items: + for i in set(items): + lst.append(i.strip("'")) + return lst + + def parse_distance(self, conf, attrib=None): + """ + This function triggers the parsing of 'distance' attributes + :param conf: configuration + :param attrib: attribute name + :return: generated config dictionary + """ + cfg_dict = self.parse_attr(conf, ["global"], match=attrib) + cfg_dict["ospf"] = self.parse_ospf(conf, "ospf") + return cfg_dict + + def parse_ospf(self, conf, attrib=None): + """ + This function triggers the parsing of 'distance ospf' attributes + :param conf: configuration + :param attrib: 'ospf' + :return: generated config dictionary + """ + cfg_dict = self.parse_attrib(conf, 'ospf', match=attrib) + return cfg_dict + + def parse_max_metric(self, conf, attrib=None): + """ + This function triggers the parsing of 'max_metric' attributes + :param conf: configuration + :param attrib: attribute name + :return: generated config dictionary + """ + cfg_dict = {} + cfg_dict["router_lsa"] = self.parse_attrib(conf, "router_lsa", match="router-lsa") + return cfg_dict + + def parse_auto_cost(self, conf, attrib=None): + """ + This function triggers the parsing of 'auto_cost' attributes + :param conf: configuration + :param attrib: attribute name + :return: generated config dictionary + """ + cfg_dict = self.parse_attr(conf, ["reference_bandwidth"], match=attrib) + return cfg_dict + + def parse_def_info(self, conf, attrib=None): + """ + This function triggers the parsing of 'default_information' attributes + :param conf: configuration + :param attrib: attribute name + :return: generated config dictionary + """ + cfg_dict = {} + cfg_dict["originate"] = self.parse_attrib(conf, "originate", "originate") + return cfg_dict + + def parse_area(self, conf, area_id): + """ + This function triggers the parsing of 'area' attributes. + :param conf: configuration data + :param area_id: area identity + :return: generated rule configuration dictionary. + """ + rule = self.parse_attrib(conf, "area", match=area_id) + r_sub = { + "area_type": self.parse_area_type(conf, "area-type"), + "network": self.parse_network(conf), + "range": self.parse_attrib_list(conf, "range", "address"), + "virtual_link": self.parse_attrib_list(conf, "virtual-link", "address") + } + rule.update(r_sub) + return rule + + def parse_area_type(self, conf, attrib=None): + """ + This function triggers the parsing of 'area_type' attributes + :param conf: configuration + :param attrib: 'area-type' + :return: generated config dictionary + """ + cfg_dict = self.parse_attr(conf, ["normal"], match=attrib) + cfg_dict["nssa"] = self.parse_attrib(conf, "nssa") + cfg_dict["stub"] = self.parse_attrib(conf, "stub") + return cfg_dict + + def parse_network(self, conf): + """ + This function forms the regex to fetch the 'network' + :param conf: configuration data + :return: generated rule list configuration + """ + a_lst = [] + applications = findall(r"network (.+)", conf, M) + if applications: + app_lst = [] + for r in set(applications): + obj = {"address": r.strip("'")} + app_lst.append(obj) + a_lst = sorted(app_lst, key=lambda i: i["address"]) + return a_lst + + def parse_vlink(self, conf): + """ + This function triggers the parsing of 'vitual_link' attributes + :param conf: configuration data + :return: generated rule configuration dictionary + """ + rule = self.parse_attrib(conf, 'vlink') + r_sub = {"authentication": self.parse_authentication(conf, "authentication")} + rule.update(r_sub) + return rule + + def parse_authentication(self, conf, attrib=None): + """ + This function triggers the parsing of 'authentication' attributes. + :param conf: configuration + :param attrib: 'authentication' + :return: generated config dictionary + """ + cfg_dict = self.parse_attr(conf, ["plaintext_password"], match=attrib) + cfg_dict["md5"] = self.parse_md5(conf, "md5") + return cfg_dict + + def parse_md5(self, conf, attrib=None): + """ + This function triggers the parsing of 'md5' attributes + :param conf: configuration + :param attrib: 'md5' + :return: generated config dictionary + """ + cfg_dict = self.parse_attr(conf, ["key_id"], match=attrib) + return cfg_dict + + def parse_attrib(self, conf, param, match=None): + """ + This function triggers the parsing of 'ospf' attributes + :param conf: configuration data + :return: generated configuration dictionary + """ + param_lst = { + 'stub': ["default_cost", "no_summary"], + 'area': ["shortcut", "authentication"], + 'mpls_te': ["enabled", "router_address"], + 'neighbor': ["priority", "poll_interval"], + 'ospf': ["external", "inter_area", "intra_area"], + 'nssa': ["translate", "default_cost", "no_summary"], + 'redistribute': ["metric", "metric_type", "route_map"], + 'spf': ["delay", "max_holdtime", "initial_holdtime"], + 'range': ["cost", "substitute", "not_advertise"], + 'originate': ["always", "metric", "metric_type", "route_map"], + 'router_lsa': ["administrative", "on_shutdown", "on_startup"], + 'config_routes': ["default_metric", "log_adjacency_changes"], + 'parameters': ["abr_type", "opaque_lsa", "router_id", "rfc1583_compatibility"], + 'vlink': ["dead_interval", "hello_interval", "transmit_delay", "retransmit_interval"] + } + cfg_dict = self.parse_attr(conf, param_lst[param], match) + return cfg_dict + + def parse_attr(self, conf, attr_list, match=None): + """ + This function peforms the following: + - Form the regex to fetch the required attribute config. + - Type cast the output in desired format. + :param conf: configuration. + :param attr_list: list of attributes. + :param match: parent node/attribute name. + :return: generated config dictionary. + """ + config = {} + for attrib in attr_list: + regex = self.map_regex(attrib) + if match: + regex = match.replace("_", "-") + " " + regex + if conf: + if self.is_bool(attrib): + out = conf.find(attrib.replace("_", "-")) + dis = conf.find(attrib.replace("_", "-") + " 'disable'") + if match: + en = conf.find(match + " 'enable'") + if out >= 1: + if dis >= 1: + config[attrib] = False + else: + config[attrib] = True + elif match and en >= 1: + config[attrib] = True + else: + out = search(r"^.*" + regex + " (.+)", conf, M) + if out: + val = out.group(1).strip("'") + if self.is_num(attrib): + val = int(val) + config[attrib] = val + return config + + def map_regex(self, attrib): + """ + - This function construct the regex string. + - replace the underscore with hyphen. + :param attrib: attribute + :return: regex string + """ + return 'disable' if attrib == "disabled" else 'enable' if attrib == "enabled" else attrib.replace("_","-") + + def is_bool(self, attrib): + """ + This function looks for the attribute in predefined bool type set. + :param attrib: attribute. + :return: True/False + """ + bool_set = ("always", "normal", "enabled", "opaque_lsa", "not_advertise", "administrative", "rfc1583_compatibility") + return True if attrib in bool_set else False + + def is_num(self, attrib): + """ + This function looks for the attribute in predefined integer type set. + :param attrib: attribute. + :return: True/false. + """ + num_set = ("ospf", "delay", "metric", "inter_area", "intra_area", "on_startup", "metric_type", "on_shutdown", + "max_holdtime", "default_metric", "initial_holdtime") + return True if attrib in num_set else False diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py index 96feddd9..c7dc575a 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -7,6 +7,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type +import q from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.compat import ( ipaddress, @@ -238,13 +239,7 @@ def _bool_to_str(val): :param val: bool value. :return: enable/disable. """ - return ( - "enable" - if str(val) == "True" - else "disable" - if str(val) == "False" - else val - ) + return "enable" if str(val) == "True" else "disable" if str(val) == "False" else val def _is_w_same(w, h, key): diff --git a/plugins/modules/vyos_facts.py b/plugins/modules/vyos_facts.py index 5849519c..72b191c4 100644 --- a/plugins/modules/vyos_facts.py +++ b/plugins/modules/vyos_facts.py @@ -48,7 +48,7 @@ options: used with an initial C(M(!)) to specify that a specific subset should not be collected. Valid subsets are 'all', 'interfaces', 'l3_interfaces', 'lag_interfaces', 'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules', 'firewall_global', - 'firewall_interfaces', 'ospfv3'. + 'firewall_interfaces', 'ospfv3', 'ospfv2'. required: false """ diff --git a/plugins/modules/vyos_ospfv2.py b/plugins/modules/vyos_ospfv2.py new file mode 100644 index 00000000..73528eb5 --- /dev/null +++ b/plugins/modules/vyos_ospfv2.py @@ -0,0 +1,1369 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 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_ospfv2 +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'network' +} + +DOCUMENTATION = """ +--- +module: vyos_ospfv2 +version_added: 2.10 +short_description: Manages attributes of OSPF IPv4 routes on VyOS network devices. +description: This module manages attributes of OSPF IPv4 routes on VyOS network devices. +author: Rohit Thakur (@rohitthakur2590) +options: + config: + description: A provided OSPF route configuration. + type: list + elements: dict + suboptions: + ospf_area: + description: OSPF area. + type: list + elements: dict + suboptions: + area: + description: Configured to discard packets. + type: str + area_type: + description: Area type. + type: dict + suboptions: + normal: + description: Normal OSPF area. + type: bool + nssa: + description: Nssa OSPF area. + type: dict + suboptions: + default_cost: + description: Summary-default cost of nssa area. + type: int + no_summary: + description: Do not inject inter-area routes into stub. + type: bool + translate: + description: Nssa-abr. + type: str + choices: ['always', 'candidate', 'never'] + stub: + description: Stub OSPF area. + type: dict + suboptions: + default_cost: + description: Summary-default cost of stub area. + type: int + no_summary: + description: Do not inject inter-area routes into stub. + type: bool + authentication: + description: OSPF area authentication type. + type: str + choices: ['plaintext-password', 'md5'] + network: + description: OSPF network. + type: list + elements: dict + suboptions: + address: + required: True + description: OSPF IPv4 network address. + type: str + range: + description: Summarize routes matching prefix (border routers only). + type: list + elements: dict + suboptions: + address: + description: border router IPv4 address. + type: str + cost: + description: Metric for this range. + type: int + not_advertise: + description: Don't advertise this range. + type: bool + substitute: + description: Announce area range (IPv4 address) as another prefix. + type: str + shortcut: + description: Area's shortcut mode. + type: str + choices: ['default', 'disable', 'enable'] + virtual_link: + description: Virtual link address. + type: list + elements: dict + suboptions: + address: + description: virtual link address. + type: str + authentication: + description: OSPF area authentication type. + type: dict + suboptions: + md5: + description: MD5 key id based authentication. + type: dict + suboptions: + key_id: + description: MD5 key id. + type: int + md5_key: + description: MD5 key. + type: str + plaintext_password: + description: Plain text password. + type: str + dead_interval: + description: Interval after which a neighbor is declared dead. + type: int + hello_interval: + description: Interval between hello packets. + type: int + retransmit_interval: + description: Interval between retransmitting lost link state advertisements. + type: int + transmit_delay: + description: Link state transmit delay. + type: int + log_adjacency_changes: + description: Log changes in adjacency state. + type: str + choices: ['detail'] + max_metric: + description: OSPF maximum/infinite-distance metric. + type: dict + suboptions: + router_lsa: + description: Advertise own Router-LSA with infinite distance (stub router). + type: dict + suboptions: + administrative: + description: Administratively apply, for an indefinite period. + type: bool + on_shutdown: + description: Time to advertise self as stub-router. + type: int + on_startup: + description: Time to advertise self as stub-router + type: int + auto_cost: + description: Calculate OSPF interface cost according to bandwidth. + type: dict + suboptions: + reference_bandwidth: + description: Reference bandwidth cost in Mbits/sec. + type: int + default_information: + description: Control distribution of default information. + type: dict + suboptions: + originate: + description: Distribute a default route. + type: dict + suboptions: + always: + description: Always advertise default route. + type: bool + metric: + description: OSPF default metric. + type: int + metric_type: + description: OSPF Metric types for default routes. + type: int + route_map: + description: Route map references. + type: str + default_metric: + description: Metric of redistributed routes + type: int + distance: + description: Administrative distance. + type: dict + suboptions: + global: + description: Global OSPF administrative distance. + type: int + ospf: + description: OSPF administrative distance. + type: dict + suboptions: + external: + description: Distance for external routes. + type: int + inter_area: + description: Distance for inter-area routes. + type: int + intra_area: + description: Distance for intra-area routes. + type: int + mpls_te: + description: MultiProtocol Label Switching-Traffic Engineering (MPLS-TE) parameters. + type: dict + suboptions: + enabled: + description: Enable MPLS-TE functionality. + type: bool + router_address: + description: Stable IP address of the advertising router. + type: str + + neighbor: + description: Neighbor IP address. + type: list + elements: dict + suboptions: + neighbor_id: + description: Identity (number/IP address) of neighbor. + type: str + poll_interval: + description: Seconds between dead neighbor polling interval. + type: int + priority: + description: Neighbor priority. + type: int + parameters: + descriptions: OSPF specific parameters. + type: dict + suboptions: + abr_type: + description: OSPF ABR Type. + type: str + choices: ['cisco', 'ibm', 'shortcut', 'standard'] + opaque_lsa: + description: Enable the Opaque-LSA capability (rfc2370). + type: bool + rfc1583_compatibility: + description: Enable rfc1583 criteria for handling AS external routes. + type: bool + router_id: + description: Override the default router identifier. + type: str + passive_interface: + description: Suppress routing updates on an interface. + type: list + passive_interface_exclude: + description: Interface to exclude when using passive-interface default. + type: list + redistribute: + description: Redistribute information from another routing protocol. + type: list + elements: dict + suboptions: + route_type: + description: Route type to redistribute. + type: str + choices: ['bgp', 'connected', 'kernel', 'rip', 'static'] + metric: + description: Metric for redistribution routes. + type: int + metric_type: + description: OSPF Metric types. + type: int + route_map: + description: Route map references. + type: str + + route_map: + description: Filter routes installed in local route map. + type: list + timers: + description: Adjust routing timers. + type: dict + suboptions: + refresh: + description: Adjust refresh parameters. + type: dict + suboptions: + timers: + description: refresh timer. + type: int + throttle: + description: Throttling adaptive timers. + type: dict + suboptions: + spf: + description: OSPF SPF timers. + type: dict + suboptions: + delay: + description: Delay (msec) from first change received till SPF calculation. + type: int + initial_holdtime: + description: Initial hold time(msec) between consecutive SPF calculations. + type: int + max_holdtime: + description: maximum hold time (sec). + type: int + state: + description: + - The state the configuration should be left in. + type: str + choices: + - merged + - replaced + - deleted + - parsed + - gathered + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# vyos@192# run show configuration commands | grep ospf +# +- name: Merge the provided configuration with the existing running configuration + vyos_ospf_routes: + config: + - afi: 'ipv4' + ospf_area: + - area: 0 + network: 192.168.0.0/24 + default_information: + originate: + always: true + metric: 2 + metric_type: 10 + log_adjacency_changes: "details" + parameters: + router_id: 10.1.1.1 + redistribute: + - route_type: 'connected' + metric_type: 2 + route_map: 'CONNECT' + - afi: 'ipv6' + ospf_area: + - area: 0.0.0.0 + range: 2001:db8:1::/64 + parameters: + router-id 192.168.1.1 + redistribute: + - route_type: 'connected' + state: merged +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# before": [] +# +# "commands": [ +# "set interfaces ethernet eth1 firewall in name 'INBOUND'", +# "set protocols ospf area 0 network 192.168.0.0/24", +# "set protocols ospf default-information originate always", +# "set protocols ospf default-information originate metric 10", +# "set protocols ospf default-information originate metric-type 2", +# "set protocols ospf log-adjacency-changes", +# "set protocols ospf parameters router-id 10.1.1.1", +# "set protocols ospf redistribute connected metric-type 2", +# "set protocols ospf redistribute connected route-map CONNECT", +# "set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64, +# "set protocols ospfv3 parameters router-id 192.168.1.1, +# "set protocols ospfv3 redistribute connected +# ] +# +# "after": [ +# { +# { +# "afi": "ipv4", +# "ospf_area":[ +# { +# "area": "0", +# "network": "192.168.0.0/24" +# } +# ], +# "default_information": +# { +# "originate": +# { +# always: true, +# metric: 2, +# metric_type: 10 +# } +# }, +# "log_adjacency_changes": "details" +# "parameters": +# { +# "router_id": "10.1.1.1" +# }, +# "redistribute":[ +# { +# "route_type": "connetced", +# "metric_type": 2 +# "route_map": "CONNECT" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "ospf_area":[ +# { +# "area": "0.0.0.0", +# } +# ], +# "range": "2001:db8:1::/64", +# "parameters": +# { +# "router_id": "192.168.1.1" +# }, +# "redistribute": +# [ +# { +# "route_type": "connetced", +# } +# ] +# } +# ] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set protocols ospf area 0 network 192.168.0.0/24 +# set protocols ospf default-information originate always +# set protocols ospf default-information originate metric 10 +# set protocols ospf default-information originate metric-type 2 +# set protocols ospf log-adjacency-changes details +# set protocols ospf parameters router-id 10.1.1.1 +# set protocols ospf redistribute connected metric-type 2 +# set protocols ospf redistribute connected route-map CONNECT +# set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64 +# set protocols ospfv3 parameters router-id 192.168.1.1 +# set protocols ospfv3 redistribute connected + + +# Using replaced +# +# Before state: +# ------------- +# +# vyos@192# run show configuration commands | grep ospf +# set protocols ospf area 0 network 192.168.0.0/24 +# set protocols ospf default-information originate always +# set protocols ospf default-information originate metric 10 +# set protocols ospf default-information originate metric-type 2 +# set protocols ospf log-adjacency-changes details +# set protocols ospf parameters router-id 10.1.1.1 +# set protocols ospf redistribute connected metric-type 2 +# set protocols ospf redistribute connected route-map CONNECT +# set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64 +# set protocols ospfv3 parameters router-id 192.168.1.1 +# set protocols ospfv3 redistribute connected +# +- name: Replace the provided configuration with the existing running configuration + vyos_ospf_routes: + config: + - afi: 'ipv4' + ospf_area: + - area: 0 + network: 192.168.0.0/24 + area_type: + normal: True + default_information: + originate: + always: true + metric: 2 + metric_type: 10 + log_adjacency_changes: "details" + parameters: + router_id: 10.1.1.1 + redistribute: + - route_type: 'static' + metric_type: 2 + route_map: 'STATIC' + - afi: 'ipv6' + ospf_area: + - area: 0.0.0.0 + range: 2001:db8:1::/64 + parameters: + router-id 192.168.1.1 + redistribute: + - route_type: 'connected' + state: replaced +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# before": [ +# { +# { +# "afi": "ipv4", +# "ospf_area":[ +# { +# "area": "0", +# "network": "192.168.0.0/24" +# } +# ], +# "default_information": +# { +# "originate": +# { +# always: true, +# metric: 2, +# metric_type: 10 +# } +# }, +# "log_adjacency_changes": "details" +# "parameters": +# { +# "router_id": "10.1.1.1" +# }, +# "redistribute":[ +# { +# "route_type": "connetced", +# "metric_type": 2 +# "route_map": "CONNECT" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "ospf_area":[ +# { +# "area": "0.0.0.0", +# } +# ], +# "range": "2001:db8:1::/64", +# "parameters": +# { +# "router_id": "192.168.1.1" +# }, +# "redistribute": +# [ +# { +# "route_type": "connetced", +# } +# ] +# } +# ] +# +# "commands": [ +# "delete protocols ospf redistribute connected", +# "set protocols ospf area 0 area_type normal", +# "set protocols ospf redistribute static metric-type 2", +# "set protocols ospf redistribute static route-map CONNECT" +# ] +# +# "after": [ +# { +# { +# "afi": "ipv4", +# "ospf_area":[ +# { +# "area": "0", +# "area_type": +# { +# normal: true +# } +# "network": "192.168.0.0/24" +# } +# ], +# "default_information": +# { +# "originate": +# { +# always: true, +# metric: 2, +# metric_type: 10 +# } +# }, +# "log_adjacency_changes": "details" +# "parameters": +# { +# "router_id": "10.1.1.1" +# }, +# "redistribute":[ +# { +# "route_type": "static", +# "metric_type": 2 +# "route_map": "STATIC" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "ospf_area":[ +# { +# "area": "0.0.0.0", +# } +# ], +# "range": "2001:db8:1::/64", +# "parameters": +# { +# "router_id": "192.168.1.1" +# }, +# "redistribute": +# [ +# { +# "route_type": "connected", +# } +# ] +# } +# ] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set protocols ospf area 0 network 192.168.0.0/24 +# set protocols ospf default-information originate always +# set protocols ospf default-information originate metric 10 +# set protocols ospf default-information originate metric-type 2 +# set protocols ospf log-adjacency-changes details +# set protocols ospf parameters router-id 10.1.1.1 +# set protocols ospf redistribute static metric-type 2 +# set protocols ospf redistribute static route-map CONNECT +# set protocols ospfv3 area 0.0.0.0 range 2001:db8:2::/64 +# set protocols ospfv3 parameters router-id 192.168.2.1 +# set protocols ospfv3 redistribute connected + + +# Using replaced +# +# Before state: +# ------------- +# +# vyos@192# run show configuration commands | grep ospf +# set protocols ospf area 0 network 192.168.0.0/24 +# set protocols ospf default-information originate always +# set protocols ospf default-information originate metric 10 +# set protocols ospf default-information originate metric-type 2 +# set protocols ospf log-adjacency-changes details +# set protocols ospf parameters router-id 10.1.1.1 +# set protocols ospf redistribute connected metric-type 2 +# set protocols ospf redistribute connected route-map CONNECT +# set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64 +# set protocols ospfv3 parameters router-id 192.168.1.1 +# set protocols ospfv3 redistribute connected +# +- name: Replace the provided configuration with the existing running configuration + vyos_ospf_routes: + config: + - afi: 'ipv4' + ospf_area: + - area: 0 + network: 192.168.0.0/24 + area_type: + normal: True + default_information: + originate: + always: true + metric: 2 + metric_type: 10 + log_adjacency_changes: "details" + parameters: + router_id: 10.1.1.1 + redistribute: + - route_type: 'static' + metric_type: 2 + route_map: 'STATIC' + - afi: 'ipv6' + ospf_area: + - area: 0.0.0.0 + range: 2001:db8:1::/64 + parameters: + router-id 192.168.1.1 + redistribute: + - route_type: 'connected' + state: replaced +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# before": [ +# { +# { +# "afi": "ipv4", +# "ospf_area":[ +# { +# "area": "0", +# "network": "192.168.0.0/24" +# } +# ], +# "default_information": +# { +# "originate": +# { +# always: true, +# metric: 2, +# metric_type: 10 +# } +# }, +# "log_adjacency_changes": "details" +# "parameters": +# { +# "router_id": "10.1.1.1" +# }, +# "redistribute":[ +# { +# "route_type": "connetced", +# "metric_type": 2 +# "route_map": "CONNECT" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "ospf_area":[ +# { +# "area": "0.0.0.0", +# } +# ], +# "range": "2001:db8:1::/64", +# "parameters": +# { +# "router_id": "192.168.1.1" +# }, +# "redistribute": +# [ +# { +# "route_type": "connetced", +# } +# ] +# } +# ] +# +# "commands": [ +# "delete protocols ospf redistribute connected", +# "set protocols ospf area 0 area_type normal", +# "set protocols ospf redistribute static metric-type 2", +# "set protocols ospf redistribute static route-map CONNECT" +# ] +# +# "after": [ +# { +# { +# "afi": "ipv4", +# "ospf_area":[ +# { +# "area": "0", +# "area_type": +# { +# normal: true +# } +# "network": "192.168.0.0/24" +# } +# ], +# "default_information": +# { +# "originate": +# { +# always: true, +# metric: 2, +# metric_type: 10 +# } +# }, +# "log_adjacency_changes": "details" +# "parameters": +# { +# "router_id": "10.1.1.1" +# }, +# "redistribute":[ +# { +# "route_type": "static", +# "metric_type": 2 +# "route_map": "STATIC" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "ospf_area":[ +# { +# "area": "0.0.0.0", +# } +# ], +# "range": "2001:db8:1::/64", +# "parameters": +# { +# "router_id": "192.168.1.1" +# }, +# "redistribute": +# [ +# { +# "route_type": "connected", +# } +# ] +# } +# ] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set protocols ospf area 0 network 192.168.0.0/24 +# set protocols ospf default-information originate always +# set protocols ospf default-information originate metric 10 +# set protocols ospf default-information originate metric-type 2 +# set protocols ospf log-adjacency-changes details +# set protocols ospf parameters router-id 10.1.1.1 +# set protocols ospf redistribute static metric-type 2 +# set protocols ospf redistribute static route-map CONNECT +# set protocols ospfv3 area 0.0.0.0 range 2001:db8:2::/64 +# set protocols ospfv3 parameters router-id 192.168.2.1 +# set protocols ospfv3 redistribute connected + + +# Using replaced +# +# Before state: +# ------------- +# +# vyos@192# run show configuration commands | grep ospf +# set protocols ospf area 0 network 192.168.0.0/24 +# set protocols ospf default-information originate always +# set protocols ospf default-information originate metric 10 +# set protocols ospf default-information originate metric-type 2 +# set protocols ospf log-adjacency-changes details +# set protocols ospf parameters router-id 10.1.1.1 +# set protocols ospf redistribute connected metric-type 2 +# set protocols ospf redistribute connected route-map CONNECT +# set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64 +# set protocols ospfv3 parameters router-id 192.168.1.1 +# set protocols ospfv3 redistribute connected +# +- name: Replace the provided configuration with the existing running configuration + vyos_ospf_routes: + config: + - afi: 'ipv4' + ospf_area: + - area: 0 + network: 192.168.0.0/24 + area_type: + normal: True + default_information: + originate: + always: true + metric: 2 + metric_type: 10 + log_adjacency_changes: "details" + parameters: + router_id: 10.1.1.1 + redistribute: + - route_type: 'static' + metric_type: 2 + route_map: 'STATIC' + - afi: 'ipv6' + ospf_area: + - area: 0.0.0.0 + range: 2001:db8:1::/64 + parameters: + router-id 192.168.1.1 + redistribute: + - route_type: 'connected' + state: replaced +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# before": [ +# { +# { +# "afi": "ipv4", +# "ospf_area":[ +# { +# "area": "0", +# "network": "192.168.0.0/24" +# } +# ], +# "default_information": +# { +# "originate": +# { +# always: true, +# metric: 2, +# metric_type: 10 +# } +# }, +# "log_adjacency_changes": "details" +# "parameters": +# { +# "router_id": "10.1.1.1" +# }, +# "redistribute":[ +# { +# "route_type": "connetced", +# "metric_type": 2 +# "route_map": "CONNECT" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "ospf_area":[ +# { +# "area": "0.0.0.0", +# } +# ], +# "range": "2001:db8:1::/64", +# "parameters": +# { +# "router_id": "192.168.1.1" +# }, +# "redistribute": +# [ +# { +# "route_type": "connetced", +# } +# ] +# } +# ] +# +# "commands": [ +# "delete protocols ospf redistribute connected", +# "set protocols ospf area 0 area_type normal", +# "set protocols ospf redistribute static metric-type 2", +# "set protocols ospf redistribute static route-map CONNECT" +# ] +# +# "after": [ +# { +# { +# "afi": "ipv4", +# "ospf_area":[ +# { +# "area": "0", +# "area_type": +# { +# normal: true +# } +# "network": "192.168.0.0/24" +# } +# ], +# "default_information": +# { +# "originate": +# { +# always: true, +# metric: 2, +# metric_type: 10 +# } +# }, +# "log_adjacency_changes": "details" +# "parameters": +# { +# "router_id": "10.1.1.1" +# }, +# "redistribute":[ +# { +# "route_type": "static", +# "metric_type": 2 +# "route_map": "STATIC" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "ospf_area":[ +# { +# "area": "0.0.0.0", +# } +# ], +# "range": "2001:db8:1::/64", +# "parameters": +# { +# "router_id": "192.168.1.1" +# }, +# "redistribute": +# [ +# { +# "route_type": "connected", +# } +# ] +# } +# ] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set protocols ospf area 0 network 192.168.0.0/24 +# set protocols ospf default-information originate always +# set protocols ospf default-information originate metric 10 +# set protocols ospf default-information originate metric-type 2 +# set protocols ospf log-adjacency-changes details +# set protocols ospf parameters router-id 10.1.1.1 +# set protocols ospf redistribute static metric-type 2 +# set protocols ospf redistribute static route-map CONNECT +# set protocols ospfv3 area 0.0.0.0 range 2001:db8:2::/64 +# set protocols ospfv3 parameters router-id 192.168.2.1 +# set protocols ospfv3 redistribute connected + + +# Using replaced +# +# Before state: +# ------------- +# +# vyos@192# run show configuration commands | grep ospf +# set protocols ospf area 0 network 192.168.0.0/24 +# set protocols ospf default-information originate always +# set protocols ospf default-information originate metric 10 +# set protocols ospf default-information originate metric-type 2 +# set protocols ospf log-adjacency-changes details +# set protocols ospf parameters router-id 10.1.1.1 +# set protocols ospf redistribute connected metric-type 2 +# set protocols ospf redistribute connected route-map CONNECT +# set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64 +# set protocols ospfv3 parameters router-id 192.168.1.1 +# set protocols ospfv3 redistribute connected +# +- name: Replace the provided configuration with the existing running configuration + vyos_ospf_routes: + config: + - afi: 'ipv4' + ospf_area: + - area: 0 + network: 192.168.0.0/24 + area_type: + normal: True + default_information: + originate: + always: true + metric: 2 + metric_type: 10 + log_adjacency_changes: "details" + parameters: + router_id: 10.1.1.1 + redistribute: + - route_type: 'static' + metric_type: 2 + route_map: 'STATIC' + - afi: 'ipv6' + ospf_area: + - area: 0.0.0.0 + range: 2001:db8:1::/64 + parameters: + router-id 192.168.1.1 + redistribute: + - route_type: 'connected' + state: replaced +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# before": [ +# { +# { +# "afi": "ipv4", +# "ospf_area":[ +# { +# "area": "0", +# "network": "192.168.0.0/24" +# } +# ], +# "default_information": +# { +# "originate": +# { +# always: true, +# metric: 2, +# metric_type: 10 +# } +# }, +# "log_adjacency_changes": "details" +# "parameters": +# { +# "router_id": "10.1.1.1" +# }, +# "redistribute":[ +# { +# "route_type": "connetced", +# "metric_type": 2 +# "route_map": "CONNECT" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "ospf_area":[ +# { +# "area": "0.0.0.0", +# } +# ], +# "range": "2001:db8:1::/64", +# "parameters": +# { +# "router_id": "192.168.1.1" +# }, +# "redistribute": +# [ +# { +# "route_type": "connetced", +# } +# ] +# } +# ] +# +# "commands": [ +# "delete protocols ospf redistribute connected", +# "set protocols ospf area 0 area_type normal", +# "set protocols ospf redistribute static metric-type 2", +# "set protocols ospf redistribute static route-map CONNECT" +# ] +# +# "after": [ +# { +# { +# "afi": "ipv4", +# "ospf_area":[ +# { +# "area": "0", +# "area_type": +# { +# normal: true +# } +# "network": "192.168.0.0/24" +# } +# ], +# "default_information": +# { +# "originate": +# { +# always: true, +# metric: 2, +# metric_type: 10 +# } +# }, +# "log_adjacency_changes": "details" +# "parameters": +# { +# "router_id": "10.1.1.1" +# }, +# "redistribute":[ +# { +# "route_type": "static", +# "metric_type": 2 +# "route_map": "STATIC" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "ospf_area":[ +# { +# "area": "0.0.0.0", +# } +# ], +# "range": "2001:db8:1::/64", +# "parameters": +# { +# "router_id": "192.168.1.1" +# }, +# "redistribute": +# [ +# { +# "route_type": "connected", +# } +# ] +# } +# ] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall +# set protocols ospf area 0 network 192.168.0.0/24 +# set protocols ospf default-information originate always +# set protocols ospf default-information originate metric 10 +# set protocols ospf default-information originate metric-type 2 +# set protocols ospf log-adjacency-changes details +# set protocols ospf parameters router-id 10.1.1.1 +# set protocols ospf redistribute static metric-type 2 +# set protocols ospf redistribute static route-map CONNECT +# set protocols ospfv3 area 0.0.0.0 range 2001:db8:2::/64 +# set protocols ospfv3 parameters router-id 192.168.2.1 +# set protocols ospfv3 redistribute connected + + +# Using deleted +# +# Before state: +# ------------- +# +# vyos@192# run show configuration commands | grep ospf +# set protocols ospf area 0 network 192.168.0.0/24 +# set protocols ospf default-information originate always +# set protocols ospf default-information originate metric 10 +# set protocols ospf default-information originate metric-type 2 +# set protocols ospf log-adjacency-changes details +# set protocols ospf parameters router-id 10.1.1.1 +# set protocols ospf redistribute connected metric-type 2 +# set protocols ospf redistribute connected route-map CONNECT +# set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64 +# set protocols ospfv3 parameters router-id 192.168.1.1 +# set protocols ospfv3 redistribute connected +# +- name: Delete all the configuration + vyos_ospf_routes: + config: + state: deleted +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# before": [ +# { +# { +# "afi": "ipv4", +# "ospf_area":[ +# { +# "area": "0", +# "network": "192.168.0.0/24" +# } +# ], +# "default_information": +# { +# "originate": +# { +# always: true, +# metric: 2, +# metric_type: 10 +# } +# }, +# "log_adjacency_changes": "details" +# "parameters": +# { +# "router_id": "10.1.1.1" +# }, +# "redistribute":[ +# { +# "route_type": "connetced", +# "metric_type": 2 +# "route_map": "CONNECT" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "ospf_area":[ +# { +# "area": "0.0.0.0", +# } +# ], +# "range": "2001:db8:1::/64", +# "parameters": +# { +# "router_id": "192.168.1.1" +# }, +# "redistribute": +# [ +# { +# "route_type": "connetced", +# } +# ] +# } +# ] +# +# "commands": [ +# "delete protocols ospf", +# "delete protocols ospfv3", +# ] +# +# "after": [] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands| grep firewall + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['command 1', 'command 2', 'command 3'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import Ospfv2Args +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv2.ospfv2 import Ospfv2 + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + module = AnsibleModule( + argument_spec=Ospfv2Args.argument_spec, + required_if=required_if, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive, + ) + + result = Ospfv2(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() -- cgit v1.2.3 From 7e32c63e6d065062b4540b9bf467989ee86e1f2a Mon Sep 17 00:00:00 2001 From: Rohit Thakur Date: Wed, 22 Apr 2020 16:22:46 +0530 Subject: test cases added Signed-off-by: Rohit Thakur --- .../network/vyos/argspec/ospfv2/ospfv2.py | 16 +- .../network/vyos/config/ospfv2/ospfv2.py | 467 ++-- .../network/vyos/facts/ospfv2/ospfv2.py | 309 ++- plugins/module_utils/network/vyos/utils/utils.py | 1 - plugins/modules/vyos_ospfv2.py | 2366 ++++++++++++-------- .../targets/vyos_ospfv2/defaults/main.yaml | 3 + .../integration/targets/vyos_ospfv2/meta/main.yaml | 3 + .../integration/targets/vyos_ospfv2/tasks/cli.yaml | 19 + .../targets/vyos_ospfv2/tasks/main.yaml | 4 + .../vyos_ospfv2/tests/cli/_parsed_config.cfg | 29 + .../targets/vyos_ospfv2/tests/cli/_populate.yaml | 35 + .../vyos_ospfv2/tests/cli/_remove_config.yaml | 6 + .../vyos_ospfv2/tests/cli/delete_single.yaml | 57 + .../targets/vyos_ospfv2/tests/cli/deleted.yaml | 48 + .../vyos_ospfv2/tests/cli/empty_config.yaml | 49 + .../targets/vyos_ospfv2/tests/cli/gathered.yaml | 33 + .../targets/vyos_ospfv2/tests/cli/merged.yaml | 101 + .../vyos_ospfv2/tests/cli/merged_update.yaml | 70 + .../targets/vyos_ospfv2/tests/cli/parsed.yaml | 41 + .../targets/vyos_ospfv2/tests/cli/rendered.yaml | 88 + .../targets/vyos_ospfv2/tests/cli/replaced.yaml | 100 + .../targets/vyos_ospfv2/tests/cli/rtt.yaml | 149 ++ .../integration/targets/vyos_ospfv2/vars/main.yaml | 444 ++++ .../network/vyos/fixtures/vyos_ospfv2_config.cfg | 9 + .../unit/modules/network/vyos/test_vyos_ospfv2.py | 526 +++++ tests/unit/modules/network/vyos/vyos_module.py | 2 +- 26 files changed, 3817 insertions(+), 1158 deletions(-) create mode 100644 tests/integration/targets/vyos_ospfv2/defaults/main.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/meta/main.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tasks/cli.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tasks/main.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/_parsed_config.cfg create mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/_populate.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/_remove_config.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/delete_single.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/deleted.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/empty_config.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/gathered.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/merged.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/merged_update.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/parsed.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/rendered.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/replaced.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/rtt.yaml create mode 100644 tests/integration/targets/vyos_ospfv2/vars/main.yaml create mode 100644 tests/unit/modules/network/vyos/fixtures/vyos_ospfv2_config.cfg create mode 100644 tests/unit/modules/network/vyos/test_vyos_ospfv2.py (limited to 'plugins') diff --git a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py index 1b11d3c9..a2a6e04e 100644 --- a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py @@ -34,7 +34,6 @@ class Ospfv2Args(object): # pylint: disable=R0903 argument_spec = { 'config': { - 'elements': 'dict', 'options': { 'auto_cost': { 'options': { @@ -140,10 +139,10 @@ class Ospfv2Args(object): # pylint: disable=R0903 }, 'type': 'list' }, - 'ospf_area': { + 'areas': { 'elements': 'dict', 'options': { - 'area': { + 'area_id': { 'type': 'str' }, 'area_type': { @@ -159,6 +158,9 @@ class Ospfv2Args(object): # pylint: disable=R0903 'no_summary': { 'type': 'bool' }, + 'set': { + 'type': 'bool' + }, 'translate': { 'choices': ['always', 'candidate', 'never'], @@ -175,6 +177,9 @@ class Ospfv2Args(object): # pylint: disable=R0903 }, 'no_summary': { 'type': 'bool' + }, + 'set': { + 'type': 'bool' } }, 'type': 'dict' @@ -227,6 +232,7 @@ class Ospfv2Args(object): # pylint: disable=R0903 'authentication': { 'options': { 'md5': { + 'elements': 'dict', 'options': { 'key_id': { 'type': 'int' @@ -235,7 +241,7 @@ class Ospfv2Args(object): # pylint: disable=R0903 'type': 'str' } }, - 'type': 'dict' + 'type': 'list' }, 'plaintext_password': { 'type': 'str' @@ -343,7 +349,7 @@ class Ospfv2Args(object): # pylint: disable=R0903 'type': 'dict' } }, - 'type': 'list' + 'type': 'dict' }, "running_config": {"type": "str"}, 'state': { diff --git a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py index 0109ca1a..13645cd6 100644 --- a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py @@ -29,19 +29,16 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils list_diff_want_only, _in_target, _is_w_same, _bool_to_str ) + class Ospfv2(ConfigBase): + """ The vyos_ospfv2 class """ - gather_subset = [ - '!all', - '!min', - ] + gather_subset = ['!all', '!min'] - gather_network_resources = [ - 'ospfv2', - ] + gather_network_resources = ['ospfv2'] def __init__(self, module): super(Ospfv2, self).__init__(module) @@ -52,7 +49,10 @@ class Ospfv2(ConfigBase): :rtype: A dictionary :returns: The current configuration as a dictionary """ - facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources, data=data) + + (facts, _warnings) = \ + Facts(self._module).get_facts(self.gather_subset, + self.gather_network_resources, data=data) ospfv2_facts = facts['ansible_network_resources'].get('ospfv2') if not ospfv2_facts: return [] @@ -64,6 +64,7 @@ class Ospfv2(ConfigBase): :rtype: A dictionary :returns: The result from module execution """ + result = {'changed': False} warnings = list() commands = list() @@ -73,41 +74,38 @@ class Ospfv2(ConfigBase): else: existing_ospfv2_facts = [] - if self.state in self.ACTION_STATES or self.state == "rendered": + if self.state in self.ACTION_STATES or self.state == 'rendered': commands.extend(self.set_config(existing_ospfv2_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) - result["changed"] = True + result['changed'] = True if self.state in self.ACTION_STATES: - result["commands"] = commands + result['commands'] = commands - if self.state in self.ACTION_STATES or self.state == "gathered": + if self.state in self.ACTION_STATES or self.state == 'gathered': changed_ospfv2_facts = self.get_ospfv2_facts() - elif self.state == "rendered": - result["rendered"] = commands - elif self.state == "parsed": - running_config = self._module.params["running_config"] + elif self.state == 'rendered': + result['rendered'] = commands + elif self.state == 'parsed': + running_config = self._module.params['running_config'] if not running_config: - self._module.fail_json( - msg="value of running_config parameter must not be empty for state parsed" - ) - result["parsed"] = self.get_ospfv2_facts( - data=running_config - ) + self._module.fail_json(msg='value of running_config parameter must not be empty for state parsed') + result['parsed'] = \ + self.get_ospfv2_facts(data=running_config) else: changed_ospfv2_facts = [] if self.state in self.ACTION_STATES: - result["before"] = existing_ospfv2_facts - if result["changed"]: - result["after"] = changed_ospfv2_facts - elif self.state == "gathered": - result["gathered"] = changed_ospfv2_facts + result['before'] = existing_ospfv2_facts + if result['changed']: + result['after'] = changed_ospfv2_facts + elif self.state == 'gathered': + result['gathered'] = changed_ospfv2_facts - result["warnings"] = warnings + result['warnings'] = warnings return result def set_config(self, existing_ospfv2_facts): @@ -118,6 +116,7 @@ class Ospfv2(ConfigBase): :returns: the commands necessary to migrate the current configuration to the desired configuration """ + want = self._module.params['config'] have = existing_ospfv2_facts resp = self.set_state(want, have) @@ -132,24 +131,18 @@ class Ospfv2(ConfigBase): :returns: the commands necessary to migrate the current configuration to the desired configuration """ + commands = [] - if self.state in ("merged", "replaced", "overridden", "rendered") and not w: - self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - self.state - ) - ) - if self.state == "overridden": - commands.extend(self._state_overridden(w, h)) - elif self.state == "deleted": + if self.state in ('merged', 'replaced', 'overridden', 'rendered' + ) and not w: + self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(self.state)) + + if self.state == 'deleted': commands.extend(self._state_deleted(w, h)) - elif w: - if self.state == "merged" or self.state == "rendered": - for w_item in w: - commands.extend(self._state_merged(w_item, h)) - elif self.state == "replaced": - for w_item in w: - commands.extend(self._state_replaced(w_item, h)) + elif self.state in ('merged', 'rendered'): + commands.extend(self._state_merged(w, h)) + elif self.state == 'replaced': + commands.extend(self._state_replaced(w, h)) return commands def search_obj_in_have(self, have, w_name, key): @@ -160,6 +153,7 @@ class Ospfv2(ConfigBase): :param type: rule_sets/rule/r_list. :return: rule-set/rule. """ + if have: for item in have: if item[key] == w_name[key]: @@ -173,12 +167,12 @@ class Ospfv2(ConfigBase): :returns: the commands necessary to migrate the current configuration to the desired configuration """ + commands = [] - h_item = {} if have: - h_item = have[0] - commands.extend(self._render_ospf_param(h_item, want, opr=False)) - commands.extend(self._render_ospf_param(want, h_item)) + commands.extend(self._render_ospf_param(have, want, + opr=False)) + commands.extend(self._render_ospf_param(want, have)) return commands def _state_merged(self, want, have): @@ -188,11 +182,9 @@ class Ospfv2(ConfigBase): :returns: the commands necessary to merge the provided into the current configuration """ + commands = [] - h_item = {} - if have: - h_item = have[0] - commands.extend(self._render_ospf_param(want, h_item)) + commands.extend(self._render_ospf_param(want, have)) return commands def _state_deleted(self, want, have): @@ -202,18 +194,27 @@ class Ospfv2(ConfigBase): :returns: the commands necessary to remove the current configuration of the provided objects """ + commands = [] - if want: - for w in want: - if have: - h = have[0] - if h: - for key, val in iteritems(w): - if key in h: - if key == 'ospf_area': - key = 'area' - commands.append(self._compute_command(attr=key, opr=False)) - elif have and have[0]: + if want and have: + for (key, val) in iteritems(want): + if key in have: + if key == 'areas': + h_areas = have.get(key) or [] + key = 'area' + for area in h_areas: + h_vlist = area.get('virtual_link') or [] + if h_vlist: + for vlink in h_vlist: + cmd = self._compute_command( + key=key + ' ' + area['area_id'], attr='virtual_link', + val=vlink['address'], opr=False) + commands.append(cmd) + commands.append(self._compute_command(key=key, + attr=area['area_id'], opr=False)) + commands.append(self._compute_command(attr=key, + opr=False)) + elif have: commands.append('delete protocols ospf') return commands @@ -227,16 +228,15 @@ class Ospfv2(ConfigBase): :param opr: True/False. :return: generated commands list. """ + commands = [] w = deepcopy(remove_empties(want)) - leaf = ( - "default_metric", - "log_adjacency_changes" - ) + leaf = ('default_metric', 'log_adjacency_changes') if w: - for key, val in iteritems(w): + for (key, val) in iteritems(w): if opr and key in leaf and not _is_w_same(w, have, key): - commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) + commands.append(self._form_attr_cmd(attr=key, + val=_bool_to_str(val), opr=opr)) elif not opr and key in leaf and not _in_target(have, key): commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) else: @@ -253,21 +253,25 @@ class Ospfv2(ConfigBase): :param opr: operation. :return: list of commands. """ + commands = [] - if key in ("neighbor", "redistribute"): - commands.extend(self._render_list_dict_param(key, w, h, opr=opr)) - elif key in ("default_information", 'max_metric'): - commands.extend(self._render_nested_dict_param(key, w, h, opr=opr)) - elif key in ("mpls_te", "auto_cost", "parameters", "auto_cost"): + if key in ('neighbor', 'redistribute'): + commands.extend(self._render_list_dict_param(key, w, h, + opr=opr)) + elif key in ('default_information', 'max_metric'): + commands.extend(self._render_nested_dict_param(key, w, h, + opr=opr)) + elif key in ('mpls_te', 'auto_cost', 'parameters', 'auto_cost'): commands.extend(self._render_dict_param(key, w, h, opr=opr)) - elif key in ("route_map", "passive_interface", "passive_interface_exclude"): + elif key in ('route_map', 'passive_interface', + 'passive_interface_exclude'): commands.extend(self._render_list_param(key, w, h, opr=opr)) - elif key == "ospf_area": - commands.extend(self._render_ospf_area(key, w, h, opr=opr)) - elif key == "timers": - commands.extend(self._render_timers(key, w, h, opr=opr, remove=remove)) - elif key == "distance": - commands.extend(self._render_distance(key, w, h, opr=opr, remove=remove)) + elif key == 'areas': + commands.extend(self._render_areas(key, w, h, opr=opr)) + elif key == 'timers': + commands.extend(self._render_timers(key, w, h, opr=opr)) + elif key == 'distance': + commands.extend(self._render_distance(key, w, h, opr=opr)) return commands def _render_dict_param(self, attr, want, have, opr=True): @@ -279,6 +283,7 @@ class Ospfv2(ConfigBase): :param opr: True/False. :return: generated list of commands. """ + commands = [] h = {} if have: @@ -288,18 +293,19 @@ class Ospfv2(ConfigBase): elif want[attr]: leaf_dict = {'auto_cost': 'reference_bandwidth', 'mpls_te': ('enabled', 'router_address'), - 'parameters': ("router_id", "abr_type", "opaque_lsa", "rfc1583_compatibility")} + 'parameters': ('router_id', 'abr_type', + 'opaque_lsa', 'rfc1583_compatibility')} leaf = leaf_dict[attr] - for item, value in iteritems(want[attr]): + for (item, value) in iteritems(want[attr]): if opr and item in leaf and not _is_w_same(want[attr], h, item): - if item == "enabled": + if item == 'enabled': item = 'enable' - if item in ("opaque_lsa", "enable", "rfc1583_compatibility"): + if item in ('opaque_lsa', 'enable', 'rfc1583_compatibility'): commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) else: commands.append(self._form_attr_cmd(key=attr, attr=item, val=value, opr=opr)) elif not opr and item in leaf and not _in_target(h, item): - if item == "enabled": + if item == 'enabled': commands.append(self._form_attr_cmd(key=attr, attr='enable', opr=opr)) else: commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) @@ -315,6 +321,7 @@ class Ospfv2(ConfigBase): :param opr: True/False. :return: generated list of commands. """ + commands = [] h = [] if want: @@ -327,7 +334,7 @@ class Ospfv2(ConfigBase): if opr: members = list_diff_want_only(w, h) for member in members: - command = cmd + attr.replace("_","-") + " " + command = cmd + attr.replace('_', '-') + ' ' if attr == 'network': command += member['address'] else: @@ -338,11 +345,96 @@ class Ospfv2(ConfigBase): for member in w: if attr == 'network': if not self.search_obj_in_have(h, member, 'address'): - commands.append(cmd + attr.replace("_","-") + ' ' + member['address']) + commands.append(cmd + attr.replace('_','-') + + ' ' + member['address']) elif member not in h: - commands.append(cmd + attr.replace("_","-") + ' ' + member) + commands.append(cmd + attr.replace('_', '-') + + ' ' + member) else: - commands.append(cmd + " " + attr.replace("_","-")) + commands.append(cmd + ' ' + attr.replace('_', '-')) + return commands + + def _render_vlink(self, attr, want, have, cmd=None, opr=True): + """ + This function forms the set/delete commands based on the 'opr' type + for attributes with in desired list of dictionary. + :param attr: attribute name. + :param w: the desired config. + :param h: the target config. + :param cmd: commands to be prepend. + :param opr: True/False. + :return: generated commands list. + """ + + commands = [] + h = [] + name = {'virtual_link': 'address'} + leaf_dict = {'virtual_link': ('address', 'dead_interval', + 'transmit_delay', 'hello_interval', + 'retransmit_interval')} + leaf = leaf_dict[attr] + w = want.get(attr) or [] + if have: + h = have.get(attr) or [] + if not opr and not h: + commands.append(cmd + attr.replace('_', '-')) + elif w: + for w_item in w: + for (key, val) in iteritems(w_item): + if not cmd: + cmd = self._compute_command(opr=opr) + h_item = self.search_obj_in_have(h, w_item, name[attr]) + if opr and key in leaf and not _is_w_same(w_item, h_item, key): + if key in 'address': + commands.append(cmd + + attr.replace('_', '-') + + ' ' + str(val)) + else: + commands.append(cmd + attr.replace('_', '-') + + ' ' + w_item[name[attr]] + + ' ' + key.replace('_', '-') + + ' ' + str(val)) + elif not opr and key in leaf and not _in_target(h_item, key): + if key in 'address': + commands.append(cmd + attr.replace('_', '-') + + ' ' + str(val)) + else: + commands.append(cmd + attr.replace('_', '-') + + ' ' + w_item[name[attr]] + + ' ' + key) + elif key == 'authentication': + commands.extend(self._render_vlink_auth( + attr, + key, + w_item, + h_item, + w_item['address'], + cmd, + opr, + )) + return commands + + def _render_vlink_auth(self, attr, key, want, have, address, cmd=None, opr=True): + """ + This function forms the set/delete commands based on the 'opr' type + for attributes with in desired list of dictionary. + :param attr: attribute name. + :param w: the desired config. + :param h: the target config. + :param cmd: commands to be prepend. + :param opr: True/False. + :return: generated commands list. + """ + + commands = [] + h = [] + + w = want.get(key) or {} + if have: + h = have.get(key) or {} + cmd += attr.replace('_', '-') + ' ' + address + ' ' + key + ' ' + commands.extend(self._render_list_dict_param('md5', w, h, cmd, + opr)) return commands def _render_list_dict_param(self, attr, want, have, cmd=None, opr=True): @@ -356,17 +448,24 @@ class Ospfv2(ConfigBase): :param opr: True/False. :return: generated commands list. """ + commands = [] h = [] - name = {'redistribute': 'route_type', - 'neighbor': 'neighbor_id', - 'range': 'address', - 'vlink': 'address'} - leaf_dict = {'redistribute': ("metric", "route_map", "route_type", "metric_type"), - 'neighbor': ("priority", "poll_interval", "neighbor_id"), - 'range': ("cost", "address", "substitute", "not_advertise"), - 'vlink': ("address", "dead_interval", "transmit_delay", "hello_interval", "retransmit_interval") - } + name = { + 'redistribute': 'route_type', + 'neighbor': 'neighbor_id', + 'range': 'address', + 'md5': 'key_id', + 'vlink': 'address', + } + leaf_dict = { + 'md5': 'md5_key', + 'redistribute': ('metric', 'route_map', 'route_type', 'metric_type'), + 'neighbor': ('priority', 'poll_interval', 'neighbor_id'), + 'range': ('cost', 'address', 'substitute', 'not_advertise'), + 'vlink': ('address', 'dead_interval', 'transmit_delay', + 'hello_interval', 'retransmit_interval'), + } leaf = leaf_dict[attr] w = want.get(attr) or [] if have: @@ -375,26 +474,43 @@ class Ospfv2(ConfigBase): commands.append(self._compute_command(attr=attr, opr=opr)) elif w: for w_item in w: - for key, val in iteritems(w_item): + for (key, val) in iteritems(w_item): if not cmd: cmd = self._compute_command(opr=opr) h_item = self.search_obj_in_have(h, w_item, name[attr]) if opr and key in leaf and not _is_w_same(w_item, h_item, key): - if key == 'cost': - commands.append(cmd + attr + ' ' + w_item[name[attr]] + ' ' + key + ' ' + str(val)) - elif key == 'not_advertise': - commands.append(cmd + attr + ' ' + w_item[name[attr]] + ' ' + key.replace("_","-")) - elif key in ('route_type', 'neighbor_id', 'address'): + if key in ('route_type', 'neighbor_id', + 'address', 'key_id'): commands.append(cmd + attr + ' ' + str(val)) - elif key == 'authentication': - commands.append(self._render_vlink(key, w_item, h_item, cmd, opr)) + elif key == 'cost': + commands.append(cmd + attr + + ' ' + w_item[name[attr]] + + ' ' + key + + ' ' + str(val)) + elif key == 'not_advertise': + commands.append(cmd + attr + + ' ' + w_item[name[attr]] + + ' ' + key.replace('_', '-')) + elif key == 'md5_key': + commands.append(cmd + attr + + ' ' + 'key-id' + + ' ' + str(w_item[name[attr]]) + + ' ' + key.replace('_', '-') + + ' ' + w_item[key]) else: - commands.append(cmd + attr + ' ' + w_item[name[attr]] + ' ' + key.replace("_", "-") + ' ' + str(val)) - elif not opr and key in leaf and not _in_target(h_item, key): - if key in ('route_type', 'neighbor_id', 'address'): + commands.append(cmd + attr + + ' ' + w_item[name[attr]] + + ' ' + key.replace('_', '-') + + ' ' + str(val)) + elif not opr and key in leaf \ + and not _in_target(h_item, key): + if key in ('route_type', 'neighbor_id', + 'address', 'key_id'): commands.append(cmd + attr + ' ' + str(val)) else: - commands.append(cmd + (attr + ' ' + w_item[name[attr]] + ' ' + key)) + commands.append(cmd + attr + + ' ' + w_item[name[attr]] + + ' ' + key) return commands def _render_nested_dict_param(self, attr, want, have, opr=True): @@ -408,13 +524,12 @@ class Ospfv2(ConfigBase): :param opr: True/False. :return: generated commands list. """ + commands = [] attr_dict = {'default_information': 'originate', - 'max_metric': 'router_lsa', - } - leaf_dict = {'default_information': ("always", "metric", "metric_type", "route_map"), - 'max_metric': ("always", "metric", "metric_type", "route_map"), - } + 'max_metric': 'router_lsa'} + leaf_dict = {'default_information': ('always', 'metric', 'metric_type', 'route_map'), + 'max_metric': ('administrative', 'on_startup', 'on_shutdown')} h = {} w = want.get(attr) or {} if have: @@ -430,18 +545,24 @@ class Ospfv2(ConfigBase): leaf = leaf_dict[attr] if h and key in h.keys(): h_attrib = h.get(key) or {} - for item, val in iteritems(w[key]): + for (item, val) in iteritems(w[key]): if opr and item in leaf and not _is_w_same(w[key], h_attrib, item): - if item in ('administrative', 'always'): - commands.append(cmd + (attr.replace("_","-") + " " + key.replace("_","-") + " " + item.replace("_","-"))) - else: - commands.append(cmd + (attr.replace("_","-") + " " + key.replace("_","-") + " " + item.replace("_","-") + " " + str(val))) - - elif not opr and item in leaf and not _in_target(h_attrib, item): - commands.append(cmd + (attr + " " + item)) + if item in ('administrative', 'always') and val: + commands.append(cmd + attr.replace('_', '-') + + ' ' + key.replace('_', '-') + + ' ' + item.replace('_', '-')) + elif item not in ('administrative', 'always'): + commands.append(cmd + attr.replace('_', '-') + + ' ' + key.replace('_', '-') + + ' ' + item.replace('_', '-') + + ' ' + str(val)) + elif not opr and item in leaf \ + and not _in_target(h_attrib, item): + + commands.append(cmd + attr + ' ' + item) return commands - def _render_ospf_area(self, attr, want, have, opr=True): + def _render_areas(self, attr, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for ospf area attributes. @@ -451,32 +572,38 @@ class Ospfv2(ConfigBase): :param opr: True/False. :return: generated commands list. """ + commands = [] h_lst = {} w_lst = want.get(attr) or [] - l_set = ("area", "shortcut", "authentication") + l_set = ('area_id', 'shortcut', 'authentication') if have: h_lst = have.get(attr) or [] if not opr and not h_lst: commands.append(self._form_attr_cmd(attr='area', opr=opr)) elif w_lst: for w_area in w_lst: - cmd = self._compute_command(key='area', attr=_bool_to_str(w_area['area']), opr=opr) + ' ' - h_area = self.search_obj_in_have(h_lst, w_area, 'area') + cmd = self._compute_command( + key='area', attr=_bool_to_str(w_area['area_id']), opr=opr + ) + ' ' + h_area = self.search_obj_in_have(h_lst, w_area, 'area_id') if not opr and not h_area: - commands.append(self._form_attr_cmd(key='area', attr=w_area['area'], opr=opr)) + commands.append(self._form_attr_cmd(key='area', + attr=w_area['area_id'], opr=opr)) else: - for key, val in iteritems(w_area): + for (key, val) in iteritems(w_area): if opr and key in l_set and not _is_w_same(w_area, h_area, key): - if key == 'area': - commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) + if key == 'area_id': + commands.append(self._form_attr_cmd(attr='area', + val=_bool_to_str(val), opr=opr)) else: - commands.append(cmd + key + ' ' + _bool_to_str(val).replace("_","-")) + commands.append(cmd + key + ' ' + + _bool_to_str(val).replace('_', '-')) elif not opr and key in l_set: - if key == 'area' and not _in_target(h_area, key): + if key == 'area_id' and not _in_target(h_area, key): commands.append(cmd) continue - elif key != 'area' and not _in_target(h_area, key): + elif key != 'area_id' and not _in_target(h_area, key): commands.append(cmd + val + ' ' + key) elif key == 'area_type': commands.extend(self._render_area_type(w_area, h_area, key, cmd, opr)) @@ -499,33 +626,61 @@ class Ospfv2(ConfigBase): :param opr: True/False. :return: generated commands list. """ + commands = [] h_type = {} w_type = want.get(attr) or [] if have: h_type = have.get(attr) or {} if not opr and not h_type: - commands.append(cmd + attr.replace("_","-")) + commands.append(cmd + attr.replace('_', '-')) elif w_type: - key = "normal" + key = 'normal' if opr and key in w_type.keys() and not _is_w_same(w_type, h_type, key): - commands.append(cmd + attr.replace("_","-") + ' ' + key) - elif not opr and key in w_type.keys() and not (h_type and key in h_type.keys()): - commands.append(cmd + want['area'] + ' ' + attr.replace("_","-")) - - a_type = {'nssa': ("default_cost", "no_summary", "translate"), - 'stub': ("default_cost", "no_summary")} + if not w_type[key] and h_type and h_type[key]: + commands.append(cmd.replace('set', 'delete') + + attr.replace('_', '-') + + ' ' + key) + elif w_type[key]: + commands.append(cmd + attr.replace('_', '-') + + ' ' + key) + elif not opr and key in w_type.keys() and not (h_type + and key in h_type.keys()): + commands.append(cmd + want['area'] + + ' ' + attr.replace('_', '-')) + + a_type = {'nssa': ('set', 'default_cost', 'no_summary', 'translate'), + 'stub': ('set', 'default_cost', 'no_summary')} for key in a_type: w_area = want[attr].get(key) or {} h_area = {} if w_area: if h_type and key in h_type.keys(): h_area = h_type.get(key) or {} - for item, val in iteritems(w_type[key]): - if opr and item in a_type[key] and not _is_w_same(w_type[key], h_area, item): - commands.append(cmd + (attr.replace("_","-") + " " + key + " " + item.replace("_","-") + " " + str(val))) - elif not opr and item in a_type[key] and not (h_type and key in h_type): - commands.append(cmd + (want['area'] + ' ' + attr.replace("_","-") + " " + key + " " + item.replace("_","-"))) + for (item, val) in iteritems(w_type[key]): + if opr and item in a_type[key] \ + and not _is_w_same(w_type[key], h_area, item): + if item == 'set' and val: + commands.append(cmd + attr.replace('_', '-') + + ' ' + key) + elif not val and h_area and h_area[item]: + commands.append(cmd.replace('set', 'delete') + + attr.replace('_', '-') + + ' ' + key) + elif item != 'set': + commands.append(cmd + attr.replace('_', '-') + + ' ' + key + + ' ' + item.replace('_', '-') + + ' ' + str(val)) + elif not opr and item in a_type[key] \ + and not (h_type and key in h_type): + if item == 'set': + commands.append(cmd + attr.replace('_', '-') + + ' ' + key) + else: + commands.append(cmd + want['area'] + + ' ' + attr.replace('_', '-') + + ' ' + key + ' ' + item.replace('_', '-')) return commands def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True): @@ -537,6 +692,7 @@ class Ospfv2(ConfigBase): :param opr: True/False. :return: generated command. """ + return self._compute_command(key, attr=self._map_attrib(attr), val=val, opr=opr) def _compute_command(self, key=None, attr=None, val=None, remove=False, opr=True): @@ -548,15 +704,16 @@ class Ospfv2(ConfigBase): :param opr: True/False. :return: generated command. """ + if remove or not opr: - cmd = "delete protocols ospf " + cmd = 'delete protocols ospf ' else: - cmd = "set protocols ospf " + cmd = 'set protocols ospf ' if key: - cmd += key.replace("_", "-") + " " + cmd += key.replace('_', '-') + ' ' if attr: - cmd += attr.replace("_", "-") - if val and opr: + cmd += attr.replace('_', '-') + if val: cmd += " '" + str(val) + "'" return cmd @@ -567,4 +724,6 @@ class Ospfv2(ConfigBase): :param attrib: attribute :return: regex string """ - return 'disable' if attrib =='disabled' else attrib.replace("_","-") + + return ('disable' if attrib == 'disabled' + else attrib.replace('_', '-')) diff --git a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py index 3457fac8..0467b721 100644 --- a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py @@ -9,19 +9,29 @@ It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + from re import findall, search, M from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import Ospfv2Args class Ospfv2Facts(object): + """ The vyos ospfv2 fact class """ - def __init__(self, module, subspec='config', options='options'): + def __init__( + self, + module, + subspec='config', + options='options', + ): + self._module = module self.argument_spec = Ospfv2Args.argument_spec spec = deepcopy(self.argument_spec) @@ -46,25 +56,22 @@ class Ospfv2Facts(object): :rtype: dictionary :returns: facts """ + if not data: data = self.get_device_data(connection) + # typically data is populated from the current device configuration # data = connection.get('show running-config | section ^interface') # using mock data instead - objs = [] + + objs = {} ospfv2 = findall(r"^set protocols ospf (.+)", data, M) if ospfv2: - config = self.render_config(ospfv2) - if config: - objs.append(config) - ansible_facts["ansible_network_resources"].pop("ospfv2", None) + objs = self.render_config(ospfv2) facts = {} - if objs: - facts["ospfv2"] = [] - params = utils.validate_config(self.argument_spec, {"config": objs}) - for cfg in params["config"]: - facts["ospfv2"].append(utils.remove_empties(cfg)) - ansible_facts["ansible_network_resources"].update(facts) + params = utils.validate_config(self.argument_spec,{'config': objs}) + facts['ospfv2'] = utils.remove_empties(params['config']) + ansible_facts['ansible_network_resources'].update(facts) return ansible_facts def render_config(self, conf): @@ -74,37 +81,38 @@ class Ospfv2Facts(object): :param conf: The configuration :returns: The generated config """ - conf = "\n".join(filter(lambda x: x, conf)) - a_lst = ["default_metric", "log_adjacency_changes"] + + conf = '\n'.join(filter(lambda x: x, conf)) + a_lst = ['default_metric', 'log_adjacency_changes'] config = self.parse_attr(conf, a_lst) if not config: config = {} - config["timers"] = self.parse_timers(conf) - config["auto_cost"] = self.parse_auto_cost(conf) - config["distance"] = self.parse_distance(conf) - config["max_metric"] = self.parse_max_metric(conf) - config["mpls_te"] = self.parse_attrib(conf, "mpls_te", "mpls-te") - config["default_information"] = self.parse_def_info(conf) - config["parameters"] = self.parse_attrib(conf, "parameters", "parameters") - config["route_map"] = self.parse_leaf_list(conf, "route-map") - config["ospf_area"] = self.parse_attrib_list(conf, "area", "area") - config["neighbor"] = self.parse_attrib_list(conf, "neighbor", "neighbor_id") - config["passive_interface"] = self.parse_leaf_list(conf, "passive-interface") - config["redistribute"] = self.parse_attrib_list(conf, "redistribute", "route_type") - config["passive_interface_exclude"] = self.parse_leaf_list(conf, "passive-interface-exclude") + config['timers'] = self.parse_timers(conf) + config['auto_cost'] = self.parse_auto_cost(conf) + config['distance'] = self.parse_distance(conf) + config['max_metric'] = self.parse_max_metric(conf) + config['default_information'] = self.parse_def_info(conf) + config['route_map'] = self.parse_leaf_list(conf, 'route-map') + config['mpls_te'] = self.parse_attrib(conf, 'mpls_te', 'mpls-te') + config['areas'] = self.parse_attrib_list(conf, 'area', 'area_id') + config['parameters'] = self.parse_attrib(conf, 'parameters', 'parameters') + config['neighbor'] = self.parse_attrib_list(conf, 'neighbor', 'neighbor_id') + config['passive_interface'] = self.parse_leaf_list(conf, 'passive-interface') + config['redistribute'] = self.parse_attrib_list(conf, 'redistribute', 'route_type') + config['passive_interface_exclude'] = self.parse_leaf_list(conf, 'passive-interface-exclude') return config - def parse_timers(self, conf, attrib=None): + def parse_timers(self, conf): """ This function triggers the parsing of 'timers' attributes :param conf: configuration - :param attrib: attribute name :return: generated config dictionary """ + cfg_dict = {} - cfg_dict["refresh"] = self.parse_refresh(conf, "refresh") - cfg_dict["throttle"] = self.parse_throttle(conf, "spf") + cfg_dict['refresh'] = self.parse_refresh(conf, 'refresh') + cfg_dict['throttle'] = self.parse_throttle(conf, 'spf') return cfg_dict def parse_throttle(self, conf, attrib=None): @@ -114,6 +122,7 @@ class Ospfv2Facts(object): :param attrib: 'spf' :return: generated config dictionary """ + cfg_dict = {} cfg_dict[attrib] = self.parse_attrib(conf, attrib, match=attrib) return cfg_dict @@ -125,39 +134,9 @@ class Ospfv2Facts(object): :param attrib: 'refresh' :return: generated config dictionary """ - cfg_dict = self.parse_attr(conf, ["timers"], match=attrib) - return cfg_dict - def parse_attrib_list(self, conf, attrib, param): - """ - This function forms the regex to fetch the listed attributes - from config - :param conf: configuration data - :param attrib: attribute name - :param param: parameter data - :return: generated rule list configuration - """ - r_lst = [] - if attrib == "area": - items = findall(r"^" + attrib + " (?:\'*)(\S+)(?:\'*)", conf, M) - else: - items = findall(r"" + attrib + " (?:\'*)(\S+)(?:\'*)", conf, M) - if items: - a_lst = [] - for item in set(items): - i_regex = r" %s .+$" % item - cfg = "\n".join(findall(i_regex, conf, M)) - if attrib == 'area': - obj = self.parse_area(cfg, item) - elif attrib == 'virtual-link': - obj = self.parse_vlink(cfg) - else: - obj = self.parse_attrib(cfg, attrib) - obj[param] = item.strip("'") - if obj: - a_lst.append(obj) - r_lst = sorted(a_lst, key=lambda i: i[param]) - return r_lst + cfg_dict = self.parse_attr(conf, ['timers'], match=attrib) + return cfg_dict def parse_leaf_list(self, conf, attrib): """ @@ -167,8 +146,9 @@ class Ospfv2Facts(object): :param attrib: attribute name :return: generated rule list configuration """ + lst = [] - items = findall(r"^" + attrib + " (?:\'*)(\S+)(?:\'*)", conf, M) + items = findall(r"^" + attrib + " (?:\'*)(\\S+)(?:\'*)", conf, M) if items: for i in set(items): lst.append(i.strip("'")) @@ -181,8 +161,9 @@ class Ospfv2Facts(object): :param attrib: attribute name :return: generated config dictionary """ - cfg_dict = self.parse_attr(conf, ["global"], match=attrib) - cfg_dict["ospf"] = self.parse_ospf(conf, "ospf") + + cfg_dict = self.parse_attr(conf, ['global'], match=attrib) + cfg_dict['ospf'] = self.parse_ospf(conf, 'ospf') return cfg_dict def parse_ospf(self, conf, attrib=None): @@ -192,18 +173,19 @@ class Ospfv2Facts(object): :param attrib: 'ospf' :return: generated config dictionary """ + cfg_dict = self.parse_attrib(conf, 'ospf', match=attrib) return cfg_dict - def parse_max_metric(self, conf, attrib=None): + def parse_max_metric(self, conf): """ This function triggers the parsing of 'max_metric' attributes :param conf: configuration - :param attrib: attribute name :return: generated config dictionary """ + cfg_dict = {} - cfg_dict["router_lsa"] = self.parse_attrib(conf, "router_lsa", match="router-lsa") + cfg_dict['router_lsa'] = self.parse_attrib(conf, 'router_lsa', match='router-lsa') return cfg_dict def parse_auto_cost(self, conf, attrib=None): @@ -213,18 +195,20 @@ class Ospfv2Facts(object): :param attrib: attribute name :return: generated config dictionary """ - cfg_dict = self.parse_attr(conf, ["reference_bandwidth"], match=attrib) + + cfg_dict = self.parse_attr(conf, ['reference_bandwidth'], + match=attrib) return cfg_dict - def parse_def_info(self, conf, attrib=None): + def parse_def_info(self, conf): """ This function triggers the parsing of 'default_information' attributes :param conf: configuration - :param attrib: attribute name :return: generated config dictionary """ + cfg_dict = {} - cfg_dict["originate"] = self.parse_attrib(conf, "originate", "originate") + cfg_dict['originate'] = self.parse_attrib(conf, 'originate', 'originate') return cfg_dict def parse_area(self, conf, area_id): @@ -234,16 +218,28 @@ class Ospfv2Facts(object): :param area_id: area identity :return: generated rule configuration dictionary. """ - rule = self.parse_attrib(conf, "area", match=area_id) + + rule = self.parse_attrib(conf, 'area_id', match=area_id) r_sub = { - "area_type": self.parse_area_type(conf, "area-type"), - "network": self.parse_network(conf), - "range": self.parse_attrib_list(conf, "range", "address"), - "virtual_link": self.parse_attrib_list(conf, "virtual-link", "address") - } + 'area_type': self.parse_area_type(conf, 'area-type'), + 'network': self.parse_network(conf), + 'range': self.parse_attrib_list(conf, 'range', 'address'), + 'virtual_link': self.parse_attrib_list(conf, 'virtual-link', 'address'), + } rule.update(r_sub) return rule + def parse_key(self, conf, key_id): + """ + This function triggers the parsing of 'area' attributes. + :param conf: configuration data + :param area_id: area identity + :return: generated rule configuration dictionary. + """ + + rule = self.parse_attrib(conf, 'key_id', match=key_id) + return rule + def parse_area_type(self, conf, attrib=None): """ This function triggers the parsing of 'area_type' attributes @@ -251,9 +247,10 @@ class Ospfv2Facts(object): :param attrib: 'area-type' :return: generated config dictionary """ - cfg_dict = self.parse_attr(conf, ["normal"], match=attrib) - cfg_dict["nssa"] = self.parse_attrib(conf, "nssa") - cfg_dict["stub"] = self.parse_attrib(conf, "stub") + + cfg_dict = self.parse_attr(conf, ['normal'], match=attrib) + cfg_dict['nssa'] = self.parse_attrib(conf, 'nssa', match='nssa') + cfg_dict['stub'] = self.parse_attrib(conf, 'stub', match='stub') return cfg_dict def parse_network(self, conf): @@ -262,24 +259,27 @@ class Ospfv2Facts(object): :param conf: configuration data :return: generated rule list configuration """ + a_lst = [] applications = findall(r"network (.+)", conf, M) if applications: app_lst = [] for r in set(applications): - obj = {"address": r.strip("'")} + obj = {'address': r.strip("'")} app_lst.append(obj) - a_lst = sorted(app_lst, key=lambda i: i["address"]) + a_lst = sorted(app_lst, key=lambda i: i['address']) return a_lst def parse_vlink(self, conf): """ - This function triggers the parsing of 'vitual_link' attributes + This function triggers the parsing of 'virtual_link' attributes :param conf: configuration data :return: generated rule configuration dictionary """ + rule = self.parse_attrib(conf, 'vlink') - r_sub = {"authentication": self.parse_authentication(conf, "authentication")} + r_sub = {'authentication': self.parse_authentication(conf, + 'authentication')} rule.update(r_sub) return rule @@ -290,19 +290,49 @@ class Ospfv2Facts(object): :param attrib: 'authentication' :return: generated config dictionary """ - cfg_dict = self.parse_attr(conf, ["plaintext_password"], match=attrib) - cfg_dict["md5"] = self.parse_md5(conf, "md5") + + cfg_dict = self.parse_attr(conf, ['plaintext_password'], + match=attrib) + cfg_dict['md5'] = self.parse_attrib_list(conf, 'key-id', 'key_id') return cfg_dict - def parse_md5(self, conf, attrib=None): + def parse_attrib_list(self, conf, attrib, param): """ - This function triggers the parsing of 'md5' attributes - :param conf: configuration - :param attrib: 'md5' - :return: generated config dictionary + This function forms the regex to fetch the listed attributes + from config + :param conf: configuration data + :param attrib: attribute name + :param param: parameter data + :return: generated rule list configuration """ - cfg_dict = self.parse_attr(conf, ["key_id"], match=attrib) - return cfg_dict + + r_lst = [] + if attrib == 'area': + items = findall(r"^" + attrib.replace('_', '-') + + " (?:\'*)(\\S+)(?:\'*)", conf, M) + elif attrib == 'key-id': + items = findall(r"^.*" + attrib.replace('_', '-') + + " (?:\'*)(\\S+)(?:\'*)", conf, M) + else: + items = findall(r"" + attrib + " (?:\'*)(\\S+)(?:\'*)", conf, M) + if items: + a_lst = [] + for item in set(items): + i_regex = r" %s .+$" % item + cfg = '\n'.join(findall(i_regex, conf, M)) + if attrib == 'area': + obj = self.parse_area(cfg, item) + elif attrib == 'virtual-link': + obj = self.parse_vlink(cfg) + elif attrib == 'key-id': + obj = self.parse_key(cfg, item) + else: + obj = self.parse_attrib(cfg, attrib) + obj[param] = item.strip("'") + if obj: + a_lst.append(obj) + r_lst = sorted(a_lst, key=lambda i: i[param]) + return r_lst def parse_attrib(self, conf, param, match=None): """ @@ -310,21 +340,28 @@ class Ospfv2Facts(object): :param conf: configuration data :return: generated configuration dictionary """ + param_lst = { - 'stub': ["default_cost", "no_summary"], - 'area': ["shortcut", "authentication"], - 'mpls_te': ["enabled", "router_address"], - 'neighbor': ["priority", "poll_interval"], - 'ospf': ["external", "inter_area", "intra_area"], - 'nssa': ["translate", "default_cost", "no_summary"], - 'redistribute': ["metric", "metric_type", "route_map"], - 'spf': ["delay", "max_holdtime", "initial_holdtime"], - 'range': ["cost", "substitute", "not_advertise"], - 'originate': ["always", "metric", "metric_type", "route_map"], - 'router_lsa': ["administrative", "on_shutdown", "on_startup"], - 'config_routes': ["default_metric", "log_adjacency_changes"], - 'parameters': ["abr_type", "opaque_lsa", "router_id", "rfc1583_compatibility"], - 'vlink': ["dead_interval", "hello_interval", "transmit_delay", "retransmit_interval"] + 'key_id': ['md5_key'], + 'mpls_te': ['enabled', 'router_address'], + 'area_id': ['shortcut', 'authentication'], + 'neighbor': ['priority', 'poll_interval'], + 'stub': ['set', 'default_cost', 'no_summary'], + 'range': ['cost', 'substitute', 'not_advertise'], + 'ospf': ['external', 'inter_area', 'intra_area'], + 'spf': ['delay', 'max_holdtime', 'initial_holdtime'], + 'redistribute': ['metric', 'metric_type', 'route_map'], + 'nssa': ['set', 'translate', 'default_cost', 'no_summary'], + 'config_routes': ['default_metric', 'log_adjacency_changes' + ], + 'originate': ['always', 'metric', 'metric_type', 'route_map' + ], + 'router_lsa': ['administrative', 'on_shutdown', 'on_startup' + ], + 'parameters': ['abr_type', 'opaque_lsa', 'router_id', + 'rfc1583_compatibility'], + 'vlink': ['dead_interval', 'hello_interval', + 'transmit_delay', 'retransmit_interval'], } cfg_dict = self.parse_attr(conf, param_lst[param], match) return cfg_dict @@ -339,16 +376,21 @@ class Ospfv2Facts(object): :param match: parent node/attribute name. :return: generated config dictionary. """ + config = {} for attrib in attr_list: regex = self.map_regex(attrib) + if match: - regex = match.replace("_", "-") + " " + regex + regex = match.replace('_', '-') + ' ' + regex if conf: if self.is_bool(attrib): - out = conf.find(attrib.replace("_", "-")) - dis = conf.find(attrib.replace("_", "-") + " 'disable'") + out = conf.find(attrib.replace('_', '-')) + dis = conf.find(attrib.replace('_', '-') + + " 'disable'") if match: + if attrib == 'set' and conf.find(match) >= 1: + config[attrib] = True en = conf.find(match + " 'enable'") if out >= 1: if dis >= 1: @@ -358,7 +400,7 @@ class Ospfv2Facts(object): elif match and en >= 1: config[attrib] = True else: - out = search(r"^.*" + regex + " (.+)", conf, M) + out = search(r"^.*" + regex + ' (.+)', conf, M) if out: val = out.group(1).strip("'") if self.is_num(attrib): @@ -373,7 +415,10 @@ class Ospfv2Facts(object): :param attrib: attribute :return: regex string """ - return 'disable' if attrib == "disabled" else 'enable' if attrib == "enabled" else attrib.replace("_","-") + + return ('disable' if attrib == 'disabled' else ('enable' + if attrib == 'enabled' else ('area' if attrib + == 'area_id' else attrib.replace('_', '-')))) def is_bool(self, attrib): """ @@ -381,7 +426,17 @@ class Ospfv2Facts(object): :param attrib: attribute. :return: True/False """ - bool_set = ("always", "normal", "enabled", "opaque_lsa", "not_advertise", "administrative", "rfc1583_compatibility") + + bool_set = ( + 'set', + 'always', + 'normal', + 'enabled', + 'opaque_lsa', + 'not_advertise', + 'administrative', + 'rfc1583_compatibility', + ) return True if attrib in bool_set else False def is_num(self, attrib): @@ -390,6 +445,20 @@ class Ospfv2Facts(object): :param attrib: attribute. :return: True/false. """ - num_set = ("ospf", "delay", "metric", "inter_area", "intra_area", "on_startup", "metric_type", "on_shutdown", - "max_holdtime", "default_metric", "initial_holdtime") + + num_set = ( + 'ospf', + 'delay', + 'metric', + 'inter_area', + 'intra_area', + 'on_startup', + 'metric_type', + 'on_shutdown', + 'max_holdtime', + 'poll_interval', + 'default_metric', + 'initial_holdtime', + 'key_id', + ) return True if attrib in num_set else False diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py index c7dc575a..c539bebb 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -7,7 +7,6 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -import q from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.compat import ( ipaddress, diff --git a/plugins/modules/vyos_ospfv2.py b/plugins/modules/vyos_ospfv2.py index 73528eb5..866ed762 100644 --- a/plugins/modules/vyos_ospfv2.py +++ b/plugins/modules/vyos_ospfv2.py @@ -39,21 +39,24 @@ DOCUMENTATION = """ --- module: vyos_ospfv2 version_added: 2.10 -short_description: Manages attributes of OSPF IPv4 routes on VyOS network devices. -description: This module manages attributes of OSPF IPv4 routes on VyOS network devices. -author: Rohit Thakur (@rohitthakur2590) +short_description: This resource module configures and manages attributes of OSPFv2 routes on VyOS network devices. +description: This resource module configures and manages attributes of OSPFv2 routes on VyOS network devices. +notes: + - Tested against VyOS 1.1.8 (helium). + - This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). +author: + - Rohit Thakur (@rohitthakur2590) options: config: - description: A provided OSPF route configuration. - type: list - elements: dict + description: A provided OSPFv2 route configuration. + type: dict suboptions: - ospf_area: - description: OSPF area. + areas: + description: OSPFv2 area. type: list elements: dict suboptions: - area: + area_id: description: Configured to discard packets. type: str area_type: @@ -61,12 +64,15 @@ options: type: dict suboptions: normal: - description: Normal OSPF area. + description: Normal OSPFv2 area. type: bool nssa: - description: Nssa OSPF area. + description: Nssa OSPFv2 area. type: dict suboptions: + set: + description: Enabling nssa. + type: bool default_cost: description: Summary-default cost of nssa area. type: int @@ -78,9 +84,12 @@ options: type: str choices: ['always', 'candidate', 'never'] stub: - description: Stub OSPF area. + description: Stub OSPFv2 area. type: dict suboptions: + set: + description: Enabling stub. + type: bool default_cost: description: Summary-default cost of stub area. type: int @@ -88,17 +97,17 @@ options: description: Do not inject inter-area routes into stub. type: bool authentication: - description: OSPF area authentication type. + description: OSPFv2 area authentication type. type: str choices: ['plaintext-password', 'md5'] network: - description: OSPF network. + description: OSPFv2 network. type: list elements: dict suboptions: address: required: True - description: OSPF IPv4 network address. + description: OSPFv2 IPv4 network address. type: str range: description: Summarize routes matching prefix (border routers only). @@ -130,12 +139,13 @@ options: description: virtual link address. type: str authentication: - description: OSPF area authentication type. + description: OSPFv2 area authentication type. type: dict suboptions: md5: description: MD5 key id based authentication. - type: dict + type: list + elements: dict suboptions: key_id: description: MD5 key id. @@ -163,7 +173,7 @@ options: type: str choices: ['detail'] max_metric: - description: OSPF maximum/infinite-distance metric. + description: OSPFv2 maximum/infinite-distance metric. type: dict suboptions: router_lsa: @@ -180,7 +190,7 @@ options: description: Time to advertise self as stub-router type: int auto_cost: - description: Calculate OSPF interface cost according to bandwidth. + description: Calculate OSPFv2 interface cost according to bandwidth. type: dict suboptions: reference_bandwidth: @@ -198,10 +208,10 @@ options: description: Always advertise default route. type: bool metric: - description: OSPF default metric. + description: OSPFv2 default metric. type: int metric_type: - description: OSPF Metric types for default routes. + description: OSPFv2 Metric types for default routes. type: int route_map: description: Route map references. @@ -214,10 +224,10 @@ options: type: dict suboptions: global: - description: Global OSPF administrative distance. + description: Global OSPFv2 administrative distance. type: int ospf: - description: OSPF administrative distance. + description: OSPFv2 administrative distance. type: dict suboptions: external: @@ -255,11 +265,11 @@ options: description: Neighbor priority. type: int parameters: - descriptions: OSPF specific parameters. + descriptions: OSPFv2 specific parameters. type: dict suboptions: abr_type: - description: OSPF ABR Type. + description: OSPFv2 ABR Type. type: str choices: ['cisco', 'ibm', 'shortcut', 'standard'] opaque_lsa: @@ -290,7 +300,7 @@ options: description: Metric for redistribution routes. type: int metric_type: - description: OSPF Metric types. + description: OSPFv2 Metric types. type: int route_map: description: Route map references. @@ -315,7 +325,7 @@ options: type: dict suboptions: spf: - description: OSPF SPF timers. + description: OSPFv2 SPF timers. type: dict suboptions: delay: @@ -327,6 +337,16 @@ options: max_holdtime: description: maximum hold time (sec). type: int + running_config: + description: + - The module, by default, will connect to the remote device and retrieve the current + running-config to use as a base for comparing against the contents of source. + There are times when it is not desirable to have the task get the current running-config + for every task in a playbook. The I(running_config) argument allows the implementer + to pass in the configuration to use as the base config for comparison. This + value of this option should be the output received from device by executing + command C(show configuration commands | grep 'ospf') + type: str state: description: - The state the configuration should be left in. @@ -346,35 +366,66 @@ EXAMPLES = """ # Before state: # ------------- # -# vyos@192# run show configuration commands | grep ospf +# vyos@vyos# run show configuration commands | grep ospf +# # -- name: Merge the provided configuration with the existing running configuration - vyos_ospf_routes: +- name: Merge the provided configuration with the exisiting running configuration + vyos.vyos.vyos_ospfv2: config: - - afi: 'ipv4' - ospf_area: - - area: 0 - network: 192.168.0.0/24 - default_information: - originate: - always: true - metric: 2 - metric_type: 10 - log_adjacency_changes: "details" - parameters: - router_id: 10.1.1.1 - redistribute: - - route_type: 'connected' - metric_type: 2 - route_map: 'CONNECT' - - afi: 'ipv6' - ospf_area: - - area: 0.0.0.0 - range: 2001:db8:1::/64 - parameters: - router-id 192.168.1.1 - redistribute: - - route_type: 'connected' + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + mpls_te: + enabled: true + router_address: '192.0.11.11' + auto_cost: + reference_bandwidth: 2 + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + redistribute: + - route_type: 'bgp' + metric: 10 + metric_type: 2 + passive_interface: + - 'eth1' + - 'eth2' + parameters: + router_id: '192.0.1.1' + opaque_lsa: true + rfc1583_compatibility: true + abr_type: 'cisco' + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + area_type: + stub: + default_cost: 20 + network: + - address: '192.0.2.0/24' + range: + - address: '192.0.3.0/24' + cost: 10 + - address: '192.0.4.0/24' + cost: 12 state: merged # # @@ -385,276 +436,432 @@ EXAMPLES = """ # before": [] # # "commands": [ -# "set interfaces ethernet eth1 firewall in name 'INBOUND'", -# "set protocols ospf area 0 network 192.168.0.0/24", +# "set protocols ospf mpls-te enable", +# "set protocols ospf mpls-te router-address '192.0.11.11'", +# "set protocols ospf redistribute bgp", +# "set protocols ospf redistribute bgp metric-type 2", +# "set protocols ospf redistribute bgp metric 10", +# "set protocols ospf default-information originate metric-type 2", # "set protocols ospf default-information originate always", # "set protocols ospf default-information originate metric 10", -# "set protocols ospf default-information originate metric-type 2", -# "set protocols ospf log-adjacency-changes", -# "set protocols ospf parameters router-id 10.1.1.1", -# "set protocols ospf redistribute connected metric-type 2", -# "set protocols ospf redistribute connected route-map CONNECT", -# "set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64, -# "set protocols ospfv3 parameters router-id 192.168.1.1, -# "set protocols ospfv3 redistribute connected +# "set protocols ospf default-information originate route-map ingress", +# "set protocols ospf auto-cost reference-bandwidth '2'", +# "set protocols ospf parameters router-id '192.0.1.1'", +# "set protocols ospf parameters opaque-lsa", +# "set protocols ospf parameters abr-type 'cisco'", +# "set protocols ospf parameters rfc1583-compatibility", +# "set protocols ospf passive-interface eth1", +# "set protocols ospf passive-interface eth2", +# "set protocols ospf max-metric router-lsa on-shutdown 10", +# "set protocols ospf max-metric router-lsa administrative", +# "set protocols ospf max-metric router-lsa on-startup 10", +# "set protocols ospf log-adjacency-changes 'detail'", +# "set protocols ospf neighbor 192.0.11.12 priority 2", +# "set protocols ospf neighbor 192.0.11.12 poll-interval 10", +# "set protocols ospf neighbor 192.0.11.12", +# "set protocols ospf area '2'", +# "set protocols ospf area 2 authentication plaintext-password", +# "set protocols ospf area 2 shortcut enable", +# "set protocols ospf area 2 area-type normal", +# "set protocols ospf area '3'", +# "set protocols ospf area 3 area-type nssa", +# "set protocols ospf area 4 range 192.0.3.0/24 cost 10", +# "set protocols ospf area 4 range 192.0.3.0/24", +# "set protocols ospf area 4 range 192.0.4.0/24 cost 12", +# "set protocols ospf area 4 range 192.0.4.0/24", +# "set protocols ospf area 4 area-type stub default-cost 20", +# "set protocols ospf area '4'", +# "set protocols ospf area 4 network 192.0.2.0/24" # ] # -# "after": [ -# { -# { -# "afi": "ipv4", -# "ospf_area":[ -# { -# "area": "0", -# "network": "192.168.0.0/24" -# } -# ], -# "default_information": -# { -# "originate": -# { -# always: true, -# metric: 2, -# metric_type: 10 -# } -# }, -# "log_adjacency_changes": "details" -# "parameters": -# { -# "router_id": "10.1.1.1" -# }, -# "redistribute":[ -# { -# "route_type": "connetced", -# "metric_type": 2 -# "route_map": "CONNECT" -# } -# ] -# }, -# { -# "afi": "ipv6", -# "ospf_area":[ -# { -# "area": "0.0.0.0", -# } -# ], -# "range": "2001:db8:1::/64", -# "parameters": -# { -# "router_id": "192.168.1.1" -# }, -# "redistribute": -# [ -# { -# "route_type": "connetced", -# } -# ] +# "after": { +# "areas": [ +# { +# "area_id": "2", +# "area_type": { +# "normal": true +# }, +# "authentication": "plaintext-password", +# "shortcut": "enable" +# }, +# { +# "area_id": "3", +# "area_type": { +# "nssa": { +# "set": true +# } # } -# ] +# }, +# { +# "area_id": "4", +# "area_type": { +# "stub": { +# "default_cost": 20, +# "set": true +# } +# }, +# "network": [ +# { +# "address": "192.0.2.0/24" +# } +# ], +# "range": [ +# { +# "address": "192.0.3.0/24", +# "cost": 10 +# }, +# { +# "address": "192.0.4.0/24", +# "cost": 12 +# } +# ] +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 2 +# }, +# "default_information": { +# "originate": { +# "always": true, +# "metric": 10, +# "metric_type": 2, +# "route_map": "ingress" +# } +# }, +# "log_adjacency_changes": "detail", +# "max_metric": { +# "router_lsa": { +# "administrative": true, +# "on_shutdown": 10, +# "on_startup": 10 +# } +# }, +# "mpls_te": { +# "enabled": true, +# "router_address": "192.0.11.11" +# }, +# "neighbor": [ +# { +# "neighbor_id": "192.0.11.12", +# "poll_interval": 10, +# "priority": 2 +# } +# ], +# "parameters": { +# "abr_type": "cisco", +# "opaque_lsa": true, +# "rfc1583_compatibility": true, +# "router_id": "192.0.1.1" +# }, +# "passive_interface": [ +# "eth2", +# "eth1" +# ], +# "redistribute": [ +# { +# "metric": 10, +# "metric_type": 2, +# "route_type": "bgp" +# } +# ] +# } # # After state: # ------------- # -# vyos@vyos:~$ show configuration commands| grep firewall -# set protocols ospf area 0 network 192.168.0.0/24 -# set protocols ospf default-information originate always -# set protocols ospf default-information originate metric 10 -# set protocols ospf default-information originate metric-type 2 -# set protocols ospf log-adjacency-changes details -# set protocols ospf parameters router-id 10.1.1.1 -# set protocols ospf redistribute connected metric-type 2 -# set protocols ospf redistribute connected route-map CONNECT -# set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64 -# set protocols ospfv3 parameters router-id 192.168.1.1 -# set protocols ospfv3 redistribute connected +# vyos@192# run show configuration commands | grep ospf +# set protocols ospf area 2 area-type 'normal' +# set protocols ospf area 2 authentication 'plaintext-password' +# set protocols ospf area 2 shortcut 'enable' +# set protocols ospf area 3 area-type 'nssa' +# set protocols ospf area 4 area-type stub default-cost '20' +# set protocols ospf area 4 network '192.0.2.0/24' +# set protocols ospf area 4 range 192.0.3.0/24 cost '10' +# set protocols ospf area 4 range 192.0.4.0/24 cost '12' +# set protocols ospf auto-cost reference-bandwidth '2' +# set protocols ospf default-information originate 'always' +# set protocols ospf default-information originate metric '10' +# set protocols ospf default-information originate metric-type '2' +# set protocols ospf default-information originate route-map 'ingress' +# set protocols ospf log-adjacency-changes 'detail' +# set protocols ospf max-metric router-lsa 'administrative' +# set protocols ospf max-metric router-lsa on-shutdown '10' +# set protocols ospf max-metric router-lsa on-startup '10' +# set protocols ospf mpls-te 'enable' +# set protocols ospf mpls-te router-address '192.0.11.11' +# set protocols ospf neighbor 192.0.11.12 poll-interval '10' +# set protocols ospf neighbor 192.0.11.12 priority '2' +# set protocols ospf parameters abr-type 'cisco' +# set protocols ospf parameters 'opaque-lsa' +# set protocols ospf parameters 'rfc1583-compatibility' +# set protocols ospf parameters router-id '192.0.1.1' +# set protocols ospf passive-interface 'eth1' +# set protocols ospf passive-interface 'eth2' +# set protocols ospf redistribute bgp metric '10' +# set protocols ospf redistribute bgp metric-type '2' -# Using replaced +# Using merged # # Before state: # ------------- # -# vyos@192# run show configuration commands | grep ospf -# set protocols ospf area 0 network 192.168.0.0/24 -# set protocols ospf default-information originate always -# set protocols ospf default-information originate metric 10 -# set protocols ospf default-information originate metric-type 2 -# set protocols ospf log-adjacency-changes details -# set protocols ospf parameters router-id 10.1.1.1 -# set protocols ospf redistribute connected metric-type 2 -# set protocols ospf redistribute connected route-map CONNECT -# set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64 -# set protocols ospfv3 parameters router-id 192.168.1.1 -# set protocols ospfv3 redistribute connected -# -- name: Replace the provided configuration with the existing running configuration - vyos_ospf_routes: +# vyos@vyos# run show configuration commands | grep ospf +# +# +- name: Merge the provided configuration to update exisiting running configuration + vyos.vyos.vyos_ospfv2: config: - - afi: 'ipv4' - ospf_area: - - area: 0 - network: 192.168.0.0/24 - area_type: - normal: True - default_information: - originate: - always: true - metric: 2 - metric_type: 10 - log_adjacency_changes: "details" - parameters: - router_id: 10.1.1.1 - redistribute: - - route_type: 'static' - metric_type: 2 - route_map: 'STATIC' - - afi: 'ipv6' - ospf_area: - - area: 0.0.0.0 - range: 2001:db8:1::/64 - parameters: - router-id 192.168.1.1 - redistribute: - - route_type: 'connected' - state: replaced + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: false + - area_id: '4' + area_type: + stub: + default_cost: 20 + network: + - address: '192.0.2.0/24' + - address: '192.0.22.0/24' + - address: '192.0.32.0/24' + state: merged # # # ------------------------- # Module Execution Result # ------------------------- # -# before": [ -# { -# { -# "afi": "ipv4", -# "ospf_area":[ -# { -# "area": "0", -# "network": "192.168.0.0/24" -# } -# ], -# "default_information": -# { -# "originate": -# { -# always: true, -# metric: 2, -# metric_type: 10 -# } -# }, -# "log_adjacency_changes": "details" -# "parameters": -# { -# "router_id": "10.1.1.1" -# }, -# "redistribute":[ -# { -# "route_type": "connetced", -# "metric_type": 2 -# "route_map": "CONNECT" -# } -# ] -# }, -# { -# "afi": "ipv6", -# "ospf_area":[ -# { -# "area": "0.0.0.0", -# } -# ], -# "range": "2001:db8:1::/64", -# "parameters": -# { -# "router_id": "192.168.1.1" -# }, -# "redistribute": -# [ -# { -# "route_type": "connetced", -# } -# ] +# "before": { +# "areas": [ +# { +# "area_id": "2", +# "area_type": { +# "normal": true +# }, +# "authentication": "plaintext-password", +# "shortcut": "enable" +# }, +# { +# "area_id": "3", +# "area_type": { +# "nssa": { +# "set": true +# } # } -# ] +# }, +# { +# "area_id": "4", +# "area_type": { +# "stub": { +# "default_cost": 20, +# "set": true +# } +# }, +# "network": [ +# { +# "address": "192.0.2.0/24" +# } +# ], +# "range": [ +# { +# "address": "192.0.3.0/24", +# "cost": 10 +# }, +# { +# "address": "192.0.4.0/24", +# "cost": 12 +# } +# ] +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 2 +# }, +# "default_information": { +# "originate": { +# "always": true, +# "metric": 10, +# "metric_type": 2, +# "route_map": "ingress" +# } +# }, +# "log_adjacency_changes": "detail", +# "max_metric": { +# "router_lsa": { +# "administrative": true, +# "on_shutdown": 10, +# "on_startup": 10 +# } +# }, +# "mpls_te": { +# "enabled": true, +# "router_address": "192.0.11.11" +# }, +# "neighbor": [ +# { +# "neighbor_id": "192.0.11.12", +# "poll_interval": 10, +# "priority": 2 +# } +# ], +# "parameters": { +# "abr_type": "cisco", +# "opaque_lsa": true, +# "rfc1583_compatibility": true, +# "router_id": "192.0.1.1" +# }, +# "passive_interface": [ +# "eth2", +# "eth1" +# ], +# "redistribute": [ +# { +# "metric": 10, +# "metric_type": 2, +# "route_type": "bgp" +# } +# ] +# } # # "commands": [ -# "delete protocols ospf redistribute connected", -# "set protocols ospf area 0 area_type normal", -# "set protocols ospf redistribute static metric-type 2", -# "set protocols ospf redistribute static route-map CONNECT" +# "delete protocols ospf area 4 area-type stub", +# "set protocols ospf area 4 network 192.0.22.0/24" +# "set protocols ospf area 4 network 192.0.32.0/24" # ] # -# "after": [ -# { -# { -# "afi": "ipv4", -# "ospf_area":[ -# { -# "area": "0", -# "area_type": -# { -# normal: true -# } -# "network": "192.168.0.0/24" -# } -# ], -# "default_information": -# { -# "originate": -# { -# always: true, -# metric: 2, -# metric_type: 10 -# } -# }, -# "log_adjacency_changes": "details" -# "parameters": -# { -# "router_id": "10.1.1.1" -# }, -# "redistribute":[ -# { -# "route_type": "static", -# "metric_type": 2 -# "route_map": "STATIC" -# } -# ] -# }, -# { -# "afi": "ipv6", -# "ospf_area":[ -# { -# "area": "0.0.0.0", -# } -# ], -# "range": "2001:db8:1::/64", -# "parameters": -# { -# "router_id": "192.168.1.1" -# }, -# "redistribute": -# [ -# { -# "route_type": "connected", -# } -# ] +# "after": { +# "areas": [ +# { +# "area_id": "2", +# "area_type": { +# "normal": true +# }, +# "authentication": "plaintext-password", +# "shortcut": "enable" +# }, +# { +# "area_id": "3", +# "area_type": { +# "nssa": { +# "set": true +# } # } -# ] +# }, +# { +# "area_id": "4", +# }, +# "network": [ +# { +# "address": "192.0.2.0/24" +# }, +# { +# "address": "192.0.22.0/24" +# }, +# { +# "address": "192.0.32.0/24" +# } +# ], +# "range": [ +# { +# "address": "192.0.3.0/24", +# "cost": 10 +# }, +# { +# "address": "192.0.4.0/24", +# "cost": 12 +# } +# ] +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 2 +# }, +# "default_information": { +# "originate": { +# "always": true, +# "metric": 10, +# "metric_type": 2, +# "route_map": "ingress" +# } +# }, +# "log_adjacency_changes": "detail", +# "max_metric": { +# "router_lsa": { +# "administrative": true, +# "on_shutdown": 10, +# "on_startup": 10 +# } +# }, +# "mpls_te": { +# "enabled": true, +# "router_address": "192.0.11.11" +# }, +# "neighbor": [ +# { +# "neighbor_id": "192.0.11.12", +# "poll_interval": 10, +# "priority": 2 +# } +# ], +# "parameters": { +# "abr_type": "cisco", +# "opaque_lsa": true, +# "rfc1583_compatibility": true, +# "router_id": "192.0.1.1" +# }, +# "passive_interface": [ +# "eth2", +# "eth1" +# ], +# "redistribute": [ +# { +# "metric": 10, +# "metric_type": 2, +# "route_type": "bgp" +# } +# ] +# } # # After state: # ------------- # -# vyos@vyos:~$ show configuration commands| grep firewall -# set protocols ospf area 0 network 192.168.0.0/24 -# set protocols ospf default-information originate always -# set protocols ospf default-information originate metric 10 -# set protocols ospf default-information originate metric-type 2 -# set protocols ospf log-adjacency-changes details -# set protocols ospf parameters router-id 10.1.1.1 -# set protocols ospf redistribute static metric-type 2 -# set protocols ospf redistribute static route-map CONNECT -# set protocols ospfv3 area 0.0.0.0 range 2001:db8:2::/64 -# set protocols ospfv3 parameters router-id 192.168.2.1 -# set protocols ospfv3 redistribute connected +# vyos@192# run show configuration commands | grep ospf +# set protocols ospf area 2 area-type 'normal' +# set protocols ospf area 2 authentication 'plaintext-password' +# set protocols ospf area 2 shortcut 'enable' +# set protocols ospf area 3 area-type 'nssa' +# set protocols ospf area 4 network '192.0.2.0/24' +# set protocols ospf area 4 network '192.0.22.0/24' +# set protocols ospf area 4 network '192.0.32.0/24' +# set protocols ospf area 4 range 192.0.3.0/24 cost '10' +# set protocols ospf area 4 range 192.0.4.0/24 cost '12' +# set protocols ospf auto-cost reference-bandwidth '2' +# set protocols ospf default-information originate 'always' +# set protocols ospf default-information originate metric '10' +# set protocols ospf default-information originate metric-type '2' +# set protocols ospf default-information originate route-map 'ingress' +# set protocols ospf log-adjacency-changes 'detail' +# set protocols ospf max-metric router-lsa 'administrative' +# set protocols ospf max-metric router-lsa on-shutdown '10' +# set protocols ospf max-metric router-lsa on-startup '10' +# set protocols ospf mpls-te 'enable' +# set protocols ospf mpls-te router-address '192.0.11.11' +# set protocols ospf neighbor 192.0.11.12 poll-interval '10' +# set protocols ospf neighbor 192.0.11.12 priority '2' +# set protocols ospf parameters abr-type 'cisco' +# set protocols ospf parameters 'opaque-lsa' +# set protocols ospf parameters 'rfc1583-compatibility' +# set protocols ospf parameters router-id '192.0.1.1' +# set protocols ospf passive-interface 'eth1' +# set protocols ospf passive-interface 'eth2' +# set protocols ospf redistribute bgp metric '10' +# set protocols ospf redistribute bgp metric-type '2' # Using replaced @@ -663,47 +870,89 @@ EXAMPLES = """ # ------------- # # vyos@192# run show configuration commands | grep ospf -# set protocols ospf area 0 network 192.168.0.0/24 -# set protocols ospf default-information originate always -# set protocols ospf default-information originate metric 10 -# set protocols ospf default-information originate metric-type 2 -# set protocols ospf log-adjacency-changes details -# set protocols ospf parameters router-id 10.1.1.1 -# set protocols ospf redistribute connected metric-type 2 -# set protocols ospf redistribute connected route-map CONNECT -# set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64 -# set protocols ospfv3 parameters router-id 192.168.1.1 -# set protocols ospfv3 redistribute connected -# -- name: Replace the provided configuration with the existing running configuration - vyos_ospf_routes: +# set protocols ospf area 2 area-type 'normal' +# set protocols ospf area 2 authentication 'plaintext-password' +# set protocols ospf area 2 shortcut 'enable' +# set protocols ospf area 3 area-type 'nssa' +# set protocols ospf area 4 area-type stub default-cost '20' +# set protocols ospf area 4 network '192.0.2.0/24' +# set protocols ospf area 4 range 192.0.3.0/24 cost '10' +# set protocols ospf area 4 range 192.0.4.0/24 cost '12' +# set protocols ospf auto-cost reference-bandwidth '2' +# set protocols ospf default-information originate 'always' +# set protocols ospf default-information originate metric '10' +# set protocols ospf default-information originate metric-type '2' +# set protocols ospf default-information originate route-map 'ingress' +# set protocols ospf log-adjacency-changes 'detail' +# set protocols ospf max-metric router-lsa 'administrative' +# set protocols ospf max-metric router-lsa on-shutdown '10' +# set protocols ospf max-metric router-lsa on-startup '10' +# set protocols ospf mpls-te 'enable' +# set protocols ospf mpls-te router-address '192.0.11.11' +# set protocols ospf neighbor 192.0.11.12 poll-interval '10' +# set protocols ospf neighbor 192.0.11.12 priority '2' +# set protocols ospf parameters abr-type 'cisco' +# set protocols ospf parameters 'opaque-lsa' +# set protocols ospf parameters 'rfc1583-compatibility' +# set protocols ospf parameters router-id '192.0.1.1' +# set protocols ospf passive-interface 'eth1' +# set protocols ospf passive-interface 'eth2' +# set protocols ospf redistribute bgp metric '10' +# set protocols ospf redistribute bgp metric-type '2' +# +- name: Replace ospfv2 routes attributes configuration. + vyos.vyos.vyos_ospfv2: config: - - afi: 'ipv4' - ospf_area: - - area: 0 - network: 192.168.0.0/24 - area_type: - normal: True - default_information: - originate: - always: true - metric: 2 - metric_type: 10 - log_adjacency_changes: "details" - parameters: - router_id: 10.1.1.1 - redistribute: - - route_type: 'static' - metric_type: 2 - route_map: 'STATIC' - - afi: 'ipv6' - ospf_area: - - area: 0.0.0.0 - range: 2001:db8:1::/64 - parameters: - router-id 192.168.1.1 - redistribute: - - route_type: 'connected' + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + mpls_te: + enabled: true + router_address: '192.0.22.22' + auto_cost: + reference_bandwidth: 2 + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + redistribute: + - route_type: 'bgp' + metric: 10 + metric_type: 2 + passive_interface: + - 'eth1' + parameters: + router_id: '192.0.1.1' + opaque_lsa: true + rfc1583_compatibility: true + abr_type: 'cisco' + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '4' + area_type: + stub: + default_cost: 20 + network: + - address: '192.0.2.0/24' + - address: '192.0.12.0/24' + - address: '192.0.22.0/24' + - address: '192.0.32.0/24' + range: + - address: '192.0.42.0/24' + cost: 10 state: replaced # # @@ -711,608 +960,968 @@ EXAMPLES = """ # Module Execution Result # ------------------------- # -# before": [ -# { -# { -# "afi": "ipv4", -# "ospf_area":[ -# { -# "area": "0", -# "network": "192.168.0.0/24" -# } -# ], -# "default_information": -# { -# "originate": -# { -# always: true, -# metric: 2, -# metric_type: 10 -# } -# }, -# "log_adjacency_changes": "details" -# "parameters": -# { -# "router_id": "10.1.1.1" -# }, -# "redistribute":[ -# { -# "route_type": "connetced", -# "metric_type": 2 -# "route_map": "CONNECT" -# } -# ] -# }, -# { -# "afi": "ipv6", -# "ospf_area":[ -# { -# "area": "0.0.0.0", -# } -# ], -# "range": "2001:db8:1::/64", -# "parameters": -# { -# "router_id": "192.168.1.1" -# }, -# "redistribute": -# [ -# { -# "route_type": "connetced", -# } -# ] -# } -# ] -# -# "commands": [ -# "delete protocols ospf redistribute connected", -# "set protocols ospf area 0 area_type normal", -# "set protocols ospf redistribute static metric-type 2", -# "set protocols ospf redistribute static route-map CONNECT" -# ] -# -# "after": [ -# { -# { -# "afi": "ipv4", -# "ospf_area":[ -# { -# "area": "0", -# "area_type": -# { -# normal: true -# } -# "network": "192.168.0.0/24" -# } -# ], -# "default_information": -# { -# "originate": -# { -# always: true, -# metric: 2, -# metric_type: 10 -# } -# }, -# "log_adjacency_changes": "details" -# "parameters": -# { -# "router_id": "10.1.1.1" -# }, -# "redistribute":[ -# { -# "route_type": "static", -# "metric_type": 2 -# "route_map": "STATIC" -# } -# ] -# }, -# { -# "afi": "ipv6", -# "ospf_area":[ -# { -# "area": "0.0.0.0", -# } -# ], -# "range": "2001:db8:1::/64", -# "parameters": -# { -# "router_id": "192.168.1.1" -# }, -# "redistribute": -# [ -# { -# "route_type": "connected", -# } -# ] +# "before": { +# "areas": [ +# { +# "area_id": "2", +# "area_type": { +# "normal": true +# }, +# "authentication": "plaintext-password", +# "shortcut": "enable" +# }, +# { +# "area_id": "3", +# "area_type": { +# "nssa": { +# "set": true +# } # } +# }, +# { +# "area_id": "4", +# "area_type": { +# "stub": { +# "default_cost": 20, +# "set": true +# } +# }, +# "network": [ +# { +# "address": "192.0.2.0/24" +# } +# ], +# "range": [ +# { +# "address": "192.0.3.0/24", +# "cost": 10 +# }, +# { +# "address": "192.0.4.0/24", +# "cost": 12 +# } +# ] +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 2 +# }, +# "default_information": { +# "originate": { +# "always": true, +# "metric": 10, +# "metric_type": 2, +# "route_map": "ingress" +# } +# }, +# "log_adjacency_changes": "detail", +# "max_metric": { +# "router_lsa": { +# "administrative": true, +# "on_shutdown": 10, +# "on_startup": 10 +# } +# }, +# "mpls_te": { +# "enabled": true, +# "router_address": "192.0.11.11" +# }, +# "neighbor": [ +# { +# "neighbor_id": "192.0.11.12", +# "poll_interval": 10, +# "priority": 2 +# } +# ], +# "parameters": { +# "abr_type": "cisco", +# "opaque_lsa": true, +# "rfc1583_compatibility": true, +# "router_id": "192.0.1.1" +# }, +# "passive_interface": [ +# "eth2", +# "eth1" +# ], +# "redistribute": [ +# { +# "metric": 10, +# "metric_type": 2, +# "route_type": "bgp" +# } +# ] +# } +# +# "commands": [ +# "delete protocols ospf passive-interface eth2", +# "delete protocols ospf area 3", +# "delete protocols ospf area 4 range 192.0.3.0/24 cost", +# "delete protocols ospf area 4 range 192.0.3.0/24", +# "delete protocols ospf area 4 range 192.0.4.0/24 cost", +# "delete protocols ospf area 4 range 192.0.4.0/24", +# "set protocols ospf mpls-te router-address '192.0.22.22'", +# "set protocols ospf area 4 range 192.0.42.0/24 cost 10", +# "set protocols ospf area 4 range 192.0.42.0/24", +# "set protocols ospf area 4 network 192.0.12.0/24", +# "set protocols ospf area 4 network 192.0.22.0/24", +# "set protocols ospf area 4 network 192.0.32.0/24" # ] # +# "after": { +# "areas": [ +# { +# "area_id": "2", +# "area_type": { +# "normal": true +# }, +# "authentication": "plaintext-password", +# "shortcut": "enable" +# }, +# { +# "area_id": "4", +# "area_type": { +# "stub": { +# "default_cost": 20, +# "set": true +# } +# }, +# "network": [ +# { +# "address": "192.0.12.0/24" +# }, +# { +# "address": "192.0.2.0/24" +# }, +# { +# "address": "192.0.22.0/24" +# }, +# { +# "address": "192.0.32.0/24" +# } +# ], +# "range": [ +# { +# "address": "192.0.42.0/24", +# "cost": 10 +# } +# ] +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 2 +# }, +# "default_information": { +# "originate": { +# "always": true, +# "metric": 10, +# "metric_type": 2, +# "route_map": "ingress" +# } +# }, +# "log_adjacency_changes": "detail", +# "max_metric": { +# "router_lsa": { +# "administrative": true, +# "on_shutdown": 10, +# "on_startup": 10 +# } +# }, +# "mpls_te": { +# "enabled": true, +# "router_address": "192.0.22.22" +# }, +# "neighbor": [ +# { +# "neighbor_id": "192.0.11.12", +# "poll_interval": 10, +# "priority": 2 +# } +# ], +# "parameters": { +# "abr_type": "cisco", +# "opaque_lsa": true, +# "rfc1583_compatibility": true, +# "router_id": "192.0.1.1" +# }, +# "passive_interface": [ +# "eth1" +# ], +# "redistribute": [ +# { +# "metric": 10, +# "metric_type": 2, +# "route_type": "bgp" +# } +# ] +# } +# # After state: # ------------- # -# vyos@vyos:~$ show configuration commands| grep firewall -# set protocols ospf area 0 network 192.168.0.0/24 -# set protocols ospf default-information originate always -# set protocols ospf default-information originate metric 10 -# set protocols ospf default-information originate metric-type 2 -# set protocols ospf log-adjacency-changes details -# set protocols ospf parameters router-id 10.1.1.1 -# set protocols ospf redistribute static metric-type 2 -# set protocols ospf redistribute static route-map CONNECT -# set protocols ospfv3 area 0.0.0.0 range 2001:db8:2::/64 -# set protocols ospfv3 parameters router-id 192.168.2.1 -# set protocols ospfv3 redistribute connected +# vyos@192# run show configuration commands | grep ospf +# set protocols ospf area 2 area-type 'normal' +# set protocols ospf area 2 authentication 'plaintext-password' +# set protocols ospf area 2 shortcut 'enable' +# set protocols ospf area 4 area-type stub default-cost '20' +# set protocols ospf area 4 network '192.0.2.0/24' +# set protocols ospf area 4 network '192.0.12.0/24' +# set protocols ospf area 4 network '192.0.22.0/24' +# set protocols ospf area 4 network '192.0.32.0/24' +# set protocols ospf area 4 range 192.0.42.0/24 cost '10' +# set protocols ospf auto-cost reference-bandwidth '2' +# set protocols ospf default-information originate 'always' +# set protocols ospf default-information originate metric '10' +# set protocols ospf default-information originate metric-type '2' +# set protocols ospf default-information originate route-map 'ingress' +# set protocols ospf log-adjacency-changes 'detail' +# set protocols ospf max-metric router-lsa 'administrative' +# set protocols ospf max-metric router-lsa on-shutdown '10' +# set protocols ospf max-metric router-lsa on-startup '10' +# set protocols ospf mpls-te 'enable' +# set protocols ospf mpls-te router-address '192.0.22.22' +# set protocols ospf neighbor 192.0.11.12 poll-interval '10' +# set protocols ospf neighbor 192.0.11.12 priority '2' +# set protocols ospf parameters abr-type 'cisco' +# set protocols ospf parameters 'opaque-lsa' +# set protocols ospf parameters 'rfc1583-compatibility' +# set protocols ospf parameters router-id '192.0.1.1' +# set protocols ospf passive-interface 'eth1' +# set protocols ospf redistribute bgp metric '10' +# set protocols ospf redistribute bgp metric-type '2' -# Using replaced +# Using rendered # -# Before state: -# ------------- # -# vyos@192# run show configuration commands | grep ospf -# set protocols ospf area 0 network 192.168.0.0/24 -# set protocols ospf default-information originate always -# set protocols ospf default-information originate metric 10 -# set protocols ospf default-information originate metric-type 2 -# set protocols ospf log-adjacency-changes details -# set protocols ospf parameters router-id 10.1.1.1 -# set protocols ospf redistribute connected metric-type 2 -# set protocols ospf redistribute connected route-map CONNECT -# set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64 -# set protocols ospfv3 parameters router-id 192.168.1.1 -# set protocols ospfv3 redistribute connected -# -- name: Replace the provided configuration with the existing running configuration - vyos_ospf_routes: +- name: Render the commands for provided configuration + vyos.vyos.vyos_ospfv2: config: - - afi: 'ipv4' - ospf_area: - - area: 0 - network: 192.168.0.0/24 - area_type: - normal: True - default_information: - originate: - always: true - metric: 2 - metric_type: 10 - log_adjacency_changes: "details" - parameters: - router_id: 10.1.1.1 - redistribute: - - route_type: 'static' - metric_type: 2 - route_map: 'STATIC' - - afi: 'ipv6' - ospf_area: - - area: 0.0.0.0 - range: 2001:db8:1::/64 - parameters: - router-id 192.168.1.1 - redistribute: - - route_type: 'connected' - state: replaced + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + mpls_te: + enabled: true + router_address: '192.0.11.11' + auto_cost: + reference_bandwidth: 2 + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + redistribute: + - route_type: 'bgp' + metric: 10 + metric_type: 2 + passive_interface: + - 'eth1' + - 'eth2' + parameters: + router_id: '192.0.1.1' + opaque_lsa: true + rfc1583_compatibility: true + abr_type: 'cisco' + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + area_type: + stub: + default_cost: 20 + network: + - address: '192.0.2.0/24' + range: + - address: '192.0.3.0/24' + cost: 10 + - address: '192.0.4.0/24' + cost: 12 + state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # -# before": [ -# { -# { -# "afi": "ipv4", -# "ospf_area":[ -# { -# "area": "0", -# "network": "192.168.0.0/24" -# } -# ], -# "default_information": -# { -# "originate": -# { -# always: true, -# metric: 2, -# metric_type: 10 -# } -# }, -# "log_adjacency_changes": "details" -# "parameters": -# { -# "router_id": "10.1.1.1" -# }, -# "redistribute":[ -# { -# "route_type": "connetced", -# "metric_type": 2 -# "route_map": "CONNECT" -# } -# ] -# }, -# { -# "afi": "ipv6", -# "ospf_area":[ -# { -# "area": "0.0.0.0", -# } -# ], -# "range": "2001:db8:1::/64", -# "parameters": -# { -# "router_id": "192.168.1.1" -# }, -# "redistribute": -# [ -# { -# "route_type": "connetced", -# } -# ] -# } -# ] # -# "commands": [ -# "delete protocols ospf redistribute connected", -# "set protocols ospf area 0 area_type normal", -# "set protocols ospf redistribute static metric-type 2", -# "set protocols ospf redistribute static route-map CONNECT" +# "rendered": [ +# [ +# "set protocols ospf mpls-te enable", +# "set protocols ospf mpls-te router-address '192.0.11.11'", +# "set protocols ospf redistribute bgp", +# "set protocols ospf redistribute bgp metric-type 2", +# "set protocols ospf redistribute bgp metric 10", +# "set protocols ospf default-information originate metric-type 2", +# "set protocols ospf default-information originate always", +# "set protocols ospf default-information originate metric 10", +# "set protocols ospf default-information originate route-map ingress", +# "set protocols ospf auto-cost reference-bandwidth '2'", +# "set protocols ospf parameters router-id '192.0.1.1'", +# "set protocols ospf parameters opaque-lsa", +# "set protocols ospf parameters abr-type 'cisco'", +# "set protocols ospf parameters rfc1583-compatibility", +# "set protocols ospf passive-interface eth1", +# "set protocols ospf passive-interface eth2", +# "set protocols ospf max-metric router-lsa on-shutdown 10", +# "set protocols ospf max-metric router-lsa administrative", +# "set protocols ospf max-metric router-lsa on-startup 10", +# "set protocols ospf log-adjacency-changes 'detail'", +# "set protocols ospf neighbor 192.0.11.12 priority 2", +# "set protocols ospf neighbor 192.0.11.12 poll-interval 10", +# "set protocols ospf neighbor 192.0.11.12", +# "set protocols ospf area '2'", +# "set protocols ospf area 2 authentication plaintext-password", +# "set protocols ospf area 2 shortcut enable", +# "set protocols ospf area 2 area-type normal", +# "set protocols ospf area '3'", +# "set protocols ospf area 3 area-type nssa", +# "set protocols ospf area 4 range 192.0.3.0/24 cost 10", +# "set protocols ospf area 4 range 192.0.3.0/24", +# "set protocols ospf area 4 range 192.0.4.0/24 cost 12", +# "set protocols ospf area 4 range 192.0.4.0/24", +# "set protocols ospf area 4 area-type stub default-cost 20", +# "set protocols ospf area '4'", +# "set protocols ospf area 4 network 192.0.2.0/24" # ] + + +# Using parsed +# +# +- name: Render the commands for provided configuration + vyos.vyos.vyos_ospfv2: + running_config: + "set protocols ospf area 2 area-type 'normal' + set protocols ospf area 2 authentication 'plaintext-password' + set protocols ospf area 2 shortcut 'enable' + set protocols ospf area 3 area-type 'nssa' + set protocols ospf area 4 area-type stub default-cost '20' + set protocols ospf area 4 network '192.0.2.0/24' + set protocols ospf area 4 range 192.0.3.0/24 cost '10' + set protocols ospf area 4 range 192.0.4.0/24 cost '12' + set protocols ospf auto-cost reference-bandwidth '2' + set protocols ospf default-information originate 'always' + set protocols ospf default-information originate metric '10' + set protocols ospf default-information originate metric-type '2' + set protocols ospf default-information originate route-map 'ingress' + set protocols ospf log-adjacency-changes 'detail' + set protocols ospf max-metric router-lsa 'administrative' + set protocols ospf max-metric router-lsa on-shutdown '10' + set protocols ospf max-metric router-lsa on-startup '10' + set protocols ospf mpls-te 'enable' + set protocols ospf mpls-te router-address '192.0.11.11' + set protocols ospf neighbor 192.0.11.12 poll-interval '10' + set protocols ospf neighbor 192.0.11.12 priority '2' + set protocols ospf parameters abr-type 'cisco' + set protocols ospf parameters 'opaque-lsa' + set protocols ospf parameters 'rfc1583-compatibility' + set protocols ospf parameters router-id '192.0.1.1' + set protocols ospf passive-interface 'eth1' + set protocols ospf passive-interface 'eth2' + set protocols ospf redistribute bgp metric '10' + set protocols ospf redistribute bgp metric-type '2'" + state: parsed # -# "after": [ -# { -# { -# "afi": "ipv4", -# "ospf_area":[ -# { -# "area": "0", -# "area_type": -# { -# normal: true -# } -# "network": "192.168.0.0/24" -# } -# ], -# "default_information": -# { -# "originate": -# { -# always: true, -# metric: 2, -# metric_type: 10 -# } -# }, -# "log_adjacency_changes": "details" -# "parameters": -# { -# "router_id": "10.1.1.1" -# }, -# "redistribute":[ -# { -# "route_type": "static", -# "metric_type": 2 -# "route_map": "STATIC" -# } -# ] -# }, -# { -# "afi": "ipv6", -# "ospf_area":[ -# { -# "area": "0.0.0.0", -# } -# ], -# "range": "2001:db8:1::/64", -# "parameters": -# { -# "router_id": "192.168.1.1" -# }, -# "redistribute": -# [ -# { -# "route_type": "connected", -# } -# ] -# } -# ] # -# After state: -# ------------- +# ------------------------- +# Module Execution Result +# ------------------------- +# # -# vyos@vyos:~$ show configuration commands| grep firewall -# set protocols ospf area 0 network 192.168.0.0/24 -# set protocols ospf default-information originate always -# set protocols ospf default-information originate metric 10 -# set protocols ospf default-information originate metric-type 2 -# set protocols ospf log-adjacency-changes details -# set protocols ospf parameters router-id 10.1.1.1 -# set protocols ospf redistribute static metric-type 2 -# set protocols ospf redistribute static route-map CONNECT -# set protocols ospfv3 area 0.0.0.0 range 2001:db8:2::/64 -# set protocols ospfv3 parameters router-id 192.168.2.1 -# set protocols ospfv3 redistribute connected +# "parsed": { +# "areas": [ +# { +# "area_id": "2", +# "area_type": { +# "normal": true +# }, +# "authentication": "plaintext-password", +# "shortcut": "enable" +# }, +# { +# "area_id": "3", +# "area_type": { +# "nssa": { +# "set": true +# } +# } +# }, +# { +# "area_id": "4", +# "area_type": { +# "stub": { +# "default_cost": 20, +# "set": true +# } +# }, +# "network": [ +# { +# "address": "192.0.2.0/24" +# } +# ], +# "range": [ +# { +# "address": "192.0.3.0/24", +# "cost": 10 +# }, +# { +# "address": "192.0.4.0/24", +# "cost": 12 +# } +# ] +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 2 +# }, +# "default_information": { +# "originate": { +# "always": true, +# "metric": 10, +# "metric_type": 2, +# "route_map": "ingress" +# } +# }, +# "log_adjacency_changes": "detail", +# "max_metric": { +# "router_lsa": { +# "administrative": true, +# "on_shutdown": 10, +# "on_startup": 10 +# } +# }, +# "mpls_te": { +# "enabled": true, +# "router_address": "192.0.11.11" +# }, +# "neighbor": [ +# { +# "neighbor_id": "192.0.11.12", +# "poll_interval": 10, +# "priority": 2 +# } +# ], +# "parameters": { +# "abr_type": "cisco", +# "opaque_lsa": true, +# "rfc1583_compatibility": true, +# "router_id": "192.0.1.1" +# }, +# "passive_interface": [ +# "eth2", +# "eth1" +# ], +# "redistribute": [ +# { +# "metric": 10, +# "metric_type": 2, +# "route_type": "bgp" +# } +# ] +# } +# } -# Using replaced +# Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep ospf -# set protocols ospf area 0 network 192.168.0.0/24 -# set protocols ospf default-information originate always -# set protocols ospf default-information originate metric 10 -# set protocols ospf default-information originate metric-type 2 -# set protocols ospf log-adjacency-changes details -# set protocols ospf parameters router-id 10.1.1.1 -# set protocols ospf redistribute connected metric-type 2 -# set protocols ospf redistribute connected route-map CONNECT -# set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64 -# set protocols ospfv3 parameters router-id 192.168.1.1 -# set protocols ospfv3 redistribute connected -# -- name: Replace the provided configuration with the existing running configuration - vyos_ospf_routes: - config: - - afi: 'ipv4' - ospf_area: - - area: 0 - network: 192.168.0.0/24 - area_type: - normal: True - default_information: - originate: - always: true - metric: 2 - metric_type: 10 - log_adjacency_changes: "details" - parameters: - router_id: 10.1.1.1 - redistribute: - - route_type: 'static' - metric_type: 2 - route_map: 'STATIC' - - afi: 'ipv6' - ospf_area: - - area: 0.0.0.0 - range: 2001:db8:1::/64 - parameters: - router-id 192.168.1.1 - redistribute: - - route_type: 'connected' - state: replaced +# set protocols ospf area 2 area-type 'normal' +# set protocols ospf area 2 authentication 'plaintext-password' +# set protocols ospf area 2 shortcut 'enable' +# set protocols ospf area 3 area-type 'nssa' +# set protocols ospf area 4 area-type stub default-cost '20' +# set protocols ospf area 4 network '192.0.2.0/24' +# set protocols ospf area 4 range 192.0.3.0/24 cost '10' +# set protocols ospf area 4 range 192.0.4.0/24 cost '12' +# set protocols ospf auto-cost reference-bandwidth '2' +# set protocols ospf default-information originate 'always' +# set protocols ospf default-information originate metric '10' +# set protocols ospf default-information originate metric-type '2' +# set protocols ospf default-information originate route-map 'ingress' +# set protocols ospf log-adjacency-changes 'detail' +# set protocols ospf max-metric router-lsa 'administrative' +# set protocols ospf max-metric router-lsa on-shutdown '10' +# set protocols ospf max-metric router-lsa on-startup '10' +# set protocols ospf mpls-te 'enable' +# set protocols ospf mpls-te router-address '192.0.11.11' +# set protocols ospf neighbor 192.0.11.12 poll-interval '10' +# set protocols ospf neighbor 192.0.11.12 priority '2' +# set protocols ospf parameters abr-type 'cisco' +# set protocols ospf parameters 'opaque-lsa' +# set protocols ospf parameters 'rfc1583-compatibility' +# set protocols ospf parameters router-id '192.0.1.1' +# set protocols ospf passive-interface 'eth1' +# set protocols ospf passive-interface 'eth2' +# set protocols ospf redistribute bgp metric '10' +# set protocols ospf redistribute bgp metric-type '2' +# +- name: Gather ospfv2 routes config with provided configurations + vyos.vyos.vyos_ospfv2: + config: + state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # -# before": [ -# { -# { -# "afi": "ipv4", -# "ospf_area":[ -# { -# "area": "0", -# "network": "192.168.0.0/24" -# } -# ], -# "default_information": -# { -# "originate": -# { -# always: true, -# metric: 2, -# metric_type: 10 -# } -# }, -# "log_adjacency_changes": "details" -# "parameters": -# { -# "router_id": "10.1.1.1" -# }, -# "redistribute":[ -# { -# "route_type": "connetced", -# "metric_type": 2 -# "route_map": "CONNECT" -# } -# ] -# }, -# { -# "afi": "ipv6", -# "ospf_area":[ -# { -# "area": "0.0.0.0", -# } -# ], -# "range": "2001:db8:1::/64", -# "parameters": -# { -# "router_id": "192.168.1.1" -# }, -# "redistribute": -# [ -# { -# "route_type": "connetced", -# } -# ] -# } -# ] -# -# "commands": [ -# "delete protocols ospf redistribute connected", -# "set protocols ospf area 0 area_type normal", -# "set protocols ospf redistribute static metric-type 2", -# "set protocols ospf redistribute static route-map CONNECT" -# ] -# -# "after": [ -# { -# { -# "afi": "ipv4", -# "ospf_area":[ -# { -# "area": "0", -# "area_type": -# { -# normal: true -# } -# "network": "192.168.0.0/24" -# } -# ], -# "default_information": -# { -# "originate": -# { -# always: true, -# metric: 2, -# metric_type: 10 -# } -# }, -# "log_adjacency_changes": "details" -# "parameters": -# { -# "router_id": "10.1.1.1" -# }, -# "redistribute":[ -# { -# "route_type": "static", -# "metric_type": 2 -# "route_map": "STATIC" -# } -# ] -# }, -# { -# "afi": "ipv6", -# "ospf_area":[ -# { -# "area": "0.0.0.0", -# } -# ], -# "range": "2001:db8:1::/64", -# "parameters": -# { -# "router_id": "192.168.1.1" -# }, -# "redistribute": -# [ -# { -# "route_type": "connected", -# } -# ] +# "gathered": { +# "areas": [ +# { +# "area_id": "2", +# "area_type": { +# "normal": true +# }, +# "authentication": "plaintext-password", +# "shortcut": "enable" +# }, +# { +# "area_id": "3", +# "area_type": { +# "nssa": { +# "set": true +# } # } -# ] +# }, +# { +# "area_id": "4", +# "area_type": { +# "stub": { +# "default_cost": 20, +# "set": true +# } +# }, +# "network": [ +# { +# "address": "192.0.2.0/24" +# } +# ], +# "range": [ +# { +# "address": "192.0.3.0/24", +# "cost": 10 +# }, +# { +# "address": "192.0.4.0/24", +# "cost": 12 +# } +# ] +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 2 +# }, +# "default_information": { +# "originate": { +# "always": true, +# "metric": 10, +# "metric_type": 2, +# "route_map": "ingress" +# } +# }, +# "log_adjacency_changes": "detail", +# "max_metric": { +# "router_lsa": { +# "administrative": true, +# "on_shutdown": 10, +# "on_startup": 10 +# } +# }, +# "mpls_te": { +# "enabled": true, +# "router_address": "192.0.11.11" +# }, +# "neighbor": [ +# { +# "neighbor_id": "192.0.11.12", +# "poll_interval": 10, +# "priority": 2 +# } +# ], +# "parameters": { +# "abr_type": "cisco", +# "opaque_lsa": true, +# "rfc1583_compatibility": true, +# "router_id": "192.0.1.1" +# }, +# "passive_interface": [ +# "eth2", +# "eth1" +# ], +# "redistribute": [ +# { +# "metric": 10, +# "metric_type": 2, +# "route_type": "bgp" +# } +# ] +# } # # After state: # ------------- # -# vyos@vyos:~$ show configuration commands| grep firewall -# set protocols ospf area 0 network 192.168.0.0/24 -# set protocols ospf default-information originate always -# set protocols ospf default-information originate metric 10 -# set protocols ospf default-information originate metric-type 2 -# set protocols ospf log-adjacency-changes details -# set protocols ospf parameters router-id 10.1.1.1 -# set protocols ospf redistribute static metric-type 2 -# set protocols ospf redistribute static route-map CONNECT -# set protocols ospfv3 area 0.0.0.0 range 2001:db8:2::/64 -# set protocols ospfv3 parameters router-id 192.168.2.1 -# set protocols ospfv3 redistribute connected +# vyos@192# run show configuration commands | grep ospf +# set protocols ospf area 2 area-type 'normal' +# set protocols ospf area 2 authentication 'plaintext-password' +# set protocols ospf area 2 shortcut 'enable' +# set protocols ospf area 3 area-type 'nssa' +# set protocols ospf area 4 area-type stub default-cost '20' +# set protocols ospf area 4 network '192.0.2.0/24' +# set protocols ospf area 4 range 192.0.3.0/24 cost '10' +# set protocols ospf area 4 range 192.0.4.0/24 cost '12' +# set protocols ospf auto-cost reference-bandwidth '2' +# set protocols ospf default-information originate 'always' +# set protocols ospf default-information originate metric '10' +# set protocols ospf default-information originate metric-type '2' +# set protocols ospf default-information originate route-map 'ingress' +# set protocols ospf log-adjacency-changes 'detail' +# set protocols ospf max-metric router-lsa 'administrative' +# set protocols ospf max-metric router-lsa on-shutdown '10' +# set protocols ospf max-metric router-lsa on-startup '10' +# set protocols ospf mpls-te 'enable' +# set protocols ospf mpls-te router-address '192.0.11.11' +# set protocols ospf neighbor 192.0.11.12 poll-interval '10' +# set protocols ospf neighbor 192.0.11.12 priority '2' +# set protocols ospf parameters abr-type 'cisco' +# set protocols ospf parameters 'opaque-lsa' +# set protocols ospf parameters 'rfc1583-compatibility' +# set protocols ospf parameters router-id '192.0.1.1' +# set protocols ospf passive-interface 'eth1' +# set protocols ospf passive-interface 'eth2' +# set protocols ospf redistribute bgp metric '10' +# set protocols ospf redistribute bgp metric-type '2' # Using deleted # -# Before state: +# Before state # ------------- # # vyos@192# run show configuration commands | grep ospf -# set protocols ospf area 0 network 192.168.0.0/24 -# set protocols ospf default-information originate always -# set protocols ospf default-information originate metric 10 -# set protocols ospf default-information originate metric-type 2 -# set protocols ospf log-adjacency-changes details -# set protocols ospf parameters router-id 10.1.1.1 -# set protocols ospf redistribute connected metric-type 2 -# set protocols ospf redistribute connected route-map CONNECT -# set protocols ospfv3 area 0.0.0.0 range 2001:db8:1::/64 -# set protocols ospfv3 parameters router-id 192.168.1.1 -# set protocols ospfv3 redistribute connected -# -- name: Delete all the configuration - vyos_ospf_routes: +# set protocols ospf area 2 area-type 'normal' +# set protocols ospf area 2 authentication 'plaintext-password' +# set protocols ospf area 2 shortcut 'enable' +# set protocols ospf area 3 area-type 'nssa' +# set protocols ospf area 4 area-type stub default-cost '20' +# set protocols ospf area 4 network '192.0.2.0/24' +# set protocols ospf area 4 range 192.0.3.0/24 cost '10' +# set protocols ospf area 4 range 192.0.4.0/24 cost '12' +# set protocols ospf auto-cost reference-bandwidth '2' +# set protocols ospf default-information originate 'always' +# set protocols ospf default-information originate metric '10' +# set protocols ospf default-information originate metric-type '2' +# set protocols ospf default-information originate route-map 'ingress' +# set protocols ospf log-adjacency-changes 'detail' +# set protocols ospf max-metric router-lsa 'administrative' +# set protocols ospf max-metric router-lsa on-shutdown '10' +# set protocols ospf max-metric router-lsa on-startup '10' +# set protocols ospf mpls-te 'enable' +# set protocols ospf mpls-te router-address '192.0.11.11' +# set protocols ospf neighbor 192.0.11.12 poll-interval '10' +# set protocols ospf neighbor 192.0.11.12 priority '2' +# set protocols ospf parameters abr-type 'cisco' +# set protocols ospf parameters 'opaque-lsa' +# set protocols ospf parameters 'rfc1583-compatibility' +# set protocols ospf parameters router-id '192.0.1.1' +# set protocols ospf passive-interface 'eth1' +# set protocols ospf passive-interface 'eth2' +# set protocols ospf redistribute bgp metric '10' +# set protocols ospf redistribute bgp metric-type '2' +# +- name: Delete single attributes of ospfv2 routes. + vyos.vyos.vyos_ospfv2: config: + log_adjacency_changes: 'detail' + max_metric: + default_information: + mpls_te: + neighbor: + redistribute: + parameters: + passive_interface: + areas: state: deleted # # -# ------------------------- -# Module Execution Result -# ------------------------- -# -# before": [ -# { -# { -# "afi": "ipv4", -# "ospf_area":[ -# { -# "area": "0", -# "network": "192.168.0.0/24" -# } -# ], -# "default_information": -# { -# "originate": -# { -# always: true, -# metric: 2, -# metric_type: 10 -# } -# }, -# "log_adjacency_changes": "details" -# "parameters": -# { -# "router_id": "10.1.1.1" -# }, -# "redistribute":[ -# { -# "route_type": "connetced", -# "metric_type": 2 -# "route_map": "CONNECT" -# } -# ] -# }, -# { -# "afi": "ipv6", -# "ospf_area":[ -# { -# "area": "0.0.0.0", -# } -# ], -# "range": "2001:db8:1::/64", -# "parameters": -# { -# "router_id": "192.168.1.1" -# }, -# "redistribute": -# [ -# { -# "route_type": "connetced", -# } -# ] +# ------------------------ +# Module Execution Results +# ------------------------ +# +# "before": { +# "areas": [ +# { +# "area_id": "2", +# "area_type": { +# "normal": true +# }, +# "authentication": "plaintext-password", +# "shortcut": "enable" +# }, +# { +# "area_id": "3", +# "area_type": { +# "nssa": { +# "set": true +# } # } -# ] -# -# "commands": [ -# "delete protocols ospf", -# "delete protocols ospfv3", +# }, +# { +# "area_id": "4", +# "area_type": { +# "stub": { +# "default_cost": 20, +# "set": true +# } +# }, +# "network": [ +# { +# "address": "192.0.2.0/24" +# } +# ], +# "range": [ +# { +# "address": "192.0.3.0/24", +# "cost": 10 +# }, +# { +# "address": "192.0.4.0/24", +# "cost": 12 +# } +# ] +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 2 +# }, +# "default_information": { +# "originate": { +# "always": true, +# "metric": 10, +# "metric_type": 2, +# "route_map": "ingress" +# } +# }, +# "log_adjacency_changes": "detail", +# "max_metric": { +# "router_lsa": { +# "administrative": true, +# "on_shutdown": 10, +# "on_startup": 10 +# } +# }, +# "mpls_te": { +# "enabled": true, +# "router_address": "192.0.11.11" +# }, +# "neighbor": [ +# { +# "neighbor_id": "192.0.11.12", +# "poll_interval": 10, +# "priority": 2 +# } +# ], +# "parameters": { +# "abr_type": "cisco", +# "opaque_lsa": true, +# "rfc1583_compatibility": true, +# "router_id": "192.0.1.1" +# }, +# "passive_interface": [ +# "eth2", +# "eth1" +# ], +# "redistribute": [ +# { +# "metric": 10, +# "metric_type": 2, +# "route_type": "bgp" +# } +# ] +# } +# "commands": [ +# "delete protocols ospf mpls-te", +# "delete protocols ospf redistribute", +# "delete protocols ospf auto-cost", +# "delete protocols ospf passive-interface", +# "delete protocols ospf parameters", +# "delete protocols ospf default-information", +# "delete protocols ospf max-metric", +# "delete protocols ospf log-adjacency-changes", +# "delete protocols ospf neighbor", +# "delete protocols ospf area 2", +# "delete protocols ospf area 3", +# "delete protocols ospf area 4", +# "delete protocols ospf area" # ] # # "after": [] +# After state +# ------------ +# vyos@192# run show configuration commands | grep ospf + + +# Using deleted # -# After state: +# Before state # ------------- # -# vyos@vyos:~$ show configuration commands| grep firewall +# vyos@192# run show configuration commands | grep ospf +# set protocols ospf area 2 area-type 'normal' +# set protocols ospf area 2 authentication 'plaintext-password' +# set protocols ospf area 2 shortcut 'enable' +# set protocols ospf area 3 area-type 'nssa' +# set protocols ospf area 4 area-type stub default-cost '20' +# set protocols ospf area 4 network '192.0.2.0/24' +# set protocols ospf area 4 range 192.0.3.0/24 cost '10' +# set protocols ospf area 4 range 192.0.4.0/24 cost '12' +# set protocols ospf auto-cost reference-bandwidth '2' +# set protocols ospf default-information originate 'always' +# set protocols ospf default-information originate metric '10' +# set protocols ospf default-information originate metric-type '2' +# set protocols ospf default-information originate route-map 'ingress' +# set protocols ospf log-adjacency-changes 'detail' +# set protocols ospf max-metric router-lsa 'administrative' +# set protocols ospf max-metric router-lsa on-shutdown '10' +# set protocols ospf max-metric router-lsa on-startup '10' +# set protocols ospf mpls-te 'enable' +# set protocols ospf mpls-te router-address '192.0.11.11' +# set protocols ospf neighbor 192.0.11.12 poll-interval '10' +# set protocols ospf neighbor 192.0.11.12 priority '2' +# set protocols ospf parameters abr-type 'cisco' +# set protocols ospf parameters 'opaque-lsa' +# set protocols ospf parameters 'rfc1583-compatibility' +# set protocols ospf parameters router-id '192.0.1.1' +# set protocols ospf passive-interface 'eth1' +# set protocols ospf passive-interface 'eth2' +# set protocols ospf redistribute bgp metric '10' +# set protocols ospf redistribute bgp metric-type '2' +# +- name: Delete attributes of ospfv2 routes. + vyos.vyos.vyos_ospfv2: + config: + state: deleted +# +# +# ------------------------ +# Module Execution Results +# ------------------------ +# +# "before": { +# "areas": [ +# { +# "area_id": "2", +# "area_type": { +# "normal": true +# }, +# "authentication": "plaintext-password", +# "shortcut": "enable" +# }, +# { +# "area_id": "3", +# "area_type": { +# "nssa": { +# "set": true +# } +# } +# }, +# { +# "area_id": "4", +# "area_type": { +# "stub": { +# "default_cost": 20, +# "set": true +# } +# }, +# "network": [ +# { +# "address": "192.0.2.0/24" +# } +# ], +# "range": [ +# { +# "address": "192.0.3.0/24", +# "cost": 10 +# }, +# { +# "address": "192.0.4.0/24", +# "cost": 12 +# } +# ] +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 2 +# }, +# "default_information": { +# "originate": { +# "always": true, +# "metric": 10, +# "metric_type": 2, +# "route_map": "ingress" +# } +# }, +# "log_adjacency_changes": "detail", +# "max_metric": { +# "router_lsa": { +# "administrative": true, +# "on_shutdown": 10, +# "on_startup": 10 +# } +# }, +# "mpls_te": { +# "enabled": true, +# "router_address": "192.0.11.11" +# }, +# "neighbor": [ +# { +# "neighbor_id": "192.0.11.12", +# "poll_interval": 10, +# "priority": 2 +# } +# ], +# "parameters": { +# "abr_type": "cisco", +# "opaque_lsa": true, +# "rfc1583_compatibility": true, +# "router_id": "192.0.1.1" +# }, +# "passive_interface": [ +# "eth2", +# "eth1" +# ], +# "redistribute": [ +# { +# "metric": 10, +# "metric_type": 2, +# "route_type": "bgp" +# } +# ] +# } +# "commands": [ +# "delete protocols ospf" +# ] +# +# "after": [] +# After state +# ------------ +# vyos@192# run show configuration commands | grep ospf """ @@ -1320,12 +1929,14 @@ RETURN = """ before: description: The configuration prior to the model invocation. returned: always + type: dict sample: > The configuration returned will always be in the same format of the parameters above. after: description: The resulting configuration model invocation. returned: when changed + type: dict sample: > The configuration returned will always be in the same format of the parameters above. @@ -1333,7 +1944,8 @@ commands: description: The set of commands pushed to the remote device. returned: always type: list - sample: ['command 1', 'command 2', 'command 3'] + sample: ['set protocols ospf parameters router-id 192.0.1.1', + 'set protocols ospf passive-interface 'eth1'] """ diff --git a/tests/integration/targets/vyos_ospfv2/defaults/main.yaml b/tests/integration/targets/vyos_ospfv2/defaults/main.yaml new file mode 100644 index 00000000..852a6bee --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: '[^_].*' +test_items: [] diff --git a/tests/integration/targets/vyos_ospfv2/meta/main.yaml b/tests/integration/targets/vyos_ospfv2/meta/main.yaml new file mode 100644 index 00000000..7413320e --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/meta/main.yaml @@ -0,0 +1,3 @@ +--- +dependencies: + - prepare_vyos_tests diff --git a/tests/integration/targets/vyos_ospfv2/tasks/cli.yaml b/tests/integration/targets/vyos_ospfv2/tasks/cli.yaml new file mode 100644 index 00000000..93eb2fe4 --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/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_ospfv2/tasks/main.yaml b/tests/integration/targets/vyos_ospfv2/tasks/main.yaml new file mode 100644 index 00000000..a3db933e --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tasks/main.yaml @@ -0,0 +1,4 @@ +--- +- include: cli.yaml + tags: + - cli diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/_parsed_config.cfg b/tests/integration/targets/vyos_ospfv2/tests/cli/_parsed_config.cfg new file mode 100644 index 00000000..9cc720b4 --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/_parsed_config.cfg @@ -0,0 +1,29 @@ +set protocols ospf area 2 area-type 'normal' +set protocols ospf area 2 authentication 'plaintext-password' +set protocols ospf area 2 shortcut 'enable' +set protocols ospf area 3 area-type 'nssa' +set protocols ospf area 4 area-type stub default-cost '20' +set protocols ospf area 4 network '192.0.2.0/24' +set protocols ospf area 4 range 192.0.3.0/24 cost '10' +set protocols ospf area 4 range 192.0.4.0/24 cost '12' +set protocols ospf auto-cost reference-bandwidth '2' +set protocols ospf default-information originate 'always' +set protocols ospf default-information originate metric '10' +set protocols ospf default-information originate metric-type '2' +set protocols ospf default-information originate route-map 'ingress' +set protocols ospf log-adjacency-changes 'detail' +set protocols ospf max-metric router-lsa 'administrative' +set protocols ospf max-metric router-lsa on-shutdown '10' +set protocols ospf max-metric router-lsa on-startup '10' +set protocols ospf mpls-te 'enable' +set protocols ospf mpls-te router-address '192.0.11.11' +set protocols ospf neighbor 192.0.11.12 poll-interval '10' +set protocols ospf neighbor 192.0.11.12 priority '2' +set protocols ospf parameters abr-type 'cisco' +set protocols ospf parameters 'opaque-lsa' +set protocols ospf parameters 'rfc1583-compatibility' +set protocols ospf parameters router-id '192.0.1.1' +set protocols ospf passive-interface 'eth1' +set protocols ospf passive-interface 'eth2' +set protocols ospf redistribute bgp metric '10' +set protocols ospf redistribute bgp metric-type '2' diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/_populate.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/_populate.yaml new file mode 100644 index 00000000..9f358d5e --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/_populate.yaml @@ -0,0 +1,35 @@ +--- +- name: Setup + vars: + lines: "set protocols ospf mpls-te 'enable' \n + set protocols ospf mpls-te router-address '192.0.11.11' \n + set protocols ospf redistribute bgp metric-type '2' \n + set protocols ospf redistribute bgp metric '10'\n + set protocols ospf default-information originate metric-type '2' \n + set protocols ospf default-information originate 'always' \n + set protocols ospf default-information originate metric '10' \n + set protocols ospf default-information originate route-map 'ingress' \n + set protocols ospf auto-cost reference-bandwidth '2' \n + set protocols ospf parameters router-id '192.0.1.1' \n + set protocols ospf parameters 'opaque-lsa' \n + set protocols ospf parameters abr-type 'cisco' \n + set protocols ospf parameters 'rfc1583-compatibility' \n + set protocols ospf passive-interface 'eth1' \n + set protocols ospf passive-interface 'eth2' \n + set protocols ospf max-metric router-lsa on-shutdown '10' \n + set protocols ospf max-metric router-lsa 'administrative' \n + set protocols ospf max-metric router-lsa on-startup '10' \n + set protocols ospf log-adjacency-changes 'detail' \n + set protocols ospf neighbor 192.0.11.12 priority '2' \n + set protocols ospf neighbor 192.0.11.12 poll-interval '10' \n + set protocols ospf area 2 authentication 'plaintext-password' \n + set protocols ospf area 2 shortcut 'enable' \n + set protocols ospf area 2 area-type 'normal' \n + set protocols ospf area 3 area-type 'nssa' \n + set protocols ospf area 4 range 192.0.3.0/24 cost '10' \n + set protocols ospf area 4 range 192.0.4.0/24 cost '12' \n + set protocols ospf area 4 area-type stub default-cost '20' \n + set protocols ospf area 4 network '192.0.2.0/24'" + + ansible.netcommon.cli_config: + config: '{{ lines }}' diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/_remove_config.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/_remove_config.yaml new file mode 100644 index 00000000..73608705 --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/_remove_config.yaml @@ -0,0 +1,6 @@ +--- +- name: Remove Config + vars: + lines: "delete protocols ospf\n" + ansible.netcommon.cli_config: + config: '{{ lines }}' diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/delete_single.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/delete_single.yaml new file mode 100644 index 00000000..150242a1 --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/delete_single.yaml @@ -0,0 +1,57 @@ +--- +- debug: + msg: Start vyos_ospfv2 deleted integration tests ansible_connection={{ + ansible_connection }} + +- include_tasks: _populate.yaml + +- block: + + - name: Delete attributes of ospfv2. + register: result + vyos.vyos.vyos_ospfv2: &id001 + config: + log_adjacency_changes: 'detail' + max_metric: + default_information: + mpls_te: + neighbor: + redistribute: + parameters: + passive_interface: + areas: + state: deleted + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ populate == result['before'] }}" + + - name: Assert that the correct set of commands were generated + assert: + that: + - "{{ deleted_single['commands'] | symmetric_difference(result['commands']) |length\ + \ == 0 }}" + + - name: Assert that the after dicts were correctly generated + assert: + that: + - "{{ deleted_single['after'] == result['after'] }}" + + - name: Delete attributes of given interfaces (IDEMPOTENT) + register: result + vyos.vyos.vyos_ospfv2: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result.changed == false + - result.commands|length == 0 + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ deleted_single['after'] == result['before'] }}" + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/deleted.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/deleted.yaml new file mode 100644 index 00000000..a61f5a7c --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/deleted.yaml @@ -0,0 +1,48 @@ +--- +- debug: + msg: Start vyos_ospfv2 deleted integration tests ansible_connection={{ + ansible_connection }} + +- include_tasks: _populate.yaml + +- block: + + - name: Delete attributes of ospfv2. + register: result + vyos.vyos.vyos_ospfv2: &id001 + config: + state: deleted + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ populate == result['before'] }}" + + - name: Assert that the correct set of commands were generated + assert: + that: + - "{{ deleted['commands'] | symmetric_difference(result['commands']) |length\ + \ == 0 }}" + + - name: Assert that the after dicts were correctly generated + assert: + that: + - "{{ deleted['after'] == result['after'] }}" + + - name: Delete attributes of given interfaces (IDEMPOTENT) + register: result + vyos.vyos.vyos_ospfv2: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result.changed == false + - result.commands|length == 0 + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ deleted['after'] == result['before'] }}" + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/empty_config.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/empty_config.yaml new file mode 100644 index 00000000..4566bf47 --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/empty_config.yaml @@ -0,0 +1,49 @@ +--- +- debug: + msg: START vyos_ospfv2 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_ospfv2: + 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_ospfv2: + config: + state: replaced + +- assert: + that: + - result.msg == 'value of config parameter must not be empty for state replaced' + +- name: Parsed with empty running_config should give appropriate error message + register: result + ignore_errors: true + vyos.vyos.vyos_ospfv2: + 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_ospfv2: + 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_ospfv2/tests/cli/gathered.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/gathered.yaml new file mode 100644 index 00000000..22c378b8 --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/gathered.yaml @@ -0,0 +1,33 @@ +--- +- debug: + msg: START vyos_ospfv2 gathered integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Merge the provided configuration with the exisiting running configuration + register: result + vyos.vyos.vyos_ospfv2: &id001 + config: + state: gathered + + - name: Assert that gathered dicts was correctly generated + assert: + that: + - "{{ populate == result['gathered'] }}" + + - name: Gather the existing running configuration (IDEMPOTENT) + register: result + vyos.vyos.vyos_ospfv2: *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_ospfv2/tests/cli/merged.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/merged.yaml new file mode 100644 index 00000000..6a58bb57 --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/merged.yaml @@ -0,0 +1,101 @@ +--- +- debug: + msg: START vyos_ospfv2 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_ospfv2: &id001 + config: + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + mpls_te: + enabled: true + router_address: '192.0.11.11' + auto_cost: + reference_bandwidth: 2 + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + redistribute: + - route_type: 'bgp' + metric: 10 + metric_type: 2 + passive_interface: + - 'eth1' + - 'eth2' + parameters: + router_id: '192.0.1.1' + opaque_lsa: true + rfc1583_compatibility: true + abr_type: 'cisco' + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + area_type: + stub: + default_cost: 20 + network: + - address: '192.0.2.0/24' + range: + - address: '192.0.3.0/24' + cost: 10 + - address: '192.0.4.0/24' + cost: 12 + state: merged + + - name: Assert that before dicts were correctly generated + assert: + that: "{{ merged['before'] == result['before'] }}" + + - name: Assert that correct set of commands were generated + assert: + that: + - "{{ merged['commands'] | symmetric_difference(result['commands']) |length\ + \ == 0 }}" + + - name: Assert that after dicts was correctly generated + assert: + that: + - "{{ merged['after'] == result['after'] }}" + + - name: Merge the provided configuration with the existing running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_ospfv2: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + + - name: Assert that before dicts were correctly generated + assert: + that: + - "{{ merged['after'] == result['before'] }}" + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/merged_update.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/merged_update.yaml new file mode 100644 index 00000000..453dbb09 --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/merged_update.yaml @@ -0,0 +1,70 @@ +--- +- debug: + msg: START vyos_ospfv2 merged integration tests on connection={{ ansible_connection + }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Merge the provided configuration with the exisiting running configuration + register: result + vyos.vyos.vyos_ospfv2: &id001 + config: + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + passive_interface: + - 'eth1' + areas: + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + area_type: + stub: + set: false + network: + - address: '192.0.2.0/24' + - address: '192.0.22.0/24' + - address: '192.0.32.0/24' + state: merged + + - name: Assert that before dicts were correctly generated + assert: + that: "{{ merged_update['before'] == result['before'] }}" + + - name: Assert that correct set of commands were generated + assert: + that: + - "{{ merged_update['commands'] | symmetric_difference(result['commands']) |length\ + \ == 0 }}" + + - name: Assert that after dicts was correctly generated + assert: + that: + - "{{ merged_update['after'] == result['after'] }}" + + - name: Merge the provided configuration with the existing running configuration + (IDEMPOTENT) + register: result + vyos.vyos.vyos_ospfv2: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + + - name: Assert that before dicts were correctly generated + assert: + that: + - "{{ merged_update['after'] == result['before'] }}" + always: + + - include_tasks: _remove_config.yaml \ No newline at end of file diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/parsed.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/parsed.yaml new file mode 100644 index 00000000..6fbe2f96 --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/parsed.yaml @@ -0,0 +1,41 @@ +--- +- debug: + msg: START vyos_ospfv2 parsed integration tests on connection={{ ansible_connection + }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Gather ospfv2 facts + register: ospfv2_facts + vyos.vyos.vyos_facts: + gather_subset: + - default + gather_network_resources: + - ospfv2 + + - name: Provide the running configuration for parsing (config to be parsed) + register: result + vyos.vyos.vyos_ospfv2: &id001 + running_config: "{{ lookup('file', '_parsed_config.cfg') }}" + state: parsed + + - name: Assert that correct parsing done + assert: + that: "{{ ansible_facts['network_resources']['ospfv2'] == result['parsed']\ + \ }}" + + - name: Gather the existing running configuration (IDEMPOTENT) + register: result + vyos.vyos.vyos_ospfv2: *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_ospfv2/tests/cli/rendered.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/rendered.yaml new file mode 100644 index 00000000..86c07cf4 --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/rendered.yaml @@ -0,0 +1,88 @@ +--- +- debug: + msg: START vyos_ospfv2 rendered integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Structure provided configuration into device specific commands + register: result + vyos.vyos.vyos_ospfv2: &id001 + config: + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + mpls_te: + enabled: true + router_address: '192.0.11.11' + auto_cost: + reference_bandwidth: 2 + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + redistribute: + - route_type: 'bgp' + metric: 10 + metric_type: 2 + passive_interface: + - 'eth1' + - 'eth2' + parameters: + router_id: '192.0.1.1' + opaque_lsa: true + rfc1583_compatibility: true + abr_type: 'cisco' + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + area_type: + stub: + default_cost: 20 + network: + - address: '192.0.2.0/24' + range: + - address: '192.0.3.0/24' + cost: 10 + - address: '192.0.4.0/24' + cost: 12 + state: rendered + + - name: Assert that correct set of commands were generated + assert: + that: + - "{{ rendered['commands'] | symmetric_difference(result['rendered'])\ + \ |length == 0 }}" + + - name: Structure provided configuration into device specific commands (IDEMPOTENT) + register: result + vyos.vyos.vyos_ospfv2: *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_ospfv2/tests/cli/replaced.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/replaced.yaml new file mode 100644 index 00000000..07606f94 --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/replaced.yaml @@ -0,0 +1,100 @@ +--- +- debug: + msg: START vyos_ospfv2 replaced integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Replace device configurations of listed ospfv2 routes with provided configurations + register: result + vyos.vyos.vyos_ospfv2: &id001 + config: + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + mpls_te: + enabled: true + router_address: '192.0.22.22' + auto_cost: + reference_bandwidth: 2 + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + redistribute: + - route_type: 'bgp' + metric: 10 + metric_type: 2 + passive_interface: + - 'eth1' + parameters: + router_id: '192.0.1.1' + opaque_lsa: true + rfc1583_compatibility: true + abr_type: 'cisco' + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '4' + area_type: + stub: + default_cost: 20 + network: + - address: '192.0.2.0/24' + - address: '192.0.12.0/24' + - address: '192.0.22.0/24' + - address: '192.0.32.0/24' + range: + - address: '1.1.2.0/24' + cost: 10 + state: replaced + + - name: Assert that correct set of commands were generated + assert: + that: + - "{{ replaced['commands'] | symmetric_difference(result['commands'])\ + \ |length == 0 }}" + + - name: Assert that before dicts are correctly generated + assert: + that: + - "{{ populate == result['before'] }}" + + - name: Assert that after dict is correctly generated + assert: + that: + - "{{ replaced['after'] == result['after'] }}" + + - name: Replace device configurations of listed ospfv2 routes with provided configurarions + (IDEMPOTENT) + register: result + vyos.vyos.vyos_ospfv2: *id001 + + - name: Assert that task was idempotent + assert: + that: + - result['changed'] == false + + - name: Assert that before dict is correctly generated + assert: + that: + - "{{ replaced['after'] == result['before'] }}" + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/rtt.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/rtt.yaml new file mode 100644 index 00000000..7efc2a7b --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/rtt.yaml @@ -0,0 +1,149 @@ +--- +- debug: + msg: START vyos_ospfv2 round trip integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_config.yaml + +- block: + + - name: Apply the provided configuration (base config) + register: base_config + vyos.vyos.vyos_ospfv2: + config: + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + mpls_te: + enabled: true + router_address: '192.0.11.11' + auto_cost: + reference_bandwidth: 2 + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + redistribute: + - route_type: 'bgp' + metric: 10 + metric_type: 2 + passive_interface: + - 'eth1' + - 'eth2' + parameters: + router_id: '192.0.1.1' + opaque_lsa: true + rfc1583_compatibility: true + abr_type: 'cisco' + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + area_type: + stub: + default_cost: 20 + network: + - address: '192.0.2.0/24' + range: + - address: '192.0.3.0/24' + cost: 10 + - address: '192.0.4.0/24' + cost: 12 + state: merged + + - name: Gather ospfv2 facts + vyos.vyos.vyos_facts: + gather_subset: + - default + gather_network_resources: + - ospfv2 + + - name: Apply the provided configuration (config to be reverted) + register: result + vyos.vyos.vyos_ospfv2: + config: + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '4' + area_type: + stub: + default_cost: 20 + set: true + network: + - address: '192.0.12.0/24' + - address: '192.0.2.0/24' + - address: '192.0.22.0/24' + - address: '192.0.32.0/24' + range: + - address: '1.1.2.0/24' + cost: 10 + auto_cost: + reference_bandwidth: 2 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + mpls_te: + enabled: true + router_address: '192.0.22.22' + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + parameters: + abr_type: 'cisco' + opaque_lsa: true + rfc1583_compatibility: true + router_id: '192.0.1.1' + passive_interface: + - 'eth1' + redistribute: + - metric: 10 + metric_type: 2 + route_type: 'bgp' + state: replaced + + - name: Assert that changes were applied + assert: + that: "{{ round_trip['after'] == result['after'] }}" + + - name: Revert back to base config using facts round trip + register: revert + vyos.vyos.vyos_ospfv2: + config: "{{ ansible_facts['network_resources']['ospfv2'] }}" + state: replaced + + - name: Assert that config was reverted + assert: + that: "{{ base_config['after'] == revert['after']}}" + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_ospfv2/vars/main.yaml b/tests/integration/targets/vyos_ospfv2/vars/main.yaml new file mode 100644 index 00000000..76a54e32 --- /dev/null +++ b/tests/integration/targets/vyos_ospfv2/vars/main.yaml @@ -0,0 +1,444 @@ +--- +merged: + before: [] + commands: + - set protocols ospf mpls-te enable + - set protocols ospf mpls-te router-address '192.0.11.11' + - set protocols ospf redistribute bgp + - set protocols ospf redistribute bgp metric-type 2 + - set protocols ospf redistribute bgp metric 10 + - set protocols ospf default-information originate metric-type 2 + - set protocols ospf default-information originate always + - set protocols ospf default-information originate metric 10 + - set protocols ospf default-information originate route-map ingress + - set protocols ospf auto-cost reference-bandwidth '2' + - set protocols ospf parameters router-id '192.0.1.1' + - set protocols ospf parameters opaque-lsa + - set protocols ospf parameters abr-type 'cisco' + - set protocols ospf parameters rfc1583-compatibility + - set protocols ospf passive-interface eth1 + - set protocols ospf passive-interface eth2 + - set protocols ospf max-metric router-lsa on-shutdown 10 + - set protocols ospf max-metric router-lsa administrative + - set protocols ospf max-metric router-lsa on-startup 10 + - set protocols ospf log-adjacency-changes 'detail' + - set protocols ospf neighbor 192.0.11.12 priority 2 + - set protocols ospf neighbor 192.0.11.12 poll-interval 10 + - set protocols ospf neighbor 192.0.11.12 + - set protocols ospf area '2' + - set protocols ospf area 2 authentication plaintext-password + - set protocols ospf area 2 shortcut enable + - set protocols ospf area 2 area-type normal + - set protocols ospf area '3' + - set protocols ospf area 3 area-type nssa + - set protocols ospf area 4 range 192.0.3.0/24 cost 10 + - set protocols ospf area 4 range 192.0.3.0/24 + - set protocols ospf area 4 range 192.0.4.0/24 cost 12 + - set protocols ospf area 4 range 192.0.4.0/24 + - set protocols ospf area 4 area-type stub default-cost 20 + - set protocols ospf area '4' + - set protocols ospf area 4 network 192.0.2.0/24 + after: + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + area_type: + stub: + default_cost: 20 + set: true + network: + - address: '192.0.2.0/24' + range: + - address: '192.0.3.0/24' + cost: 10 + - address: '192.0.4.0/24' + cost: 12 + auto_cost: + reference_bandwidth: 2 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + mpls_te: + enabled: true + router_address: '192.0.11.11' + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + parameters: + abr_type: 'cisco' + opaque_lsa: true + rfc1583_compatibility: true + router_id: '192.0.1.1' + passive_interface: + - 'eth2' + - 'eth1' + redistribute: + - metric: 10 + metric_type: 2 + route_type: 'bgp' +merged_update: + before: + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + area_type: + stub: + default_cost: 20 + set: true + network: + - address: '192.0.2.0/24' + range: + - address: '192.0.3.0/24' + cost: 10 + - address: '192.0.4.0/24' + cost: 12 + auto_cost: + reference_bandwidth: 2 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + mpls_te: + enabled: true + router_address: '192.0.11.11' + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + parameters: + abr_type: 'cisco' + opaque_lsa: true + rfc1583_compatibility: true + router_id: '192.0.1.1' + passive_interface: + - 'eth2' + - 'eth1' + redistribute: + - metric: 10 + metric_type: 2 + route_type: 'bgp' + after: + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + network: + - address: '192.0.2.0/24' + - address: '192.0.22.0/24' + - address: '192.0.32.0/24' + range: + - address: '192.0.3.0/24' + cost: 10 + - address: '192.0.4.0/24' + cost: 12 + auto_cost: + reference_bandwidth: 2 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + mpls_te: + enabled: true + router_address: '192.0.11.11' + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + parameters: + abr_type: 'cisco' + opaque_lsa: true + rfc1583_compatibility: true + router_id: '192.0.1.1' + passive_interface: + - 'eth2' + - 'eth1' + redistribute: + - metric: 10 + metric_type: 2 + route_type: 'bgp' + commands: + - delete protocols ospf area 4 area-type stub + - set protocols ospf area 4 network 192.0.22.0/24 + - set protocols ospf area 4 network 192.0.32.0/24 +populate: + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + area_type: + stub: + default_cost: 20 + set: true + network: + - address: '192.0.2.0/24' + range: + - address: '192.0.3.0/24' + cost: 10 + - address: '192.0.4.0/24' + cost: 12 + auto_cost: + reference_bandwidth: 2 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + mpls_te: + enabled: true + router_address: '192.0.11.11' + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + parameters: + abr_type: 'cisco' + opaque_lsa: true + rfc1583_compatibility: true + router_id: '192.0.1.1' + passive_interface: + - 'eth2' + - 'eth1' + redistribute: + - metric: 10 + metric_type: 2 + route_type: 'bgp' +replaced: + commands: + - delete protocols ospf passive-interface eth2 + - delete protocols ospf area 3 + - delete protocols ospf area 4 range 192.0.3.0/24 cost + - delete protocols ospf area 4 range 192.0.3.0/24 + - delete protocols ospf area 4 range 192.0.4.0/24 cost + - delete protocols ospf area 4 range 192.0.4.0/24 + - set protocols ospf mpls-te router-address '192.0.22.22' + - set protocols ospf area 4 range 1.1.2.0/24 cost 10 + - set protocols ospf area 4 range 1.1.2.0/24 + - set protocols ospf area 4 network 192.0.12.0/24 + - set protocols ospf area 4 network 192.0.22.0/24 + - set protocols ospf area 4 network 192.0.32.0/24 + after: + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '4' + area_type: + stub: + default_cost: 20 + set: true + network: + - address: '192.0.12.0/24' + - address: '192.0.2.0/24' + - address: '192.0.22.0/24' + - address: '192.0.32.0/24' + range: + - address: '1.1.2.0/24' + cost: 10 + auto_cost: + reference_bandwidth: 2 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + mpls_te: + enabled: true + router_address: '192.0.22.22' + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + parameters: + abr_type: 'cisco' + opaque_lsa: true + rfc1583_compatibility: true + router_id: '192.0.1.1' + passive_interface: + - 'eth1' + redistribute: + - metric: 10 + metric_type: 2 + route_type: 'bgp' +rendered: + commands: + - set protocols ospf mpls-te enable + - set protocols ospf mpls-te router-address '192.0.11.11' + - set protocols ospf redistribute bgp + - set protocols ospf redistribute bgp metric-type 2 + - set protocols ospf redistribute bgp metric 10 + - set protocols ospf default-information originate metric-type 2 + - set protocols ospf default-information originate always + - set protocols ospf default-information originate metric 10 + - set protocols ospf default-information originate route-map ingress + - set protocols ospf auto-cost reference-bandwidth '2' + - set protocols ospf parameters router-id '192.0.1.1' + - set protocols ospf parameters opaque-lsa + - set protocols ospf parameters abr-type 'cisco' + - set protocols ospf parameters rfc1583-compatibility + - set protocols ospf passive-interface eth1 + - set protocols ospf passive-interface eth2 + - set protocols ospf max-metric router-lsa on-shutdown 10 + - set protocols ospf max-metric router-lsa administrative + - set protocols ospf max-metric router-lsa on-startup 10 + - set protocols ospf log-adjacency-changes 'detail' + - set protocols ospf neighbor 192.0.11.12 priority 2 + - set protocols ospf neighbor 192.0.11.12 poll-interval 10 + - set protocols ospf neighbor 192.0.11.12 + - set protocols ospf area '2' + - set protocols ospf area 2 authentication plaintext-password + - set protocols ospf area 2 shortcut enable + - set protocols ospf area 2 area-type normal + - set protocols ospf area '3' + - set protocols ospf area 3 area-type nssa + - set protocols ospf area 4 range 192.0.3.0/24 cost 10 + - set protocols ospf area 4 range 192.0.3.0/24 + - set protocols ospf area 4 range 192.0.4.0/24 cost 12 + - set protocols ospf area 4 range 192.0.4.0/24 + - set protocols ospf area 4 area-type stub default-cost 20 + - set protocols ospf area '4' + - set protocols ospf area 4 network 192.0.2.0/24 +deleted_single: + commands: + - delete protocols ospf mpls-te + - delete protocols ospf redistribute + - delete protocols ospf auto-cost + - delete protocols ospf passive-interface + - delete protocols ospf parameters + - delete protocols ospf default-information + - delete protocols ospf max-metric + - delete protocols ospf log-adjacency-changes + - delete protocols ospf neighbor + - delete protocols ospf area 2 + - delete protocols ospf area 3 + - delete protocols ospf area 4 + - delete protocols ospf area + after: [] +deleted: + commands: + - 'delete protocols ospf' + after: [] +round_trip: + after: + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '4' + area_type: + stub: + default_cost: 20 + set: true + network: + - address: '192.0.12.0/24' + - address: '192.0.2.0/24' + - address: '192.0.22.0/24' + - address: '192.0.32.0/24' + range: + - address: '1.1.2.0/24' + cost: 10 + auto_cost: + reference_bandwidth: 2 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + mpls_te: + enabled: true + router_address: '192.0.22.22' + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + parameters: + abr_type: 'cisco' + opaque_lsa: true + rfc1583_compatibility: true + router_id: '192.0.1.1' + passive_interface: + - 'eth1' + redistribute: + - metric: 10 + metric_type: 2 + route_type: 'bgp' diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_ospfv2_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_ospfv2_config.cfg new file mode 100644 index 00000000..297671b2 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_ospfv2_config.cfg @@ -0,0 +1,9 @@ +set protocols ospf area 12 area-type normal +set protocols ospf area 12 authentication plaintext-password +set protocols ospf area 12 shortcut enable +set protocols ospf area 14 range 192.0.13.0/24 cost 10 +set protocols ospf area 14 range 192.0.13.0/24 +set protocols ospf area 14 range 192.0.14.0/24 cost 12 +set protocols ospf area 14 range 192.0.14.0/24 +set protocols ospf area 14 area-type stub default-cost 20 +set protocols ospf area 14 network 192.0.12.0/24 diff --git a/tests/unit/modules/network/vyos/test_vyos_ospfv2.py b/tests/unit/modules/network/vyos/test_vyos_ospfv2.py new file mode 100644 index 00000000..8e6b0957 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_ospfv2.py @@ -0,0 +1,526 @@ +# (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_ospfv2 +from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( + set_module_args, +) +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosFirewallRulesModule(TestVyosModule): + + module = vyos_ospfv2 + + def setUp(self): + super(TestVyosFirewallRulesModule, self).setUp() + self.mock_get_config = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config" + ) + self.get_config = self.mock_get_config.start() + + self.mock_load_config = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config" + ) + self.load_config = self.mock_load_config.start() + + self.mock_get_resource_connection_config = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base.get_resource_connection" + ) + self.get_resource_connection_config = ( + self.mock_get_resource_connection_config.start() + ) + + self.mock_get_resource_connection_facts = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection" + ) + self.get_resource_connection_facts = ( + self.mock_get_resource_connection_facts.start() + ) + + self.mock_execute_show_command = patch( + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv2.ospfv2.Ospfv2Facts.get_device_data" + ) + + self.execute_show_command = self.mock_execute_show_command.start() + + def tearDown(self): + super(TestVyosFirewallRulesModule, self).tearDown() + self.mock_get_resource_connection_config.stop() + self.mock_get_resource_connection_facts.stop() + self.mock_get_config.stop() + self.mock_load_config.stop() + self.mock_execute_show_command.stop() + + def load_fixtures(self, commands=None, transport="cli", filename=None): + if filename is None: + filename = "vyos_ospfv2_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_ospfv2_merged_new_config(self): + set_module_args( + dict( + config=dict( + log_adjacency_changes="detail", + mpls_te=dict(enabled=True, router_address='192.0.11.11'), + auto_cost=dict(reference_bandwidth=2), + areas=[ + dict( + area_id="2", + area_type=dict(normal=True), + authentication="plaintext-password", + shortcut='enable', + ), + dict( + area_id="4", + area_type=dict( + stub=dict(default_cost=10) + ), + network=[ + dict(address="192.0.2.0/24"), + ], + range=[ + dict(address="192.0.3.0/24", cost=10), + dict(address="192.0.4.0/24", cost=12) + ] + ), + ], + ), + state="merged", + ) + ) + commands = [ + "set protocols ospf mpls-te enable", + "set protocols ospf mpls-te router-address '192.0.11.11'", + "set protocols ospf auto-cost reference-bandwidth '2'", + "set protocols ospf log-adjacency-changes 'detail'", + "set protocols ospf area '2'", + "set protocols ospf area 2 authentication plaintext-password", + "set protocols ospf area 2 shortcut enable", + "set protocols ospf area 2 area-type normal", + "set protocols ospf area 4 range 192.0.3.0/24 cost 10", + "set protocols ospf area 4 range 192.0.3.0/24", + "set protocols ospf area 4 range 192.0.4.0/24 cost 12", + "set protocols ospf area 4 range 192.0.4.0/24", + "set protocols ospf area 4 area-type stub default-cost 10", + "set protocols ospf area '4'", + "set protocols ospf area 4 network 192.0.2.0/24" + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_ospfv2_merged_idem(self): + set_module_args( + dict( + config=dict( + areas=[ + dict( + area_id="12", + area_type=dict(normal=True), + authentication="plaintext-password", + shortcut='enable', + ), + dict( + area_id="14", + area_type=dict( + stub=dict(default_cost=20) + ), + network=[ + dict(address="192.0.12.0/24"), + ], + range=[ + dict(address="192.0.13.0/24", cost=10), + dict(address="192.0.14.0/24", cost=12) + ] + ), + ], + ), + state="merged", + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_ospfv2_merged_update_existing(self): + set_module_args( + dict( + config=dict( + areas=[ + dict( + area_id="12", + area_type=dict(normal=True), + authentication="plaintext-password", + shortcut='enable', + ), + dict( + area_id="14", + area_type=dict( + stub=dict(set=False) + ), + network=[ + dict(address="192.0.12.0/24"), + dict(address="192.0.22.0/24"), + ], + range=[ + dict(address="192.0.13.0/24", cost=10), + dict(address="192.0.14.0/24", cost=12) + ] + ), + ], + ), + state="merged", + ) + ) + commands = [ + "delete protocols ospf area 14 area-type stub", + "set protocols ospf area 14 network 192.0.22.0/24" + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_ospfv2_replaced(self): + set_module_args( + dict( + config=dict( + log_adjacency_changes="detail", + mpls_te=dict(enabled=True, router_address='192.0.11.11'), + auto_cost=dict(reference_bandwidth=2), + areas=[ + dict( + area_id="12", + area_type=dict(normal=True), + authentication="plaintext-password", + shortcut='enable', + ), + dict( + area_id="15", + area_type=dict( + stub=dict(default_cost=10) + ), + network=[ + dict(address="192.0.12.0/24"), + ], + range=[ + dict(address="192.0.13.0/24", cost=10), + dict(address="192.0.14.0/24", cost=12), + dict(address="192.0.15.0/24", cost=14) + ] + ), + ], + ), + state="replaced", + ) + ) + commands = [ + "set protocols ospf mpls-te enable", + "set protocols ospf mpls-te router-address '192.0.11.11'", + "set protocols ospf auto-cost reference-bandwidth '2'", + "set protocols ospf log-adjacency-changes 'detail'", + "delete protocols ospf area 14", + "set protocols ospf area 15 range 192.0.13.0/24 cost 10", + "set protocols ospf area 15 range 192.0.13.0/24", + "set protocols ospf area 15 range 192.0.14.0/24 cost 12", + "set protocols ospf area 15 range 192.0.14.0/24", + "set protocols ospf area 15 range 192.0.15.0/24 cost 14", + "set protocols ospf area 15 range 192.0.15.0/24", + "set protocols ospf area 15 area-type stub default-cost 10", + "set protocols ospf area '15'", + "set protocols ospf area 15 network 192.0.12.0/24" + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_ospfv2_replaced_idem(self): + set_module_args( + dict( + config=dict( + areas=[ + dict( + area_id="12", + area_type=dict(normal=True), + authentication="plaintext-password", + shortcut='enable', + ), + dict( + area_id="14", + area_type=dict( + stub=dict(default_cost=20) + ), + network=[ + dict(address="192.0.12.0/24"), + ], + range=[ + dict(address="192.0.13.0/24", cost=10), + dict(address="192.0.14.0/24", cost=12) + ] + ), + ], + ), + state="replaced", + ) + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_ospfv2_deleted_no_config(self): + set_module_args(dict(config=None, state="deleted")) + commands = ["delete protocols ospf"] + self.execute_module(changed=True, commands=commands) + + def test_vyos_ospfv2_set_01_deleted_single_attributes(self): + set_module_args( + dict( + config=dict( + mpls_te=dict(), + auto_cost=dict(), + areas=[] + ), + state="deleted", + ) + ) + commands = ["delete protocols ospf area 12", + "delete protocols ospf area 14", + "delete protocols ospf area"] + self.execute_module(changed=True, commands=commands) + + def test_vyos_ospfv2_gathered(self): + set_module_args(dict(state="gathered")) + result = self.execute_module( + changed=False, filename="vyos_ospfv2_config.cfg" + ) + gather_dict = { + "areas": [ + { + "area_id": "2", + "area_type": { + "normal": True + }, + "authentication": "plaintext-password", + "shortcut": "enable" + }, + { + "area_id": "14", + "area_type": { + "stub": { + "default_cost": 20, + "set": True + } + }, + "network": [ + { + "address": "192.0.12.0/24" + } + ], + "range": [ + { + "address": "192.0.13.0/24", + "cost": 10 + }, + { + "address": "192.0.14.0/24", + "cost": 12 + } + ] + } + ], + } + self.assertEqual(sorted(gather_dict), sorted(result["gathered"])) + + def test_vyos_ospfv2_parsed(self): + parsed_str = """set protocols ospf area 2 area-type 'normal' + set protocols ospf area 2 authentication 'plaintext-password' + set protocols ospf area 2 shortcut 'enable' + set protocols ospf area 3 area-type 'nssa' + set protocols ospf area 4 area-type stub default-cost '20' + set protocols ospf area 4 network '192.0.2.0/24' + set protocols ospf area 4 range 192.0.3.0/24 cost '10' + set protocols ospf area 4 range 192.0.4.0/24 cost '12' + set protocols ospf default-information originate 'always' + set protocols ospf default-information originate metric '10' + set protocols ospf default-information originate metric-type '2' +set protocols ospf auto-cost reference-bandwidth '2' +set protocols ospf default-information originate route-map 'ingress' +set protocols ospf log-adjacency-changes 'detail' +set protocols ospf max-metric router-lsa 'administrative' +set protocols ospf max-metric router-lsa on-shutdown '10' +set protocols ospf max-metric router-lsa on-startup '10' +set protocols ospf mpls-te 'enable' +set protocols ospf mpls-te router-address '192.0.11.11' +set protocols ospf neighbor 192.0.11.12 poll-interval '10' +set protocols ospf neighbor 192.0.11.12 priority '2' +set protocols ospf parameters abr-type 'cisco' +set protocols ospf parameters 'opaque-lsa' +set protocols ospf parameters 'rfc1583-compatibility' +set protocols ospf parameters router-id '192.0.1.1' +set protocols ospf passive-interface 'eth1' +set protocols ospf passive-interface 'eth2' +set protocols ospf redistribute bgp metric '10' +set protocols ospf redistribute bgp metric-type '2'""" + set_module_args(dict(running_config=parsed_str, state="parsed")) + result = self.execute_module(changed=False) + parsed_list = { + "areas": [ + { + "area_id": "2", + "area_type": { + "normal": True + }, + "authentication": "plaintext-password", + "shortcut": "enable" + }, + { + "area_id": "3", + "area_type": { + "nssa": { + "set": True + } + } + }, + { + "area_id": "4", + "area_type": { + "stub": { + "default_cost": 20, + "set": True + } + }, + "network": [ + { + "address": "192.0.2.0/24" + } + ], + "range": [ + { + "address": "192.0.3.0/24", + "cost": 10 + }, + { + "address": "192.0.4.0/24", + "cost": 12 + } + ] + } + ], + "auto_cost": { + "reference_bandwidth": 2 + }, + "default_information": { + "originate": { + "always": True, + "metric": 10, + "metric_type": 2, + "route_map": "ingress" + } + }, + "log_adjacency_changes": "detail", + "max_metric": { + "router_lsa": { + "administrative": True, + "on_shutdown": 10, + "on_startup": 10 + } + }, + "mpls_te": { + "enabled": True, + "router_address": "192.0.11.11" + }, + "neighbor": [ + { + "neighbor_id": "192.0.11.12", + "poll_interval": 10, + "priority": 2 + } + ], + "parameters": { + "abr_type": "cisco", + "opaque_lsa": True, + "rfc1583_compatibility": True, + "router_id": "192.0.1.1" + }, + "passive_interface": [ + "eth2", + "eth1" + ], + "redistribute": [ + { + "metric": 10, + "metric_type": 2, + "route_type": "bgp" + } + ] + } + self.assertEqual(sorted(parsed_list), sorted(result["parsed"])) + + def test_vyos_ospfv2_rendered(self): + set_module_args( + dict( + config=dict( + log_adjacency_changes="detail", + mpls_te=dict(enabled=True, router_address='192.0.11.11'), + auto_cost=dict(reference_bandwidth=2), + areas=[ + dict( + area_id="2", + area_type=dict(normal=True), + authentication="plaintext-password", + shortcut='enable', + ), + dict( + area_id="4", + area_type=dict( + stub=dict(default_cost=10) + ), + network=[ + dict(address="192.0.2.0/24"), + ], + range=[ + dict(address="192.0.3.0/24", cost=10), + dict(address="192.0.4.0/24", cost=12) + ] + ), + ], + ), + state="rendered", + ) + ) + commands = [ + "set protocols ospf mpls-te enable", + "set protocols ospf mpls-te router-address '192.0.11.11'", + "set protocols ospf auto-cost reference-bandwidth '2'", + "set protocols ospf log-adjacency-changes 'detail'", + "set protocols ospf area '2'", + "set protocols ospf area 2 authentication plaintext-password", + "set protocols ospf area 2 shortcut enable", + "set protocols ospf area 2 area-type normal", + "set protocols ospf area 4 range 192.0.3.0/24 cost 10", + "set protocols ospf area 4 range 192.0.3.0/24", + "set protocols ospf area 4 range 192.0.4.0/24 cost 12", + "set protocols ospf area 4 range 192.0.4.0/24", + "set protocols ospf area 4 area-type stub default-cost 10", + "set protocols ospf area '4'", + "set protocols ospf area 4 network 192.0.2.0/24" + ] + result = self.execute_module(changed=False) + self.assertEqual( + sorted(result["rendered"]), sorted(commands), result["rendered"] + ) diff --git a/tests/unit/modules/network/vyos/vyos_module.py b/tests/unit/modules/network/vyos/vyos_module.py index 49d46522..e0ab6992 100644 --- a/tests/unit/modules/network/vyos/vyos_module.py +++ b/tests/unit/modules/network/vyos/vyos_module.py @@ -60,7 +60,7 @@ class TestVyosModule(ModuleTestCase): commands=None, sort=True, defaults=False, - filename=None, + filename=None ): self.load_fixtures(commands) -- cgit v1.2.3 From b0a8d123261afeaca84089fd51d2714ff3b088ed Mon Sep 17 00:00:00 2001 From: Rohit Thakur Date: Mon, 11 May 2020 13:16:30 +0530 Subject: comments ncorporated Signed-off-by: Rohit Thakur --- .../network/vyos/argspec/ospfv2/ospfv2.py | 460 +++++-------- .../network/vyos/config/ospfv2/ospfv2.py | 756 +++++++++++++-------- plugins/module_utils/network/vyos/facts/facts.py | 2 +- .../network/vyos/facts/ospfv2/ospfv2.py | 271 ++++---- plugins/module_utils/network/vyos/utils/utils.py | 11 +- plugins/modules/vyos_ospfv2.py | 677 +++++++----------- .../vyos_ospfv2/tests/cli/delete_single.yaml | 57 -- .../targets/vyos_ospfv2/tests/cli/gathered.yaml | 13 +- .../vyos_ospfv2/tests/cli/merged_update.yaml | 2 +- .../targets/vyos_ospfv2/tests/cli/parsed.yaml | 46 +- .../targets/vyos_ospfv2/tests/cli/rendered.yaml | 18 +- .../integration/targets/vyos_ospfv2/vars/main.yaml | 323 +++++---- .../unit/modules/network/vyos/test_vyos_ospfv2.py | 265 +++----- tests/unit/modules/network/vyos/vyos_module.py | 2 +- 14 files changed, 1374 insertions(+), 1529 deletions(-) delete mode 100644 tests/integration/targets/vyos_ospfv2/tests/cli/delete_single.yaml (limited to 'plugins') diff --git a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py index a2a6e04e..275aaf36 100644 --- a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py @@ -29,337 +29,241 @@ The arg spec for the vyos_ospfv2 module class Ospfv2Args(object): # pylint: disable=R0903 """The arg spec for the vyos_ospfv2 module """ + def __init__(self, **kwargs): pass argument_spec = { - 'config': { - 'options': { - 'auto_cost': { - 'options': { - 'reference_bandwidth': { - 'type': 'int' - } - }, - 'type': 'dict' + "config": { + "options": { + "auto_cost": { + "options": {"reference_bandwidth": {"type": "int"}}, + "type": "dict", }, - 'default_information': { - 'options': { - 'originate': { - 'options': { - 'always': { - 'type': 'bool' - }, - 'metric': { - 'type': 'int' - }, - 'metric_type': { - 'type': 'int' - }, - 'route_map': { - 'type': 'str' - } + "default_information": { + "options": { + "originate": { + "options": { + "always": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_map": {"type": "str"}, }, - 'type': 'dict' + "type": "dict", } }, - 'type': 'dict' - }, - 'default_metric': { - 'type': 'int' + "type": "dict", }, - 'distance': { - 'options': { - 'global': { - 'type': 'int' - }, - 'ospf': { - 'options': { - 'external': { - 'type': 'int' - }, - 'inter_area': { - 'type': 'int' - }, - 'intra_area': { - 'type': 'int' - } + "default_metric": {"type": "int"}, + "distance": { + "options": { + "global": {"type": "int"}, + "ospf": { + "options": { + "external": {"type": "int"}, + "inter_area": {"type": "int"}, + "intra_area": {"type": "int"}, }, - 'type': 'dict' - } + "type": "dict", + }, }, - 'type': 'dict' + "type": "dict", }, - 'log_adjacency_changes': { - 'choices': ['detail'], - 'type': 'str' + "log_adjacency_changes": { + "choices": ["detail"], + "type": "str", }, - 'max_metric': { - 'options': { - 'router_lsa': { - 'options': { - 'administrative': { - 'type': 'bool' - }, - 'on_shutdown': { - 'type': 'int' - }, - 'on_startup': { - 'type': 'int' - } + "max_metric": { + "options": { + "router_lsa": { + "options": { + "administrative": {"type": "bool"}, + "on_shutdown": {"type": "int"}, + "on_startup": {"type": "int"}, }, - 'type': 'dict' + "type": "dict", } }, - 'type': 'dict' + "type": "dict", }, - 'mpls_te': { - 'options': { - 'enabled': { - 'type': 'bool' - }, - 'router_address': { - 'type': 'str' - } + "mpls_te": { + "options": { + "enabled": {"type": "bool"}, + "router_address": {"type": "str"}, }, - 'type': 'dict' + "type": "dict", }, - 'neighbor': { - 'elements': 'dict', - 'options': { - 'neighbor_id': { - 'type': 'str' - }, - 'poll_interval': { - 'type': 'int' - }, - 'priority': { - 'type': 'int' - } + "neighbor": { + "elements": "dict", + "options": { + "neighbor_id": {"type": "str"}, + "poll_interval": {"type": "int"}, + "priority": {"type": "int"}, }, - 'type': 'list' + "type": "list", }, - 'areas': { - 'elements': 'dict', - 'options': { - 'area_id': { - 'type': 'str' - }, - 'area_type': { - 'options': { - 'normal': { - 'type': 'bool' - }, - 'nssa': { - 'options': { - 'default_cost': { - 'type': 'int' - }, - 'no_summary': { - 'type': 'bool' + "areas": { + "elements": "dict", + "options": { + "area_id": {"type": "str"}, + "area_type": { + "options": { + "normal": {"type": "bool"}, + "nssa": { + "options": { + "default_cost": {"type": "int"}, + "no_summary": {"type": "bool"}, + "set": {"type": "bool"}, + "translate": { + "choices": [ + "always", + "candidate", + "never", + ], + "type": "str", }, - 'set': { - 'type': 'bool' - }, - 'translate': { - 'choices': - ['always', 'candidate', 'never'], - 'type': - 'str' - } }, - 'type': 'dict' + "type": "dict", }, - 'stub': { - 'options': { - 'default_cost': { - 'type': 'int' - }, - 'no_summary': { - 'type': 'bool' - }, - 'set': { - 'type': 'bool' - } + "stub": { + "options": { + "default_cost": {"type": "int"}, + "no_summary": {"type": "bool"}, + "set": {"type": "bool"}, }, - 'type': 'dict' - } + "type": "dict", + }, }, - 'type': 'dict' + "type": "dict", }, - 'authentication': { - 'choices': ['plaintext-password', 'md5'], - 'type': 'str' + "authentication": { + "choices": ["plaintext-password", "md5"], + "type": "str", }, - 'network': { - 'elements': 'dict', - 'options': { - 'address': { - 'required': True, - 'type': 'str' - } + "network": { + "elements": "dict", + "options": { + "address": {"required": True, "type": "str"} }, - 'type': 'list' + "type": "list", }, - 'range': { - 'elements': 'dict', - 'options': { - 'address': { - 'type': 'str' - }, - 'cost': { - 'type': 'int' - }, - 'not_advertise': { - 'type': 'bool' - }, - 'substitute': { - 'type': 'str' - } + "range": { + "elements": "dict", + "options": { + "address": {"type": "str"}, + "cost": {"type": "int"}, + "not_advertise": {"type": "bool"}, + "substitute": {"type": "str"}, }, - 'type': 'list' + "type": "list", }, - 'shortcut': { - 'choices': ['default', 'disable', 'enable'], - 'type': 'str' + "shortcut": { + "choices": ["default", "disable", "enable"], + "type": "str", }, - 'virtual_link': { - 'elements': 'dict', - 'options': { - 'address': { - 'type': 'str' - }, - 'authentication': { - 'options': { - 'md5': { - 'elements': 'dict', - 'options': { - 'key_id': { - 'type': 'int' - }, - 'md5_key': { - 'type': 'str' - } + "virtual_link": { + "elements": "dict", + "options": { + "address": {"type": "str"}, + "authentication": { + "options": { + "md5": { + "elements": "dict", + "options": { + "key_id": {"type": "int"}, + "md5_key": {"type": "str"}, }, - 'type': 'list' + "type": "list", }, - 'plaintext_password': { - 'type': 'str' - } + "plaintext_password": {"type": "str"}, }, - 'type': 'dict' - }, - 'dead_interval': { - 'type': 'int' - }, - 'hello_interval': { - 'type': 'int' + "type": "dict", }, - 'retransmit_interval': { - 'type': 'int' - }, - 'transmit_delay': { - 'type': 'int' - } + "dead_interval": {"type": "int"}, + "hello_interval": {"type": "int"}, + "retransmit_interval": {"type": "int"}, + "transmit_delay": {"type": "int"}, }, - 'type': 'list' - } + "type": "list", + }, }, - 'type': 'list' + "type": "list", }, - 'parameters': { - 'options': { - 'abr_type': { - 'choices': - ['cisco', 'ibm', 'shortcut', 'standard'], - 'type': 'str' + "parameters": { + "options": { + "abr_type": { + "choices": [ + "cisco", + "ibm", + "shortcut", + "standard", + ], + "type": "str", }, - 'opaque_lsa': { - 'type': 'bool' - }, - 'rfc1583_compatibility': { - 'type': 'bool' - }, - 'router_id': { - 'type': 'str' - } + "opaque_lsa": {"type": "bool"}, + "rfc1583_compatibility": {"type": "bool"}, + "router_id": {"type": "str"}, }, - 'type': 'dict' + "type": "dict", }, - 'passive_interface': { - 'type': 'list' - }, - 'passive_interface_exclude': { - 'type': 'list' - }, - 'redistribute': { - 'elements': 'dict', - 'options': { - 'metric': { - 'type': 'int' - }, - 'metric_type': { - 'type': 'int' - }, - 'route_map': { - 'type': 'str' + "passive_interface": {"type": "list"}, + "passive_interface_exclude": {"type": "list"}, + "redistribute": { + "elements": "dict", + "options": { + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_map": {"type": "str"}, + "route_type": { + "choices": [ + "bgp", + "connected", + "kernel", + "rip", + "static", + ], + "type": "str", }, - 'route_type': { - 'choices': - ['bgp', 'connected', 'kernel', 'rip', 'static'], - 'type': - 'str' - } }, - 'type': 'list' - }, - 'route_map': { - 'type': 'list' + "type": "list", }, - 'timers': { - 'options': { - 'refresh': { - 'options': { - 'timers': { - 'type': 'int' - } - }, - 'type': 'dict' + "route_map": {"type": "list"}, + "timers": { + "options": { + "refresh": { + "options": {"timers": {"type": "int"}}, + "type": "dict", }, - 'throttle': { - 'options': { - 'spf': { - 'options': { - 'delay': { - 'type': 'int' - }, - 'initial_holdtime': { - 'type': 'int' - }, - 'max_holdtime': { - 'type': 'int' - } + "throttle": { + "options": { + "spf": { + "options": { + "delay": {"type": "int"}, + "initial_holdtime": {"type": "int"}, + "max_holdtime": {"type": "int"}, }, - 'type': 'dict' + "type": "dict", } }, - 'type': 'dict' - } + "type": "dict", + }, }, - 'type': 'dict' - } + "type": "dict", + }, }, - 'type': 'dict' + "type": "dict", }, "running_config": {"type": "str"}, - 'state': { - 'choices': [ - 'merged', 'replaced', 'deleted', 'parsed', 'gathered', - 'rendered' + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "parsed", + "gathered", + "rendered", ], - 'default': - 'merged', - 'type': - 'str' - } + "default": "merged", + "type": "str", + }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py index 13645cd6..fd25c178 100644 --- a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py @@ -22,11 +22,16 @@ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.u to_list, remove_empties, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( + Facts, +) from ansible.module_utils.six import iteritems from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( - list_diff_want_only, _in_target, _is_w_same, _bool_to_str + list_diff_want_only, + _in_target, + _is_w_same, + _bool_to_str, ) @@ -36,9 +41,9 @@ class Ospfv2(ConfigBase): The vyos_ospfv2 class """ - gather_subset = ['!all', '!min'] + gather_subset = ["!all", "!min"] - gather_network_resources = ['ospfv2'] + gather_network_resources = ["ospfv2"] def __init__(self, module): super(Ospfv2, self).__init__(module) @@ -50,12 +55,10 @@ class Ospfv2(ConfigBase): :returns: The current configuration as a dictionary """ - (facts, _warnings) = \ - Facts(self._module).get_facts(self.gather_subset, - self.gather_network_resources, data=data) - ospfv2_facts = facts['ansible_network_resources'].get('ospfv2') - if not ospfv2_facts: - return [] + (facts, _warnings) = Facts(self._module).get_facts( + self.gather_subset, self.gather_network_resources, data=data + ) + ospfv2_facts = facts["ansible_network_resources"].get("ospfv2", {}) return ospfv2_facts def execute_module(self): @@ -65,47 +68,48 @@ class Ospfv2(ConfigBase): :returns: The result from module execution """ - result = {'changed': False} + result = {"changed": False} warnings = list() commands = list() if self.state in self.ACTION_STATES: existing_ospfv2_facts = self.get_ospfv2_facts() else: - existing_ospfv2_facts = [] + existing_ospfv2_facts = {} - if self.state in self.ACTION_STATES or self.state == 'rendered': + if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_ospfv2_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) - result['changed'] = True + result["changed"] = True if self.state in self.ACTION_STATES: - result['commands'] = commands + result["commands"] = commands - if self.state in self.ACTION_STATES or self.state == 'gathered': + if self.state in self.ACTION_STATES or self.state == "gathered": changed_ospfv2_facts = self.get_ospfv2_facts() - elif self.state == 'rendered': - result['rendered'] = commands - elif self.state == 'parsed': - running_config = self._module.params['running_config'] + elif self.state == "rendered": + result["rendered"] = commands + elif self.state == "parsed": + running_config = self._module.params["running_config"] if not running_config: - self._module.fail_json(msg='value of running_config parameter must not be empty for state parsed') - result['parsed'] = \ - self.get_ospfv2_facts(data=running_config) + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed" + ) + result["parsed"] = self.get_ospfv2_facts(data=running_config) else: - changed_ospfv2_facts = [] + changed_ospfv2_facts = {} if self.state in self.ACTION_STATES: - result['before'] = existing_ospfv2_facts - if result['changed']: - result['after'] = changed_ospfv2_facts - elif self.state == 'gathered': - result['gathered'] = changed_ospfv2_facts + result["before"] = existing_ospfv2_facts + if result["changed"]: + result["after"] = changed_ospfv2_facts + elif self.state == "gathered": + result["gathered"] = changed_ospfv2_facts - result['warnings'] = warnings + result["warnings"] = warnings return result def set_config(self, existing_ospfv2_facts): @@ -117,7 +121,7 @@ class Ospfv2(ConfigBase): to the desired configuration """ - want = self._module.params['config'] + want = self._module.params["config"] have = existing_ospfv2_facts resp = self.set_state(want, have) return to_list(resp) @@ -133,15 +137,21 @@ class Ospfv2(ConfigBase): """ commands = [] - if self.state in ('merged', 'replaced', 'overridden', 'rendered' - ) and not w: - self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(self.state)) - - if self.state == 'deleted': - commands.extend(self._state_deleted(w, h)) - elif self.state in ('merged', 'rendered'): + if ( + self.state in ("merged", "replaced", "overridden", "rendered") + and not w + ): + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + self.state + ) + ) + + if self.state == "deleted": + commands.extend(self._state_deleted(h)) + elif self.state in ("merged", "rendered"): commands.extend(self._state_merged(w, h)) - elif self.state == 'replaced': + elif self.state == "replaced": commands.extend(self._state_replaced(w, h)) return commands @@ -170,8 +180,7 @@ class Ospfv2(ConfigBase): commands = [] if have: - commands.extend(self._render_ospf_param(have, want, - opr=False)) + commands.extend(self._render_ospf_param(have, want, opr=False)) commands.extend(self._render_ospf_param(want, have)) return commands @@ -187,7 +196,7 @@ class Ospfv2(ConfigBase): commands.extend(self._render_ospf_param(want, have)) return commands - def _state_deleted(self, want, have): + def _state_deleted(self, have): """ The command generator when state is deleted :rtype: A list @@ -196,26 +205,8 @@ class Ospfv2(ConfigBase): """ commands = [] - if want and have: - for (key, val) in iteritems(want): - if key in have: - if key == 'areas': - h_areas = have.get(key) or [] - key = 'area' - for area in h_areas: - h_vlist = area.get('virtual_link') or [] - if h_vlist: - for vlink in h_vlist: - cmd = self._compute_command( - key=key + ' ' + area['area_id'], attr='virtual_link', - val=vlink['address'], opr=False) - commands.append(cmd) - commands.append(self._compute_command(key=key, - attr=area['area_id'], opr=False)) - commands.append(self._compute_command(attr=key, - opr=False)) - elif have: - commands.append('delete protocols ospf') + if have: + commands.append("delete protocols ospf") return commands def _render_ospf_param(self, want, have, opr=True): @@ -231,16 +222,25 @@ class Ospfv2(ConfigBase): commands = [] w = deepcopy(remove_empties(want)) - leaf = ('default_metric', 'log_adjacency_changes') + leaf = ("default_metric", "log_adjacency_changes") if w: for (key, val) in iteritems(w): if opr and key in leaf and not _is_w_same(w, have, key): - commands.append(self._form_attr_cmd(attr=key, - val=_bool_to_str(val), opr=opr)) + commands.append( + self._form_attr_cmd( + attr=key, val=_bool_to_str(val), opr=opr + ) + ) elif not opr and key in leaf and not _in_target(have, key): - commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) + commands.append( + self._form_attr_cmd( + attr=key, val=_bool_to_str(val), opr=opr + ) + ) else: - commands.extend(self._render_child_param(w, have, key, opr)) + commands.extend( + self._render_child_param(w, have, key, opr) + ) return commands def _render_child_param(self, w, h, key, opr=True): @@ -255,22 +255,23 @@ class Ospfv2(ConfigBase): """ commands = [] - if key in ('neighbor', 'redistribute'): - commands.extend(self._render_list_dict_param(key, w, h, - opr=opr)) - elif key in ('default_information', 'max_metric'): - commands.extend(self._render_nested_dict_param(key, w, h, - opr=opr)) - elif key in ('mpls_te', 'auto_cost', 'parameters', 'auto_cost'): + if key in ("neighbor", "redistribute"): + commands.extend(self._render_list_dict_param(key, w, h, opr=opr)) + elif key in ("default_information", "max_metric"): + commands.extend(self._render_nested_dict_param(key, w, h, opr=opr)) + elif key in ("mpls_te", "auto_cost", "parameters", "auto_cost"): commands.extend(self._render_dict_param(key, w, h, opr=opr)) - elif key in ('route_map', 'passive_interface', - 'passive_interface_exclude'): + elif key in ( + "route_map", + "passive_interface", + "passive_interface_exclude", + ): commands.extend(self._render_list_param(key, w, h, opr=opr)) - elif key == 'areas': + elif key == "areas": commands.extend(self._render_areas(key, w, h, opr=opr)) - elif key == 'timers': + elif key == "timers": commands.extend(self._render_timers(key, w, h, opr=opr)) - elif key == 'distance': + elif key == "distance": commands.extend(self._render_distance(key, w, h, opr=opr)) return commands @@ -291,24 +292,50 @@ class Ospfv2(ConfigBase): if not opr and not h: commands.append(self._form_attr_cmd(attr=attr, opr=opr)) elif want[attr]: - leaf_dict = {'auto_cost': 'reference_bandwidth', - 'mpls_te': ('enabled', 'router_address'), - 'parameters': ('router_id', 'abr_type', - 'opaque_lsa', 'rfc1583_compatibility')} + leaf_dict = { + "auto_cost": "reference_bandwidth", + "mpls_te": ("enabled", "router_address"), + "parameters": ( + "router_id", + "abr_type", + "opaque_lsa", + "rfc1583_compatibility", + ), + } leaf = leaf_dict[attr] for (item, value) in iteritems(want[attr]): - if opr and item in leaf and not _is_w_same(want[attr], h, item): - if item == 'enabled': - item = 'enable' - if item in ('opaque_lsa', 'enable', 'rfc1583_compatibility'): - commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) + if ( + opr + and item in leaf + and not _is_w_same(want[attr], h, item) + ): + if item == "enabled": + item = "enable" + if item in ( + "opaque_lsa", + "enable", + "rfc1583_compatibility", + ): + commands.append( + self._form_attr_cmd(key=attr, attr=item, opr=opr) + ) else: - commands.append(self._form_attr_cmd(key=attr, attr=item, val=value, opr=opr)) + commands.append( + self._form_attr_cmd( + key=attr, attr=item, val=value, opr=opr + ) + ) elif not opr and item in leaf and not _in_target(h, item): - if item == 'enabled': - commands.append(self._form_attr_cmd(key=attr, attr='enable', opr=opr)) + if item == "enabled": + commands.append( + self._form_attr_cmd( + key=attr, attr="enable", opr=opr + ) + ) else: - commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) + commands.append( + self._form_attr_cmd(key=attr, attr=item, opr=opr) + ) return commands def _render_list_param(self, attr, want, have, cmd=None, opr=True): @@ -334,24 +361,31 @@ class Ospfv2(ConfigBase): if opr: members = list_diff_want_only(w, h) for member in members: - command = cmd + attr.replace('_', '-') + ' ' - if attr == 'network': - command += member['address'] + command = cmd + attr.replace("_", "-") + " " + if attr == "network": + command += member["address"] else: command += member commands.append(command) elif not opr: if h: for member in w: - if attr == 'network': - if not self.search_obj_in_have(h, member, 'address'): - commands.append(cmd + attr.replace('_','-') + - ' ' + member['address']) + if attr == "network": + if not self.search_obj_in_have( + h, member, "address" + ): + commands.append( + cmd + + attr.replace("_", "-") + + " " + + member["address"] + ) elif member not in h: - commands.append(cmd + attr.replace('_', '-') + - ' ' + member) + commands.append( + cmd + attr.replace("_", "-") + " " + member + ) else: - commands.append(cmd + ' ' + attr.replace('_', '-')) + commands.append(cmd + " " + attr.replace("_", "-")) return commands def _render_vlink(self, attr, want, have, cmd=None, opr=True): @@ -368,53 +402,81 @@ class Ospfv2(ConfigBase): commands = [] h = [] - name = {'virtual_link': 'address'} - leaf_dict = {'virtual_link': ('address', 'dead_interval', - 'transmit_delay', 'hello_interval', - 'retransmit_interval')} + name = {"virtual_link": "address"} + leaf_dict = { + "virtual_link": ( + "address", + "dead_interval", + "transmit_delay", + "hello_interval", + "retransmit_interval", + ) + } leaf = leaf_dict[attr] w = want.get(attr) or [] if have: h = have.get(attr) or [] if not opr and not h: - commands.append(cmd + attr.replace('_', '-')) + commands.append(cmd + attr.replace("_", "-")) elif w: for w_item in w: for (key, val) in iteritems(w_item): if not cmd: cmd = self._compute_command(opr=opr) h_item = self.search_obj_in_have(h, w_item, name[attr]) - if opr and key in leaf and not _is_w_same(w_item, h_item, key): - if key in 'address': - commands.append(cmd - + attr.replace('_', '-') - + ' ' + str(val)) + if ( + opr + and key in leaf + and not _is_w_same(w_item, h_item, key) + ): + if key in "address": + commands.append( + cmd + attr.replace("_", "-") + " " + str(val) + ) else: - commands.append(cmd + attr.replace('_', '-') - + ' ' + w_item[name[attr]] - + ' ' + key.replace('_', '-') - + ' ' + str(val)) - elif not opr and key in leaf and not _in_target(h_item, key): - if key in 'address': - commands.append(cmd + attr.replace('_', '-') - + ' ' + str(val)) + commands.append( + cmd + + attr.replace("_", "-") + + " " + + w_item[name[attr]] + + " " + + key.replace("_", "-") + + " " + + str(val) + ) + elif ( + not opr and key in leaf and not _in_target(h_item, key) + ): + if key in "address": + commands.append( + cmd + attr.replace("_", "-") + " " + str(val) + ) else: - commands.append(cmd + attr.replace('_', '-') - + ' ' + w_item[name[attr]] - + ' ' + key) - elif key == 'authentication': - commands.extend(self._render_vlink_auth( - attr, - key, - w_item, - h_item, - w_item['address'], - cmd, - opr, - )) + commands.append( + cmd + + attr.replace("_", "-") + + " " + + w_item[name[attr]] + + " " + + key + ) + elif key == "authentication": + commands.extend( + self._render_vlink_auth( + attr, + key, + w_item, + h_item, + w_item["address"], + cmd, + opr, + ) + ) return commands - def _render_vlink_auth(self, attr, key, want, have, address, cmd=None, opr=True): + def _render_vlink_auth( + self, attr, key, want, have, address, cmd=None, opr=True + ): """ This function forms the set/delete commands based on the 'opr' type for attributes with in desired list of dictionary. @@ -432,9 +494,8 @@ class Ospfv2(ConfigBase): w = want.get(key) or {} if have: h = have.get(key) or {} - cmd += attr.replace('_', '-') + ' ' + address + ' ' + key + ' ' - commands.extend(self._render_list_dict_param('md5', w, h, cmd, - opr)) + cmd += attr.replace("_", "-") + " " + address + " " + key + " " + commands.extend(self._render_list_dict_param("md5", w, h, cmd, opr)) return commands def _render_list_dict_param(self, attr, want, have, cmd=None, opr=True): @@ -452,20 +513,30 @@ class Ospfv2(ConfigBase): commands = [] h = [] name = { - 'redistribute': 'route_type', - 'neighbor': 'neighbor_id', - 'range': 'address', - 'md5': 'key_id', - 'vlink': 'address', - } + "redistribute": "route_type", + "neighbor": "neighbor_id", + "range": "address", + "md5": "key_id", + "vlink": "address", + } leaf_dict = { - 'md5': 'md5_key', - 'redistribute': ('metric', 'route_map', 'route_type', 'metric_type'), - 'neighbor': ('priority', 'poll_interval', 'neighbor_id'), - 'range': ('cost', 'address', 'substitute', 'not_advertise'), - 'vlink': ('address', 'dead_interval', 'transmit_delay', - 'hello_interval', 'retransmit_interval'), - } + "md5": "md5_key", + "redistribute": ( + "metric", + "route_map", + "route_type", + "metric_type", + ), + "neighbor": ("priority", "poll_interval", "neighbor_id"), + "range": ("cost", "address", "substitute", "not_advertise"), + "vlink": ( + "address", + "dead_interval", + "transmit_delay", + "hello_interval", + "retransmit_interval", + ), + } leaf = leaf_dict[attr] w = want.get(attr) or [] if have: @@ -478,39 +549,81 @@ class Ospfv2(ConfigBase): if not cmd: cmd = self._compute_command(opr=opr) h_item = self.search_obj_in_have(h, w_item, name[attr]) - if opr and key in leaf and not _is_w_same(w_item, h_item, key): - if key in ('route_type', 'neighbor_id', - 'address', 'key_id'): - commands.append(cmd + attr + ' ' + str(val)) - elif key == 'cost': - commands.append(cmd + attr - + ' ' + w_item[name[attr]] - + ' ' + key - + ' ' + str(val)) - elif key == 'not_advertise': - commands.append(cmd + attr - + ' ' + w_item[name[attr]] - + ' ' + key.replace('_', '-')) - elif key == 'md5_key': - commands.append(cmd + attr - + ' ' + 'key-id' - + ' ' + str(w_item[name[attr]]) - + ' ' + key.replace('_', '-') - + ' ' + w_item[key]) + if ( + opr + and key in leaf + and not _is_w_same(w_item, h_item, key) + ): + if key in ( + "route_type", + "neighbor_id", + "address", + "key_id", + ): + commands.append(cmd + attr + " " + str(val)) + elif key == "cost": + commands.append( + cmd + + attr + + " " + + w_item[name[attr]] + + " " + + key + + " " + + str(val) + ) + elif key == "not_advertise": + commands.append( + cmd + + attr + + " " + + w_item[name[attr]] + + " " + + key.replace("_", "-") + ) + elif key == "md5_key": + commands.append( + cmd + + attr + + " " + + "key-id" + + " " + + str(w_item[name[attr]]) + + " " + + key.replace("_", "-") + + " " + + w_item[key] + ) else: - commands.append(cmd + attr - + ' ' + w_item[name[attr]] - + ' ' + key.replace('_', '-') - + ' ' + str(val)) - elif not opr and key in leaf \ - and not _in_target(h_item, key): - if key in ('route_type', 'neighbor_id', - 'address', 'key_id'): - commands.append(cmd + attr + ' ' + str(val)) + commands.append( + cmd + + attr + + " " + + w_item[name[attr]] + + " " + + key.replace("_", "-") + + " " + + str(val) + ) + elif ( + not opr and key in leaf and not _in_target(h_item, key) + ): + if key in ( + "route_type", + "neighbor_id", + "address", + "key_id", + ): + commands.append(cmd + attr + " " + str(val)) else: - commands.append(cmd + attr - + ' ' + w_item[name[attr]] - + ' ' + key) + commands.append( + cmd + + attr + + " " + + w_item[name[attr]] + + " " + + key + ) return commands def _render_nested_dict_param(self, attr, want, have, opr=True): @@ -526,10 +639,19 @@ class Ospfv2(ConfigBase): """ commands = [] - attr_dict = {'default_information': 'originate', - 'max_metric': 'router_lsa'} - leaf_dict = {'default_information': ('always', 'metric', 'metric_type', 'route_map'), - 'max_metric': ('administrative', 'on_startup', 'on_shutdown')} + attr_dict = { + "default_information": "originate", + "max_metric": "router_lsa", + } + leaf_dict = { + "default_information": ( + "always", + "metric", + "metric_type", + "route_map", + ), + "max_metric": ("administrative", "on_startup", "on_shutdown"), + } h = {} w = want.get(attr) or {} if have: @@ -546,20 +668,38 @@ class Ospfv2(ConfigBase): if h and key in h.keys(): h_attrib = h.get(key) or {} for (item, val) in iteritems(w[key]): - if opr and item in leaf and not _is_w_same(w[key], h_attrib, item): - if item in ('administrative', 'always') and val: - commands.append(cmd + attr.replace('_', '-') - + ' ' + key.replace('_', '-') - + ' ' + item.replace('_', '-')) - elif item not in ('administrative', 'always'): - commands.append(cmd + attr.replace('_', '-') - + ' ' + key.replace('_', '-') - + ' ' + item.replace('_', '-') - + ' ' + str(val)) - elif not opr and item in leaf \ - and not _in_target(h_attrib, item): - - commands.append(cmd + attr + ' ' + item) + if ( + opr + and item in leaf + and not _is_w_same(w[key], h_attrib, item) + ): + if item in ("administrative", "always") and val: + commands.append( + cmd + + attr.replace("_", "-") + + " " + + key.replace("_", "-") + + " " + + item.replace("_", "-") + ) + elif item not in ("administrative", "always"): + commands.append( + cmd + + attr.replace("_", "-") + + " " + + key.replace("_", "-") + + " " + + item.replace("_", "-") + + " " + + str(val) + ) + elif ( + not opr + and item in leaf + and not _in_target(h_attrib, item) + ): + + commands.append(cmd + attr + " " + item) return commands def _render_areas(self, attr, want, have, opr=True): @@ -576,43 +716,84 @@ class Ospfv2(ConfigBase): commands = [] h_lst = {} w_lst = want.get(attr) or [] - l_set = ('area_id', 'shortcut', 'authentication') + l_set = ("area_id", "shortcut", "authentication") if have: h_lst = have.get(attr) or [] if not opr and not h_lst: - commands.append(self._form_attr_cmd(attr='area', opr=opr)) + commands.append(self._form_attr_cmd(attr="area", opr=opr)) elif w_lst: for w_area in w_lst: - cmd = self._compute_command( - key='area', attr=_bool_to_str(w_area['area_id']), opr=opr - ) + ' ' - h_area = self.search_obj_in_have(h_lst, w_area, 'area_id') + cmd = ( + self._compute_command( + key="area", + attr=_bool_to_str(w_area["area_id"]), + opr=opr, + ) + + " " + ) + h_area = self.search_obj_in_have(h_lst, w_area, "area_id") if not opr and not h_area: - commands.append(self._form_attr_cmd(key='area', - attr=w_area['area_id'], opr=opr)) + commands.append( + self._form_attr_cmd( + key="area", attr=w_area["area_id"], opr=opr + ) + ) else: for (key, val) in iteritems(w_area): - if opr and key in l_set and not _is_w_same(w_area, h_area, key): - if key == 'area_id': - commands.append(self._form_attr_cmd(attr='area', - val=_bool_to_str(val), opr=opr)) + if ( + opr + and key in l_set + and not _is_w_same(w_area, h_area, key) + ): + if key == "area_id": + commands.append( + self._form_attr_cmd( + attr="area", + val=_bool_to_str(val), + opr=opr, + ) + ) else: - commands.append(cmd + key + ' ' + - _bool_to_str(val).replace('_', '-')) + commands.append( + cmd + + key + + " " + + _bool_to_str(val).replace("_", "-") + ) elif not opr and key in l_set: - if key == 'area_id' and not _in_target(h_area, key): + if key == "area_id" and not _in_target( + h_area, key + ): commands.append(cmd) continue - elif key != 'area_id' and not _in_target(h_area, key): - commands.append(cmd + val + ' ' + key) - elif key == 'area_type': - commands.extend(self._render_area_type(w_area, h_area, key, cmd, opr)) - elif key == 'network': - commands.extend(self._render_list_param(key, w_area, h_area, cmd, opr)) - elif key == 'range': - commands.extend(self._render_list_dict_param(key, w_area, h_area, cmd, opr)) - elif key == 'virtual_link': - commands.extend(self._render_vlink(key, w_area, h_area, cmd, opr)) + elif key != "area_id" and not _in_target( + h_area, key + ): + commands.append(cmd + val + " " + key) + elif key == "area_type": + commands.extend( + self._render_area_type( + w_area, h_area, key, cmd, opr + ) + ) + elif key == "network": + commands.extend( + self._render_list_param( + key, w_area, h_area, cmd, opr + ) + ) + elif key == "range": + commands.extend( + self._render_list_dict_param( + key, w_area, h_area, cmd, opr + ) + ) + elif key == "virtual_link": + commands.extend( + self._render_vlink( + key, w_area, h_area, cmd, opr + ) + ) return commands def _render_area_type(self, want, have, attr, cmd, opr=True): @@ -633,24 +814,36 @@ class Ospfv2(ConfigBase): if have: h_type = have.get(attr) or {} if not opr and not h_type: - commands.append(cmd + attr.replace('_', '-')) + commands.append(cmd + attr.replace("_", "-")) elif w_type: - key = 'normal' - if opr and key in w_type.keys() and not _is_w_same(w_type, h_type, key): + key = "normal" + if ( + opr + and key in w_type.keys() + and not _is_w_same(w_type, h_type, key) + ): if not w_type[key] and h_type and h_type[key]: - commands.append(cmd.replace('set', 'delete') - + attr.replace('_', '-') - + ' ' + key) + commands.append( + cmd.replace("set", "delete") + + attr.replace("_", "-") + + " " + + key + ) elif w_type[key]: - commands.append(cmd + attr.replace('_', '-') - + ' ' + key) - elif not opr and key in w_type.keys() and not (h_type - and key in h_type.keys()): - commands.append(cmd + want['area'] - + ' ' + attr.replace('_', '-')) - - a_type = {'nssa': ('set', 'default_cost', 'no_summary', 'translate'), - 'stub': ('set', 'default_cost', 'no_summary')} + commands.append(cmd + attr.replace("_", "-") + " " + key) + elif ( + not opr + and key in w_type.keys() + and not (h_type and key in h_type.keys()) + ): + commands.append( + cmd + want["area"] + " " + attr.replace("_", "-") + ) + + a_type = { + "nssa": ("set", "default_cost", "no_summary", "translate"), + "stub": ("set", "default_cost", "no_summary"), + } for key in a_type: w_area = want[attr].get(key) or {} h_area = {} @@ -658,29 +851,53 @@ class Ospfv2(ConfigBase): if h_type and key in h_type.keys(): h_area = h_type.get(key) or {} for (item, val) in iteritems(w_type[key]): - if opr and item in a_type[key] \ - and not _is_w_same(w_type[key], h_area, item): - if item == 'set' and val: - commands.append(cmd + attr.replace('_', '-') - + ' ' + key) + if ( + opr + and item in a_type[key] + and not _is_w_same(w_type[key], h_area, item) + ): + if item == "set" and val: + commands.append( + cmd + attr.replace("_", "-") + " " + key + ) elif not val and h_area and h_area[item]: - commands.append(cmd.replace('set', 'delete') - + attr.replace('_', '-') - + ' ' + key) - elif item != 'set': - commands.append(cmd + attr.replace('_', '-') - + ' ' + key - + ' ' + item.replace('_', '-') - + ' ' + str(val)) - elif not opr and item in a_type[key] \ - and not (h_type and key in h_type): - if item == 'set': - commands.append(cmd + attr.replace('_', '-') - + ' ' + key) + commands.append( + cmd.replace("set", "delete") + + attr.replace("_", "-") + + " " + + key + ) + elif item != "set": + commands.append( + cmd + + attr.replace("_", "-") + + " " + + key + + " " + + item.replace("_", "-") + + " " + + str(val) + ) + elif ( + not opr + and item in a_type[key] + and not (h_type and key in h_type) + ): + if item == "set": + commands.append( + cmd + attr.replace("_", "-") + " " + key + ) else: - commands.append(cmd + want['area'] - + ' ' + attr.replace('_', '-') - + ' ' + key + ' ' + item.replace('_', '-')) + commands.append( + cmd + + want["area"] + + " " + + attr.replace("_", "-") + + " " + + key + + " " + + item.replace("_", "-") + ) return commands def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True): @@ -693,9 +910,13 @@ class Ospfv2(ConfigBase): :return: generated command. """ - return self._compute_command(key, attr=self._map_attrib(attr), val=val, opr=opr) + return self._compute_command( + key, attr=self._map_attrib(attr), val=val, opr=opr + ) - def _compute_command(self, key=None, attr=None, val=None, remove=False, opr=True): + def _compute_command( + self, key=None, attr=None, val=None, remove=False, opr=True + ): """ This function construct the add/delete command based on passed attributes. :param key: parent key. @@ -706,13 +927,13 @@ class Ospfv2(ConfigBase): """ if remove or not opr: - cmd = 'delete protocols ospf ' + cmd = "delete protocols ospf " else: - cmd = 'set protocols ospf ' + cmd = "set protocols ospf " if key: - cmd += key.replace('_', '-') + ' ' + cmd += key.replace("_", "-") + " " if attr: - cmd += attr.replace('_', '-') + cmd += attr.replace("_", "-") if val: cmd += " '" + str(val) + "'" return cmd @@ -725,5 +946,4 @@ class Ospfv2(ConfigBase): :return: regex string """ - return ('disable' if attrib == 'disabled' - else attrib.replace('_', '-')) + return "disable" if attrib == "disabled" else attrib.replace("_", "-") diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index 17c7aa38..f1221f6a 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -43,7 +43,7 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firew 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, + Ospfv2Facts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import ( Default, diff --git a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py index 0467b721..d62fa9ab 100644 --- a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py @@ -15,9 +15,13 @@ __metaclass__ = type from re import findall, search, M from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import Ospfv2Args +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import ( + Ospfv2Args, +) class Ospfv2Facts(object): @@ -26,11 +30,8 @@ class Ospfv2Facts(object): """ def __init__( - self, - module, - subspec='config', - options='options', - ): + self, module, subspec="config", options="options", + ): self._module = module self.argument_spec = Ospfv2Args.argument_spec @@ -69,9 +70,9 @@ class Ospfv2Facts(object): if ospfv2: objs = self.render_config(ospfv2) facts = {} - params = utils.validate_config(self.argument_spec,{'config': objs}) - facts['ospfv2'] = utils.remove_empties(params['config']) - ansible_facts['ansible_network_resources'].update(facts) + params = utils.validate_config(self.argument_spec, {"config": objs}) + facts["ospfv2"] = utils.remove_empties(params["config"]) + ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): @@ -82,25 +83,35 @@ class Ospfv2Facts(object): :returns: The generated config """ - conf = '\n'.join(filter(lambda x: x, conf)) - a_lst = ['default_metric', 'log_adjacency_changes'] + conf = "\n".join(filter(lambda x: x, conf)) + a_lst = ["default_metric", "log_adjacency_changes"] config = self.parse_attr(conf, a_lst) if not config: config = {} - config['timers'] = self.parse_timers(conf) - config['auto_cost'] = self.parse_auto_cost(conf) - config['distance'] = self.parse_distance(conf) - config['max_metric'] = self.parse_max_metric(conf) - config['default_information'] = self.parse_def_info(conf) - config['route_map'] = self.parse_leaf_list(conf, 'route-map') - config['mpls_te'] = self.parse_attrib(conf, 'mpls_te', 'mpls-te') - config['areas'] = self.parse_attrib_list(conf, 'area', 'area_id') - config['parameters'] = self.parse_attrib(conf, 'parameters', 'parameters') - config['neighbor'] = self.parse_attrib_list(conf, 'neighbor', 'neighbor_id') - config['passive_interface'] = self.parse_leaf_list(conf, 'passive-interface') - config['redistribute'] = self.parse_attrib_list(conf, 'redistribute', 'route_type') - config['passive_interface_exclude'] = self.parse_leaf_list(conf, 'passive-interface-exclude') + config["timers"] = self.parse_timers(conf) + config["auto_cost"] = self.parse_auto_cost(conf) + config["distance"] = self.parse_distance(conf) + config["max_metric"] = self.parse_max_metric(conf) + config["default_information"] = self.parse_def_info(conf) + config["route_map"] = self.parse_leaf_list(conf, "route-map") + config["mpls_te"] = self.parse_attrib(conf, "mpls_te", "mpls-te") + config["areas"] = self.parse_attrib_list(conf, "area", "area_id") + config["parameters"] = self.parse_attrib( + conf, "parameters", "parameters" + ) + config["neighbor"] = self.parse_attrib_list( + conf, "neighbor", "neighbor_id" + ) + config["passive_interface"] = self.parse_leaf_list( + conf, "passive-interface" + ) + config["redistribute"] = self.parse_attrib_list( + conf, "redistribute", "route_type" + ) + config["passive_interface_exclude"] = self.parse_leaf_list( + conf, "passive-interface-exclude" + ) return config def parse_timers(self, conf): @@ -111,8 +122,8 @@ class Ospfv2Facts(object): """ cfg_dict = {} - cfg_dict['refresh'] = self.parse_refresh(conf, 'refresh') - cfg_dict['throttle'] = self.parse_throttle(conf, 'spf') + cfg_dict["refresh"] = self.parse_refresh(conf, "refresh") + cfg_dict["throttle"] = self.parse_throttle(conf, "spf") return cfg_dict def parse_throttle(self, conf, attrib=None): @@ -135,7 +146,7 @@ class Ospfv2Facts(object): :return: generated config dictionary """ - cfg_dict = self.parse_attr(conf, ['timers'], match=attrib) + cfg_dict = self.parse_attr(conf, ["timers"], match=attrib) return cfg_dict def parse_leaf_list(self, conf, attrib): @@ -148,10 +159,11 @@ class Ospfv2Facts(object): """ lst = [] - items = findall(r"^" + attrib + " (?:\'*)(\\S+)(?:\'*)", conf, M) + items = findall(r"^" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M) if items: for i in set(items): lst.append(i.strip("'")) + lst.sort() return lst def parse_distance(self, conf, attrib=None): @@ -162,8 +174,8 @@ class Ospfv2Facts(object): :return: generated config dictionary """ - cfg_dict = self.parse_attr(conf, ['global'], match=attrib) - cfg_dict['ospf'] = self.parse_ospf(conf, 'ospf') + cfg_dict = self.parse_attr(conf, ["global"], match=attrib) + cfg_dict["ospf"] = self.parse_ospf(conf, "ospf") return cfg_dict def parse_ospf(self, conf, attrib=None): @@ -174,7 +186,7 @@ class Ospfv2Facts(object): :return: generated config dictionary """ - cfg_dict = self.parse_attrib(conf, 'ospf', match=attrib) + cfg_dict = self.parse_attrib(conf, "ospf", match=attrib) return cfg_dict def parse_max_metric(self, conf): @@ -185,7 +197,9 @@ class Ospfv2Facts(object): """ cfg_dict = {} - cfg_dict['router_lsa'] = self.parse_attrib(conf, 'router_lsa', match='router-lsa') + cfg_dict["router_lsa"] = self.parse_attrib( + conf, "router_lsa", match="router-lsa" + ) return cfg_dict def parse_auto_cost(self, conf, attrib=None): @@ -196,8 +210,7 @@ class Ospfv2Facts(object): :return: generated config dictionary """ - cfg_dict = self.parse_attr(conf, ['reference_bandwidth'], - match=attrib) + cfg_dict = self.parse_attr(conf, ["reference_bandwidth"], match=attrib) return cfg_dict def parse_def_info(self, conf): @@ -208,7 +221,9 @@ class Ospfv2Facts(object): """ cfg_dict = {} - cfg_dict['originate'] = self.parse_attrib(conf, 'originate', 'originate') + cfg_dict["originate"] = self.parse_attrib( + conf, "originate", "originate" + ) return cfg_dict def parse_area(self, conf, area_id): @@ -219,13 +234,15 @@ class Ospfv2Facts(object): :return: generated rule configuration dictionary. """ - rule = self.parse_attrib(conf, 'area_id', match=area_id) + rule = self.parse_attrib(conf, "area_id", match=area_id) r_sub = { - 'area_type': self.parse_area_type(conf, 'area-type'), - 'network': self.parse_network(conf), - 'range': self.parse_attrib_list(conf, 'range', 'address'), - 'virtual_link': self.parse_attrib_list(conf, 'virtual-link', 'address'), - } + "area_type": self.parse_area_type(conf, "area-type"), + "network": self.parse_network(conf), + "range": self.parse_attrib_list(conf, "range", "address"), + "virtual_link": self.parse_attrib_list( + conf, "virtual-link", "address" + ), + } rule.update(r_sub) return rule @@ -237,7 +254,7 @@ class Ospfv2Facts(object): :return: generated rule configuration dictionary. """ - rule = self.parse_attrib(conf, 'key_id', match=key_id) + rule = self.parse_attrib(conf, "key_id", match=key_id) return rule def parse_area_type(self, conf, attrib=None): @@ -248,9 +265,9 @@ class Ospfv2Facts(object): :return: generated config dictionary """ - cfg_dict = self.parse_attr(conf, ['normal'], match=attrib) - cfg_dict['nssa'] = self.parse_attrib(conf, 'nssa', match='nssa') - cfg_dict['stub'] = self.parse_attrib(conf, 'stub', match='stub') + cfg_dict = self.parse_attr(conf, ["normal"], match=attrib) + cfg_dict["nssa"] = self.parse_attrib(conf, "nssa", match="nssa") + cfg_dict["stub"] = self.parse_attrib(conf, "stub", match="stub") return cfg_dict def parse_network(self, conf): @@ -265,9 +282,9 @@ class Ospfv2Facts(object): if applications: app_lst = [] for r in set(applications): - obj = {'address': r.strip("'")} + obj = {"address": r.strip("'")} app_lst.append(obj) - a_lst = sorted(app_lst, key=lambda i: i['address']) + a_lst = sorted(app_lst, key=lambda i: i["address"]) return a_lst def parse_vlink(self, conf): @@ -277,9 +294,10 @@ class Ospfv2Facts(object): :return: generated rule configuration dictionary """ - rule = self.parse_attrib(conf, 'vlink') - r_sub = {'authentication': self.parse_authentication(conf, - 'authentication')} + rule = self.parse_attrib(conf, "vlink") + r_sub = { + "authentication": self.parse_authentication(conf, "authentication") + } rule.update(r_sub) return rule @@ -291,9 +309,8 @@ class Ospfv2Facts(object): :return: generated config dictionary """ - cfg_dict = self.parse_attr(conf, ['plaintext_password'], - match=attrib) - cfg_dict['md5'] = self.parse_attrib_list(conf, 'key-id', 'key_id') + cfg_dict = self.parse_attr(conf, ["plaintext_password"], match=attrib) + cfg_dict["md5"] = self.parse_attrib_list(conf, "key-id", "key_id") return cfg_dict def parse_attrib_list(self, conf, attrib, param): @@ -307,24 +324,30 @@ class Ospfv2Facts(object): """ r_lst = [] - if attrib == 'area': - items = findall(r"^" + attrib.replace('_', '-') - + " (?:\'*)(\\S+)(?:\'*)", conf, M) - elif attrib == 'key-id': - items = findall(r"^.*" + attrib.replace('_', '-') - + " (?:\'*)(\\S+)(?:\'*)", conf, M) + if attrib == "area": + items = findall( + r"^" + attrib.replace("_", "-") + " (?:'*)(\\S+)(?:'*)", + conf, + M, + ) + elif attrib == "key-id": + items = findall( + r"^.*" + attrib.replace("_", "-") + " (?:'*)(\\S+)(?:'*)", + conf, + M, + ) else: - items = findall(r"" + attrib + " (?:\'*)(\\S+)(?:\'*)", conf, M) + items = findall(r"" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M) if items: a_lst = [] for item in set(items): i_regex = r" %s .+$" % item - cfg = '\n'.join(findall(i_regex, conf, M)) - if attrib == 'area': + cfg = "\n".join(findall(i_regex, conf, M)) + if attrib == "area": obj = self.parse_area(cfg, item) - elif attrib == 'virtual-link': + elif attrib == "virtual-link": obj = self.parse_vlink(cfg) - elif attrib == 'key-id': + elif attrib == "key-id": obj = self.parse_key(cfg, item) else: obj = self.parse_attrib(cfg, attrib) @@ -342,27 +365,32 @@ class Ospfv2Facts(object): """ param_lst = { - 'key_id': ['md5_key'], - 'mpls_te': ['enabled', 'router_address'], - 'area_id': ['shortcut', 'authentication'], - 'neighbor': ['priority', 'poll_interval'], - 'stub': ['set', 'default_cost', 'no_summary'], - 'range': ['cost', 'substitute', 'not_advertise'], - 'ospf': ['external', 'inter_area', 'intra_area'], - 'spf': ['delay', 'max_holdtime', 'initial_holdtime'], - 'redistribute': ['metric', 'metric_type', 'route_map'], - 'nssa': ['set', 'translate', 'default_cost', 'no_summary'], - 'config_routes': ['default_metric', 'log_adjacency_changes' - ], - 'originate': ['always', 'metric', 'metric_type', 'route_map' - ], - 'router_lsa': ['administrative', 'on_shutdown', 'on_startup' - ], - 'parameters': ['abr_type', 'opaque_lsa', 'router_id', - 'rfc1583_compatibility'], - 'vlink': ['dead_interval', 'hello_interval', - 'transmit_delay', 'retransmit_interval'], - } + "key_id": ["md5_key"], + "mpls_te": ["enabled", "router_address"], + "area_id": ["shortcut", "authentication"], + "neighbor": ["priority", "poll_interval"], + "stub": ["set", "default_cost", "no_summary"], + "range": ["cost", "substitute", "not_advertise"], + "ospf": ["external", "inter_area", "intra_area"], + "spf": ["delay", "max_holdtime", "initial_holdtime"], + "redistribute": ["metric", "metric_type", "route_map"], + "nssa": ["set", "translate", "default_cost", "no_summary"], + "config_routes": ["default_metric", "log_adjacency_changes"], + "originate": ["always", "metric", "metric_type", "route_map"], + "router_lsa": ["administrative", "on_shutdown", "on_startup"], + "parameters": [ + "abr_type", + "opaque_lsa", + "router_id", + "rfc1583_compatibility", + ], + "vlink": [ + "dead_interval", + "hello_interval", + "transmit_delay", + "retransmit_interval", + ], + } cfg_dict = self.parse_attr(conf, param_lst[param], match) return cfg_dict @@ -382,14 +410,13 @@ class Ospfv2Facts(object): regex = self.map_regex(attrib) if match: - regex = match.replace('_', '-') + ' ' + regex + regex = match.replace("_", "-") + " " + regex if conf: if self.is_bool(attrib): - out = conf.find(attrib.replace('_', '-')) - dis = conf.find(attrib.replace('_', '-') - + " 'disable'") + out = conf.find(attrib.replace("_", "-")) + dis = conf.find(attrib.replace("_", "-") + " 'disable'") if match: - if attrib == 'set' and conf.find(match) >= 1: + if attrib == "set" and conf.find(match) >= 1: config[attrib] = True en = conf.find(match + " 'enable'") if out >= 1: @@ -400,7 +427,7 @@ class Ospfv2Facts(object): elif match and en >= 1: config[attrib] = True else: - out = search(r"^.*" + regex + ' (.+)', conf, M) + out = search(r"^.*" + regex + " (.+)", conf, M) if out: val = out.group(1).strip("'") if self.is_num(attrib): @@ -416,9 +443,17 @@ class Ospfv2Facts(object): :return: regex string """ - return ('disable' if attrib == 'disabled' else ('enable' - if attrib == 'enabled' else ('area' if attrib - == 'area_id' else attrib.replace('_', '-')))) + return ( + "disable" + if attrib == "disabled" + else ( + "enable" + if attrib == "enabled" + else ( + "area" if attrib == "area_id" else attrib.replace("_", "-") + ) + ) + ) def is_bool(self, attrib): """ @@ -428,15 +463,15 @@ class Ospfv2Facts(object): """ bool_set = ( - 'set', - 'always', - 'normal', - 'enabled', - 'opaque_lsa', - 'not_advertise', - 'administrative', - 'rfc1583_compatibility', - ) + "set", + "always", + "normal", + "enabled", + "opaque_lsa", + "not_advertise", + "administrative", + "rfc1583_compatibility", + ) return True if attrib in bool_set else False def is_num(self, attrib): @@ -447,18 +482,18 @@ class Ospfv2Facts(object): """ num_set = ( - 'ospf', - 'delay', - 'metric', - 'inter_area', - 'intra_area', - 'on_startup', - 'metric_type', - 'on_shutdown', - 'max_holdtime', - 'poll_interval', - 'default_metric', - 'initial_holdtime', - 'key_id', - ) + "ospf", + "delay", + "metric", + "inter_area", + "intra_area", + "on_startup", + "metric_type", + "on_shutdown", + "max_holdtime", + "poll_interval", + "default_metric", + "initial_holdtime", + "key_id", + ) return True if attrib in num_set else False diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py index c539bebb..8fa2bfd9 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -113,7 +113,7 @@ def get_lst_same_for_dicts(want, have, lst): def list_diff_have_only(want_list, have_list): - """ - + """ This function generated the list containing values that are only in have list. :param want_list: @@ -238,7 +238,14 @@ def _bool_to_str(val): :param val: bool value. :return: enable/disable. """ - return "enable" if str(val) == "True" else "disable" if str(val) == "False" else val + return ( + "enable" + if str(val) == "True" + else "disable" + if str(val) == "False" + else val + ) + def _is_w_same(w, h, key): diff --git a/plugins/modules/vyos_ospfv2.py b/plugins/modules/vyos_ospfv2.py index 866ed762..9c40d9c2 100644 --- a/plugins/modules/vyos_ospfv2.py +++ b/plugins/modules/vyos_ospfv2.py @@ -27,20 +27,18 @@ The module file for vyos_ospfv2 """ from __future__ import absolute_import, division, print_function + __metaclass__ = type -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'network' -} +ANSIBLE_METADATA = {"metadata_version": "1.1", "supported_by": "Ansible"} DOCUMENTATION = """ --- module: vyos_ospfv2 version_added: 2.10 -short_description: This resource module configures and manages attributes of OSPFv2 routes on VyOS network devices. +short_description: OSPFV2 resource module description: This resource module configures and manages attributes of OSPFv2 routes on VyOS network devices. +version_added: "1.0.0" notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). @@ -57,7 +55,7 @@ options: elements: dict suboptions: area_id: - description: Configured to discard packets. + description: OSPFv2 area identity. type: str area_type: description: Area type. @@ -67,27 +65,27 @@ options: description: Normal OSPFv2 area. type: bool nssa: - description: Nssa OSPFv2 area. + description: NSSA OSPFv2 area. type: dict suboptions: - set: - description: Enabling nssa. + set: + description: Enabling NSSA. type: bool default_cost: - description: Summary-default cost of nssa area. + description: Summary-default cost of NSSA area. type: int no_summary: description: Do not inject inter-area routes into stub. type: bool - translate: - description: Nssa-abr. + translate: + description: NSSA-ABR. type: str choices: ['always', 'candidate', 'never'] - stub: + stub: description: Stub OSPFv2 area. type: dict suboptions: - set: + set: description: Enabling stub. type: bool default_cost: @@ -108,7 +106,7 @@ options: address: required: True description: OSPFv2 IPv4 network address. - type: str + type: str range: description: Summarize routes matching prefix (border routers only). type: list @@ -162,7 +160,7 @@ options: hello_interval: description: Interval between hello packets. type: int - retransmit_interval: + retransmit_interval: description: Interval between retransmitting lost link state advertisements. type: int transmit_delay: @@ -172,7 +170,7 @@ options: description: Log changes in adjacency state. type: str choices: ['detail'] - max_metric: + max_metric: description: OSPFv2 maximum/infinite-distance metric. type: dict suboptions: @@ -185,10 +183,10 @@ options: type: bool on_shutdown: description: Time to advertise self as stub-router. - type: int + type: int on_startup: description: Time to advertise self as stub-router - type: int + type: int auto_cost: description: Calculate OSPFv2 interface cost according to bandwidth. type: dict @@ -215,7 +213,7 @@ options: type: int route_map: description: Route map references. - type: str + type: str default_metric: description: Metric of redistributed routes type: int @@ -226,11 +224,11 @@ options: global: description: Global OSPFv2 administrative distance. type: int - ospf: + ospf: description: OSPFv2 administrative distance. type: dict suboptions: - external: + external: description: Distance for external routes. type: int inter_area: @@ -257,18 +255,18 @@ options: suboptions: neighbor_id: description: Identity (number/IP address) of neighbor. - type: str + type: str poll_interval: description: Seconds between dead neighbor polling interval. type: int priority: description: Neighbor priority. - type: int + type: int parameters: descriptions: OSPFv2 specific parameters. type: dict - suboptions: - abr_type: + suboptions: + abr_type: description: OSPFv2 ABR Type. type: str choices: ['cisco', 'ibm', 'shortcut', 'standard'] @@ -278,7 +276,7 @@ options: rfc1583_compatibility: description: Enable rfc1583 criteria for handling AS external routes. type: bool - router_id: + router_id: description: Override the default router identifier. type: str passive_interface: @@ -287,11 +285,11 @@ options: passive_interface_exclude: description: Interface to exclude when using passive-interface default. type: list - redistribute: + redistribute: description: Redistribute information from another routing protocol. type: list elements: dict - suboptions: + suboptions: route_type: description: Route type to redistribute. type: str @@ -304,10 +302,9 @@ options: type: int route_map: description: Route map references. - type: str - + type: str route_map: - description: Filter routes installed in local route map. + description: Filter routes installed in local route map. type: list timers: description: Adjust routing timers. @@ -339,13 +336,12 @@ options: type: int running_config: description: - - The module, by default, will connect to the remote device and retrieve the current - running-config to use as a base for comparing against the contents of source. - There are times when it is not desirable to have the task get the current running-config - for every task in a playbook. The I(running_config) argument allows the implementer - to pass in the configuration to use as the base config for comparison. This - value of this option should be the output received from device by executing - command C(show configuration commands | grep 'ospf') + - This option is used only with state I(parsed). + - The value of this option should be the output received from the VyOS device by executing + the command B(show configuration commands | grep ospf). + - 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: @@ -366,74 +362,74 @@ EXAMPLES = """ # Before state: # ------------- # -# vyos@vyos# run show configuration commands | grep ospf -# -# -- name: Merge the provided configuration with the exisiting running configuration - vyos.vyos.vyos_ospfv2: - config: - log_adjacency_changes: 'detail' - max_metric: - router_lsa: - administrative: true - on_shutdown: 10 - on_startup: 10 - default_information: - originate: - always: true - metric: 10 - metric_type: 2 - route_map: 'ingress' - mpls_te: - enabled: true - router_address: '192.0.11.11' - auto_cost: - reference_bandwidth: 2 - neighbor: - - neighbor_id: '192.0.11.12' - poll_interval: 10 - priority: 2 - redistribute: - - route_type: 'bgp' - metric: 10 - metric_type: 2 - passive_interface: - - 'eth1' - - 'eth2' - parameters: - router_id: '192.0.1.1' - opaque_lsa: true - rfc1583_compatibility: true - abr_type: 'cisco' - areas: - - area_id: '2' - area_type: - normal: true - authentication: "plaintext-password" - shortcut: 'enable' - - area_id: '3' - area_type: - nssa: - set: true - - area_id: '4' - area_type: - stub: - default_cost: 20 - network: - - address: '192.0.2.0/24' - range: - - address: '192.0.3.0/24' - cost: 10 - - address: '192.0.4.0/24' - cost: 12 - state: merged +# vyos@vyos# run show configuration commands | grep ospf +# +# +- name: Merge the provided configuration with the existing running configuration + vyos.vyos.vyos_ospfv2: + config: + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + mpls_te: + enabled: true + router_address: '192.0.11.11' + auto_cost: + reference_bandwidth: 2 + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + redistribute: + - route_type: 'bgp' + metric: 10 + metric_type: 2 + passive_interface: + - 'eth1' + - 'eth2' + parameters: + router_id: '192.0.1.1' + opaque_lsa: true + rfc1583_compatibility: true + abr_type: 'cisco' + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + area_type: + stub: + default_cost: 20 + network: + - address: '192.0.2.0/24' + range: + - address: '192.0.3.0/24' + cost: 10 + - address: '192.0.4.0/24' + cost: 12 + state: merged # # # ------------------------- # Module Execution Result # ------------------------- # -# before": [] +# before": {} # # "commands": [ # "set protocols ospf mpls-te enable", @@ -606,31 +602,31 @@ EXAMPLES = """ # Before state: # ------------- # -# vyos@vyos# run show configuration commands | grep ospf -# -# -- name: Merge the provided configuration to update exisiting running configuration - vyos.vyos.vyos_ospfv2: - config: - areas: - - area_id: '2' - area_type: - normal: true - authentication: "plaintext-password" - shortcut: 'enable' - - area_id: '3' - area_type: - nssa: - set: false - - area_id: '4' - area_type: - stub: - default_cost: 20 - network: - - address: '192.0.2.0/24' - - address: '192.0.22.0/24' - - address: '192.0.32.0/24' - state: merged +# vyos@vyos# run show configuration commands | grep ospf +# +# +- name: Merge the provided configuration to update existing running configuration + vyos.vyos.vyos_ospfv2: + config: + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: false + - area_id: '4' + area_type: + stub: + default_cost: 20 + network: + - address: '192.0.2.0/24' + - address: '192.0.22.0/24' + - address: '192.0.32.0/24' + state: merged # # # ------------------------- @@ -901,59 +897,59 @@ EXAMPLES = """ # set protocols ospf redistribute bgp metric-type '2' # - name: Replace ospfv2 routes attributes configuration. - vyos.vyos.vyos_ospfv2: - config: - log_adjacency_changes: 'detail' - max_metric: - router_lsa: - administrative: true - on_shutdown: 10 - on_startup: 10 - default_information: - originate: - always: true - metric: 10 - metric_type: 2 - route_map: 'ingress' - mpls_te: - enabled: true - router_address: '192.0.22.22' - auto_cost: - reference_bandwidth: 2 - neighbor: - - neighbor_id: '192.0.11.12' - poll_interval: 10 - priority: 2 - redistribute: - - route_type: 'bgp' - metric: 10 - metric_type: 2 - passive_interface: - - 'eth1' - parameters: - router_id: '192.0.1.1' - opaque_lsa: true - rfc1583_compatibility: true - abr_type: 'cisco' - areas: - - area_id: '2' - area_type: - normal: true - authentication: "plaintext-password" - shortcut: 'enable' - - area_id: '4' - area_type: - stub: - default_cost: 20 - network: - - address: '192.0.2.0/24' - - address: '192.0.12.0/24' - - address: '192.0.22.0/24' - - address: '192.0.32.0/24' - range: - - address: '192.0.42.0/24' - cost: 10 - state: replaced + vyos.vyos.vyos_ospfv2: + config: + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + mpls_te: + enabled: true + router_address: '192.0.22.22' + auto_cost: + reference_bandwidth: 2 + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + redistribute: + - route_type: 'bgp' + metric: 10 + metric_type: 2 + passive_interface: + - 'eth1' + parameters: + router_id: '192.0.1.1' + opaque_lsa: true + rfc1583_compatibility: true + abr_type: 'cisco' + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '4' + area_type: + stub: + default_cost: 20 + network: + - address: '192.0.2.0/24' + - address: '192.0.12.0/24' + - address: '192.0.22.0/24' + - address: '192.0.32.0/24' + range: + - address: '192.0.42.0/24' + cost: 10 + state: replaced # # # ------------------------- @@ -1154,7 +1150,7 @@ EXAMPLES = """ # } # ] # } -# +# # After state: # ------------- # @@ -1194,63 +1190,63 @@ EXAMPLES = """ # # - name: Render the commands for provided configuration - vyos.vyos.vyos_ospfv2: - config: - log_adjacency_changes: 'detail' - max_metric: - router_lsa: - administrative: true - on_shutdown: 10 - on_startup: 10 - default_information: - originate: - always: true - metric: 10 - metric_type: 2 - route_map: 'ingress' - mpls_te: - enabled: true - router_address: '192.0.11.11' - auto_cost: - reference_bandwidth: 2 - neighbor: - - neighbor_id: '192.0.11.12' - poll_interval: 10 - priority: 2 - redistribute: - - route_type: 'bgp' - metric: 10 - metric_type: 2 - passive_interface: - - 'eth1' - - 'eth2' - parameters: - router_id: '192.0.1.1' - opaque_lsa: true - rfc1583_compatibility: true - abr_type: 'cisco' - areas: - - area_id: '2' - area_type: - normal: true - authentication: "plaintext-password" - shortcut: 'enable' - - area_id: '3' - area_type: - nssa: - set: true - - area_id: '4' - area_type: - stub: - default_cost: 20 - network: - - address: '192.0.2.0/24' - range: - - address: '192.0.3.0/24' - cost: 10 - - address: '192.0.4.0/24' - cost: 12 - state: rendered + vyos.vyos.vyos_ospfv2: + config: + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + mpls_te: + enabled: true + router_address: '192.0.11.11' + auto_cost: + reference_bandwidth: 2 + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + redistribute: + - route_type: 'bgp' + metric: 10 + metric_type: 2 + passive_interface: + - 'eth1' + - 'eth2' + parameters: + router_id: '192.0.1.1' + opaque_lsa: true + rfc1583_compatibility: true + abr_type: 'cisco' + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + area_type: + stub: + default_cost: 20 + network: + - address: '192.0.2.0/24' + range: + - address: '192.0.3.0/24' + cost: 10 + - address: '192.0.4.0/24' + cost: 12 + state: rendered # # # ------------------------- @@ -1302,10 +1298,10 @@ EXAMPLES = """ # Using parsed # # -- name: Render the commands for provided configuration - vyos.vyos.vyos_ospfv2: - running_config: - "set protocols ospf area 2 area-type 'normal' +- name: Parse the commands for provided structured configuration + vyos.vyos.vyos_ospfv2: + running_config: + "set protocols ospf area 2 area-type 'normal' set protocols ospf area 2 authentication 'plaintext-password' set protocols ospf area 2 shortcut 'enable' set protocols ospf area 3 area-type 'nssa' @@ -1334,7 +1330,7 @@ EXAMPLES = """ set protocols ospf passive-interface 'eth2' set protocols ospf redistribute bgp metric '10' set protocols ospf redistribute bgp metric-type '2'" - state: parsed + state: parsed # # # ------------------------- @@ -1473,9 +1469,9 @@ EXAMPLES = """ # set protocols ospf redistribute bgp metric-type '2' # - name: Gather ospfv2 routes config with provided configurations - vyos.vyos.vyos_ospfv2: - config: - state: gathered + vyos.vyos.vyos_ospfv2: + config: + state: gathered # # # ------------------------- @@ -1609,174 +1605,6 @@ EXAMPLES = """ # set protocols ospf redistribute bgp metric-type '2' -# Using deleted -# -# Before state -# ------------- -# -# vyos@192# run show configuration commands | grep ospf -# set protocols ospf area 2 area-type 'normal' -# set protocols ospf area 2 authentication 'plaintext-password' -# set protocols ospf area 2 shortcut 'enable' -# set protocols ospf area 3 area-type 'nssa' -# set protocols ospf area 4 area-type stub default-cost '20' -# set protocols ospf area 4 network '192.0.2.0/24' -# set protocols ospf area 4 range 192.0.3.0/24 cost '10' -# set protocols ospf area 4 range 192.0.4.0/24 cost '12' -# set protocols ospf auto-cost reference-bandwidth '2' -# set protocols ospf default-information originate 'always' -# set protocols ospf default-information originate metric '10' -# set protocols ospf default-information originate metric-type '2' -# set protocols ospf default-information originate route-map 'ingress' -# set protocols ospf log-adjacency-changes 'detail' -# set protocols ospf max-metric router-lsa 'administrative' -# set protocols ospf max-metric router-lsa on-shutdown '10' -# set protocols ospf max-metric router-lsa on-startup '10' -# set protocols ospf mpls-te 'enable' -# set protocols ospf mpls-te router-address '192.0.11.11' -# set protocols ospf neighbor 192.0.11.12 poll-interval '10' -# set protocols ospf neighbor 192.0.11.12 priority '2' -# set protocols ospf parameters abr-type 'cisco' -# set protocols ospf parameters 'opaque-lsa' -# set protocols ospf parameters 'rfc1583-compatibility' -# set protocols ospf parameters router-id '192.0.1.1' -# set protocols ospf passive-interface 'eth1' -# set protocols ospf passive-interface 'eth2' -# set protocols ospf redistribute bgp metric '10' -# set protocols ospf redistribute bgp metric-type '2' -# -- name: Delete single attributes of ospfv2 routes. - vyos.vyos.vyos_ospfv2: - config: - log_adjacency_changes: 'detail' - max_metric: - default_information: - mpls_te: - neighbor: - redistribute: - parameters: - passive_interface: - areas: - state: deleted -# -# -# ------------------------ -# Module Execution Results -# ------------------------ -# -# "before": { -# "areas": [ -# { -# "area_id": "2", -# "area_type": { -# "normal": true -# }, -# "authentication": "plaintext-password", -# "shortcut": "enable" -# }, -# { -# "area_id": "3", -# "area_type": { -# "nssa": { -# "set": true -# } -# } -# }, -# { -# "area_id": "4", -# "area_type": { -# "stub": { -# "default_cost": 20, -# "set": true -# } -# }, -# "network": [ -# { -# "address": "192.0.2.0/24" -# } -# ], -# "range": [ -# { -# "address": "192.0.3.0/24", -# "cost": 10 -# }, -# { -# "address": "192.0.4.0/24", -# "cost": 12 -# } -# ] -# } -# ], -# "auto_cost": { -# "reference_bandwidth": 2 -# }, -# "default_information": { -# "originate": { -# "always": true, -# "metric": 10, -# "metric_type": 2, -# "route_map": "ingress" -# } -# }, -# "log_adjacency_changes": "detail", -# "max_metric": { -# "router_lsa": { -# "administrative": true, -# "on_shutdown": 10, -# "on_startup": 10 -# } -# }, -# "mpls_te": { -# "enabled": true, -# "router_address": "192.0.11.11" -# }, -# "neighbor": [ -# { -# "neighbor_id": "192.0.11.12", -# "poll_interval": 10, -# "priority": 2 -# } -# ], -# "parameters": { -# "abr_type": "cisco", -# "opaque_lsa": true, -# "rfc1583_compatibility": true, -# "router_id": "192.0.1.1" -# }, -# "passive_interface": [ -# "eth2", -# "eth1" -# ], -# "redistribute": [ -# { -# "metric": 10, -# "metric_type": 2, -# "route_type": "bgp" -# } -# ] -# } -# "commands": [ -# "delete protocols ospf mpls-te", -# "delete protocols ospf redistribute", -# "delete protocols ospf auto-cost", -# "delete protocols ospf passive-interface", -# "delete protocols ospf parameters", -# "delete protocols ospf default-information", -# "delete protocols ospf max-metric", -# "delete protocols ospf log-adjacency-changes", -# "delete protocols ospf neighbor", -# "delete protocols ospf area 2", -# "delete protocols ospf area 3", -# "delete protocols ospf area 4", -# "delete protocols ospf area" -# ] -# -# "after": [] -# After state -# ------------ -# vyos@192# run show configuration commands | grep ospf - - # Using deleted # # Before state @@ -1814,9 +1642,9 @@ EXAMPLES = """ # set protocols ospf redistribute bgp metric-type '2' # - name: Delete attributes of ospfv2 routes. - vyos.vyos.vyos_ospfv2: - config: - state: deleted + vyos.vyos.vyos_ospfv2: + config: + state: deleted # # # ------------------------ @@ -1918,11 +1746,11 @@ EXAMPLES = """ # "delete protocols ospf" # ] # -# "after": [] +# "after": {} # After state # ------------ # vyos@192# run show configuration commands | grep ospf - +# """ RETURN = """ @@ -1950,8 +1778,12 @@ commands: from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import Ospfv2Args -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv2.ospfv2 import Ospfv2 +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import ( + Ospfv2Args, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv2.ospfv2 import ( + Ospfv2, +) def main(): @@ -1963,6 +1795,7 @@ def main(): required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] @@ -1977,5 +1810,5 @@ def main(): module.exit_json(**result) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/delete_single.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/delete_single.yaml deleted file mode 100644 index 150242a1..00000000 --- a/tests/integration/targets/vyos_ospfv2/tests/cli/delete_single.yaml +++ /dev/null @@ -1,57 +0,0 @@ ---- -- debug: - msg: Start vyos_ospfv2 deleted integration tests ansible_connection={{ - ansible_connection }} - -- include_tasks: _populate.yaml - -- block: - - - name: Delete attributes of ospfv2. - register: result - vyos.vyos.vyos_ospfv2: &id001 - config: - log_adjacency_changes: 'detail' - max_metric: - default_information: - mpls_te: - neighbor: - redistribute: - parameters: - passive_interface: - areas: - state: deleted - - - name: Assert that the before dicts were correctly generated - assert: - that: - - "{{ populate == result['before'] }}" - - - name: Assert that the correct set of commands were generated - assert: - that: - - "{{ deleted_single['commands'] | symmetric_difference(result['commands']) |length\ - \ == 0 }}" - - - name: Assert that the after dicts were correctly generated - assert: - that: - - "{{ deleted_single['after'] == result['after'] }}" - - - name: Delete attributes of given interfaces (IDEMPOTENT) - register: result - vyos.vyos.vyos_ospfv2: *id001 - - - name: Assert that the previous task was idempotent - assert: - that: - - result.changed == false - - result.commands|length == 0 - - - name: Assert that the before dicts were correctly generated - assert: - that: - - "{{ deleted_single['after'] == result['before'] }}" - always: - - - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/gathered.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/gathered.yaml index 22c378b8..bc5e1e27 100644 --- a/tests/integration/targets/vyos_ospfv2/tests/cli/gathered.yaml +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/gathered.yaml @@ -9,9 +9,9 @@ - block: - - name: Merge the provided configuration with the exisiting running configuration + - name: Gather the provided configuration with the exisiting running configuration register: result - vyos.vyos.vyos_ospfv2: &id001 + vyos.vyos.vyos_ospfv2: config: state: gathered @@ -19,15 +19,6 @@ assert: that: - "{{ populate == result['gathered'] }}" - - - name: Gather the existing running configuration (IDEMPOTENT) - register: result - vyos.vyos.vyos_ospfv2: *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_ospfv2/tests/cli/merged_update.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/merged_update.yaml index 453dbb09..9b6823c9 100644 --- a/tests/integration/targets/vyos_ospfv2/tests/cli/merged_update.yaml +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/merged_update.yaml @@ -67,4 +67,4 @@ - "{{ merged_update['after'] == result['before'] }}" always: - - include_tasks: _remove_config.yaml \ No newline at end of file + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/parsed.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/parsed.yaml index 6fbe2f96..cfa29f99 100644 --- a/tests/integration/targets/vyos_ospfv2/tests/cli/parsed.yaml +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/parsed.yaml @@ -3,39 +3,13 @@ msg: START vyos_ospfv2 parsed integration tests on connection={{ ansible_connection }} -- include_tasks: _remove_config.yaml - -- include_tasks: _populate.yaml - -- block: - - - name: Gather ospfv2 facts - register: ospfv2_facts - vyos.vyos.vyos_facts: - gather_subset: - - default - gather_network_resources: - - ospfv2 - - - name: Provide the running configuration for parsing (config to be parsed) - register: result - vyos.vyos.vyos_ospfv2: &id001 - running_config: "{{ lookup('file', '_parsed_config.cfg') }}" - state: parsed - - - name: Assert that correct parsing done - assert: - that: "{{ ansible_facts['network_resources']['ospfv2'] == result['parsed']\ - \ }}" - - - name: Gather the existing running configuration (IDEMPOTENT) - register: result - vyos.vyos.vyos_ospfv2: *id001 - - - name: Assert that the previous task was idempotent - assert: - that: - - result['changed'] == false - always: - - - include_tasks: _remove_config.yaml +- name: Parse externally provided ospfv2 config to agnostic model + register: result + vyos.vyos.vyos_ospfv2: + running_config: "{{ lookup('file', '_parsed_config.cfg') }}" + state: parsed + +- name: Assert that config was correctly parsed + assert: + that: + - "{{ parsed['after'] == result['parsed'] }}" diff --git a/tests/integration/targets/vyos_ospfv2/tests/cli/rendered.yaml b/tests/integration/targets/vyos_ospfv2/tests/cli/rendered.yaml index 86c07cf4..8a805a6a 100644 --- a/tests/integration/targets/vyos_ospfv2/tests/cli/rendered.yaml +++ b/tests/integration/targets/vyos_ospfv2/tests/cli/rendered.yaml @@ -5,13 +5,11 @@ - include_tasks: _remove_config.yaml -- include_tasks: _populate.yaml - - block: - name: Structure provided configuration into device specific commands register: result - vyos.vyos.vyos_ospfv2: &id001 + vyos.vyos.vyos_ospfv2: config: log_adjacency_changes: 'detail' max_metric: @@ -74,15 +72,5 @@ that: - "{{ rendered['commands'] | symmetric_difference(result['rendered'])\ \ |length == 0 }}" - - - name: Structure provided configuration into device specific commands (IDEMPOTENT) - register: result - vyos.vyos.vyos_ospfv2: *id001 - - - name: Assert that the previous task was idempotent - assert: - that: - - result['changed'] == false - always: - - - include_tasks: _remove_config.yaml +- debug: + msg: END vyos_ospfv2 rendered integration tests on connection={{ ansible_connection }} diff --git a/tests/integration/targets/vyos_ospfv2/vars/main.yaml b/tests/integration/targets/vyos_ospfv2/vars/main.yaml index 76a54e32..e55da20f 100644 --- a/tests/integration/targets/vyos_ospfv2/vars/main.yaml +++ b/tests/integration/targets/vyos_ospfv2/vars/main.yaml @@ -1,6 +1,6 @@ --- merged: - before: [] + before: {} commands: - set protocols ospf mpls-te enable - set protocols ospf mpls-te router-address '192.0.11.11' @@ -39,63 +39,6 @@ merged: - set protocols ospf area '4' - set protocols ospf area 4 network 192.0.2.0/24 after: - areas: - - area_id: '2' - area_type: - normal: true - authentication: "plaintext-password" - shortcut: 'enable' - - area_id: '3' - area_type: - nssa: - set: true - - area_id: '4' - area_type: - stub: - default_cost: 20 - set: true - network: - - address: '192.0.2.0/24' - range: - - address: '192.0.3.0/24' - cost: 10 - - address: '192.0.4.0/24' - cost: 12 - auto_cost: - reference_bandwidth: 2 - default_information: - originate: - always: true - metric: 10 - metric_type: 2 - route_map: 'ingress' - log_adjacency_changes: 'detail' - max_metric: - router_lsa: - administrative: true - on_shutdown: 10 - on_startup: 10 - mpls_te: - enabled: true - router_address: '192.0.11.11' - neighbor: - - neighbor_id: '192.0.11.12' - poll_interval: 10 - priority: 2 - parameters: - abr_type: 'cisco' - opaque_lsa: true - rfc1583_compatibility: true - router_id: '192.0.1.1' - passive_interface: - - 'eth2' - - 'eth1' - redistribute: - - metric: 10 - metric_type: 2 - route_type: 'bgp' -merged_update: - before: areas: - area_id: '2' area_type: @@ -145,13 +88,14 @@ merged_update: rfc1583_compatibility: true router_id: '192.0.1.1' passive_interface: - - 'eth2' - 'eth1' + - 'eth2' redistribute: - metric: 10 metric_type: 2 route_type: 'bgp' - after: +merged_update: + before: areas: - area_id: '2' area_type: @@ -163,10 +107,12 @@ merged_update: nssa: set: true - area_id: '4' + area_type: + stub: + default_cost: 20 + set: true network: - address: '192.0.2.0/24' - - address: '192.0.22.0/24' - - address: '192.0.32.0/24' range: - address: '192.0.3.0/24' cost: 10 @@ -199,17 +145,13 @@ merged_update: rfc1583_compatibility: true router_id: '192.0.1.1' passive_interface: - - 'eth2' - 'eth1' + - 'eth2' redistribute: - metric: 10 metric_type: 2 route_type: 'bgp' - commands: - - delete protocols ospf area 4 area-type stub - - set protocols ospf area 4 network 192.0.22.0/24 - - set protocols ospf area 4 network 192.0.32.0/24 -populate: + after: areas: - area_id: '2' area_type: @@ -221,12 +163,10 @@ populate: nssa: set: true - area_id: '4' - area_type: - stub: - default_cost: 20 - set: true network: - address: '192.0.2.0/24' + - address: '192.0.22.0/24' + - address: '192.0.32.0/24' range: - address: '192.0.3.0/24' cost: 10 @@ -259,12 +199,72 @@ populate: rfc1583_compatibility: true router_id: '192.0.1.1' passive_interface: - - 'eth2' - 'eth1' + - 'eth2' redistribute: - metric: 10 metric_type: 2 route_type: 'bgp' + commands: + - delete protocols ospf area 4 area-type stub + - set protocols ospf area 4 network 192.0.22.0/24 + - set protocols ospf area 4 network 192.0.32.0/24 +populate: + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + area_type: + stub: + default_cost: 20 + set: true + network: + - address: '192.0.2.0/24' + range: + - address: '192.0.3.0/24' + cost: 10 + - address: '192.0.4.0/24' + cost: 12 + auto_cost: + reference_bandwidth: 2 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + mpls_te: + enabled: true + router_address: '192.0.11.11' + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + parameters: + abr_type: 'cisco' + opaque_lsa: true + rfc1583_compatibility: true + router_id: '192.0.1.1' + passive_interface: + - 'eth1' + - 'eth2' + redistribute: + - metric: 10 + metric_type: 2 + route_type: 'bgp' replaced: commands: - delete protocols ospf passive-interface eth2 @@ -280,57 +280,57 @@ replaced: - set protocols ospf area 4 network 192.0.22.0/24 - set protocols ospf area 4 network 192.0.32.0/24 after: - areas: - - area_id: '2' - area_type: - normal: true - authentication: "plaintext-password" - shortcut: 'enable' - - area_id: '4' - area_type: - stub: - default_cost: 20 - set: true - network: - - address: '192.0.12.0/24' - - address: '192.0.2.0/24' - - address: '192.0.22.0/24' - - address: '192.0.32.0/24' - range: - - address: '1.1.2.0/24' - cost: 10 - auto_cost: - reference_bandwidth: 2 - default_information: - originate: - always: true - metric: 10 - metric_type: 2 - route_map: 'ingress' - log_adjacency_changes: 'detail' - max_metric: - router_lsa: - administrative: true - on_shutdown: 10 - on_startup: 10 - mpls_te: - enabled: true - router_address: '192.0.22.22' - neighbor: - - neighbor_id: '192.0.11.12' - poll_interval: 10 - priority: 2 - parameters: - abr_type: 'cisco' - opaque_lsa: true - rfc1583_compatibility: true - router_id: '192.0.1.1' - passive_interface: - - 'eth1' - redistribute: - - metric: 10 - metric_type: 2 - route_type: 'bgp' + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '4' + area_type: + stub: + default_cost: 20 + set: true + network: + - address: '192.0.12.0/24' + - address: '192.0.2.0/24' + - address: '192.0.22.0/24' + - address: '192.0.32.0/24' + range: + - address: '1.1.2.0/24' + cost: 10 + auto_cost: + reference_bandwidth: 2 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + mpls_te: + enabled: true + router_address: '192.0.22.22' + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + parameters: + abr_type: 'cisco' + opaque_lsa: true + rfc1583_compatibility: true + router_id: '192.0.1.1' + passive_interface: + - 'eth1' + redistribute: + - metric: 10 + metric_type: 2 + route_type: 'bgp' rendered: commands: - set protocols ospf mpls-te enable @@ -369,26 +369,67 @@ rendered: - set protocols ospf area 4 area-type stub default-cost 20 - set protocols ospf area '4' - set protocols ospf area 4 network 192.0.2.0/24 -deleted_single: - commands: - - delete protocols ospf mpls-te - - delete protocols ospf redistribute - - delete protocols ospf auto-cost - - delete protocols ospf passive-interface - - delete protocols ospf parameters - - delete protocols ospf default-information - - delete protocols ospf max-metric - - delete protocols ospf log-adjacency-changes - - delete protocols ospf neighbor - - delete protocols ospf area 2 - - delete protocols ospf area 3 - - delete protocols ospf area 4 - - delete protocols ospf area - after: [] +parsed: + after: + areas: + - area_id: '2' + area_type: + normal: true + authentication: "plaintext-password" + shortcut: 'enable' + - area_id: '3' + area_type: + nssa: + set: true + - area_id: '4' + area_type: + stub: + default_cost: 20 + set: true + network: + - address: '192.0.2.0/24' + range: + - address: '192.0.3.0/24' + cost: 10 + - address: '192.0.4.0/24' + cost: 12 + auto_cost: + reference_bandwidth: 2 + default_information: + originate: + always: true + metric: 10 + metric_type: 2 + route_map: 'ingress' + log_adjacency_changes: 'detail' + max_metric: + router_lsa: + administrative: true + on_shutdown: 10 + on_startup: 10 + mpls_te: + enabled: true + router_address: '192.0.11.11' + neighbor: + - neighbor_id: '192.0.11.12' + poll_interval: 10 + priority: 2 + parameters: + abr_type: 'cisco' + opaque_lsa: true + rfc1583_compatibility: true + router_id: '192.0.1.1' + passive_interface: + - 'eth1' + - 'eth2' + redistribute: + - metric: 10 + metric_type: 2 + route_type: 'bgp' deleted: commands: - 'delete protocols ospf' - after: [] + after: {} round_trip: after: areas: diff --git a/tests/unit/modules/network/vyos/test_vyos_ospfv2.py b/tests/unit/modules/network/vyos/test_vyos_ospfv2.py index 8e6b0957..b825066d 100644 --- a/tests/unit/modules/network/vyos/test_vyos_ospfv2.py +++ b/tests/unit/modules/network/vyos/test_vyos_ospfv2.py @@ -71,7 +71,7 @@ class TestVyosFirewallRulesModule(TestVyosModule): self.mock_get_config.stop() self.mock_load_config.stop() self.mock_execute_show_command.stop() - + def load_fixtures(self, commands=None, transport="cli", filename=None): if filename is None: filename = "vyos_ospfv2_config.cfg" @@ -87,27 +87,23 @@ class TestVyosFirewallRulesModule(TestVyosModule): dict( config=dict( log_adjacency_changes="detail", - mpls_te=dict(enabled=True, router_address='192.0.11.11'), + mpls_te=dict(enabled=True, router_address="192.0.11.11"), auto_cost=dict(reference_bandwidth=2), areas=[ dict( area_id="2", area_type=dict(normal=True), authentication="plaintext-password", - shortcut='enable', + shortcut="enable", ), dict( area_id="4", - area_type=dict( - stub=dict(default_cost=10) - ), - network=[ - dict(address="192.0.2.0/24"), - ], + area_type=dict(stub=dict(default_cost=10)), + network=[dict(address="192.0.2.0/24"),], range=[ dict(address="192.0.3.0/24", cost=10), - dict(address="192.0.4.0/24", cost=12) - ] + dict(address="192.0.4.0/24", cost=12), + ], ), ], ), @@ -115,21 +111,21 @@ class TestVyosFirewallRulesModule(TestVyosModule): ) ) commands = [ - "set protocols ospf mpls-te enable", - "set protocols ospf mpls-te router-address '192.0.11.11'", - "set protocols ospf auto-cost reference-bandwidth '2'", - "set protocols ospf log-adjacency-changes 'detail'", - "set protocols ospf area '2'", - "set protocols ospf area 2 authentication plaintext-password", - "set protocols ospf area 2 shortcut enable", - "set protocols ospf area 2 area-type normal", - "set protocols ospf area 4 range 192.0.3.0/24 cost 10", - "set protocols ospf area 4 range 192.0.3.0/24", - "set protocols ospf area 4 range 192.0.4.0/24 cost 12", - "set protocols ospf area 4 range 192.0.4.0/24", - "set protocols ospf area 4 area-type stub default-cost 10", - "set protocols ospf area '4'", - "set protocols ospf area 4 network 192.0.2.0/24" + "set protocols ospf mpls-te enable", + "set protocols ospf mpls-te router-address '192.0.11.11'", + "set protocols ospf auto-cost reference-bandwidth '2'", + "set protocols ospf log-adjacency-changes 'detail'", + "set protocols ospf area '2'", + "set protocols ospf area 2 authentication plaintext-password", + "set protocols ospf area 2 shortcut enable", + "set protocols ospf area 2 area-type normal", + "set protocols ospf area 4 range 192.0.3.0/24 cost 10", + "set protocols ospf area 4 range 192.0.3.0/24", + "set protocols ospf area 4 range 192.0.4.0/24 cost 12", + "set protocols ospf area 4 range 192.0.4.0/24", + "set protocols ospf area 4 area-type stub default-cost 10", + "set protocols ospf area '4'", + "set protocols ospf area 4 network 192.0.2.0/24", ] self.execute_module(changed=True, commands=commands) @@ -142,20 +138,16 @@ class TestVyosFirewallRulesModule(TestVyosModule): area_id="12", area_type=dict(normal=True), authentication="plaintext-password", - shortcut='enable', + shortcut="enable", ), dict( area_id="14", - area_type=dict( - stub=dict(default_cost=20) - ), - network=[ - dict(address="192.0.12.0/24"), - ], + area_type=dict(stub=dict(default_cost=20)), + network=[dict(address="192.0.12.0/24"),], range=[ dict(address="192.0.13.0/24", cost=10), - dict(address="192.0.14.0/24", cost=12) - ] + dict(address="192.0.14.0/24", cost=12), + ], ), ], ), @@ -173,21 +165,19 @@ class TestVyosFirewallRulesModule(TestVyosModule): area_id="12", area_type=dict(normal=True), authentication="plaintext-password", - shortcut='enable', + shortcut="enable", ), dict( area_id="14", - area_type=dict( - stub=dict(set=False) - ), + area_type=dict(stub=dict(set=False)), network=[ dict(address="192.0.12.0/24"), dict(address="192.0.22.0/24"), ], range=[ dict(address="192.0.13.0/24", cost=10), - dict(address="192.0.14.0/24", cost=12) - ] + dict(address="192.0.14.0/24", cost=12), + ], ), ], ), @@ -196,7 +186,7 @@ class TestVyosFirewallRulesModule(TestVyosModule): ) commands = [ "delete protocols ospf area 14 area-type stub", - "set protocols ospf area 14 network 192.0.22.0/24" + "set protocols ospf area 14 network 192.0.22.0/24", ] self.execute_module(changed=True, commands=commands) @@ -205,28 +195,24 @@ class TestVyosFirewallRulesModule(TestVyosModule): dict( config=dict( log_adjacency_changes="detail", - mpls_te=dict(enabled=True, router_address='192.0.11.11'), + mpls_te=dict(enabled=True, router_address="192.0.11.11"), auto_cost=dict(reference_bandwidth=2), areas=[ dict( area_id="12", area_type=dict(normal=True), authentication="plaintext-password", - shortcut='enable', + shortcut="enable", ), dict( area_id="15", - area_type=dict( - stub=dict(default_cost=10) - ), - network=[ - dict(address="192.0.12.0/24"), - ], + area_type=dict(stub=dict(default_cost=10)), + network=[dict(address="192.0.12.0/24"),], range=[ dict(address="192.0.13.0/24", cost=10), dict(address="192.0.14.0/24", cost=12), - dict(address="192.0.15.0/24", cost=14) - ] + dict(address="192.0.15.0/24", cost=14), + ], ), ], ), @@ -247,7 +233,7 @@ class TestVyosFirewallRulesModule(TestVyosModule): "set protocols ospf area 15 range 192.0.15.0/24", "set protocols ospf area 15 area-type stub default-cost 10", "set protocols ospf area '15'", - "set protocols ospf area 15 network 192.0.12.0/24" + "set protocols ospf area 15 network 192.0.12.0/24", ] self.execute_module(changed=True, commands=commands) @@ -260,20 +246,16 @@ class TestVyosFirewallRulesModule(TestVyosModule): area_id="12", area_type=dict(normal=True), authentication="plaintext-password", - shortcut='enable', + shortcut="enable", ), dict( area_id="14", - area_type=dict( - stub=dict(default_cost=20) - ), - network=[ - dict(address="192.0.12.0/24"), - ], + area_type=dict(stub=dict(default_cost=20)), + network=[dict(address="192.0.12.0/24"),], range=[ dict(address="192.0.13.0/24", cost=10), - dict(address="192.0.14.0/24", cost=12) - ] + dict(address="192.0.14.0/24", cost=12), + ], ), ], ), @@ -287,63 +269,30 @@ class TestVyosFirewallRulesModule(TestVyosModule): commands = ["delete protocols ospf"] self.execute_module(changed=True, commands=commands) - def test_vyos_ospfv2_set_01_deleted_single_attributes(self): - set_module_args( - dict( - config=dict( - mpls_te=dict(), - auto_cost=dict(), - areas=[] - ), - state="deleted", - ) - ) - commands = ["delete protocols ospf area 12", - "delete protocols ospf area 14", - "delete protocols ospf area"] - self.execute_module(changed=True, commands=commands) - def test_vyos_ospfv2_gathered(self): set_module_args(dict(state="gathered")) result = self.execute_module( changed=False, filename="vyos_ospfv2_config.cfg" ) gather_dict = { - "areas": [ - { - "area_id": "2", - "area_type": { - "normal": True - }, - "authentication": "plaintext-password", - "shortcut": "enable" - }, - { - "area_id": "14", - "area_type": { - "stub": { - "default_cost": 20, - "set": True - } - }, - "network": [ - { - "address": "192.0.12.0/24" - } - ], - "range": [ - { - "address": "192.0.13.0/24", - "cost": 10 - }, - { - "address": "192.0.14.0/24", - "cost": 12 - } - ] - } - ], - } + "areas": [ + { + "area_id": "2", + "area_type": {"normal": True}, + "authentication": "plaintext-password", + "shortcut": "enable", + }, + { + "area_id": "14", + "area_type": {"stub": {"default_cost": 20, "set": True}}, + "network": [{"address": "192.0.12.0/24"}], + "range": [ + {"address": "192.0.13.0/24", "cost": 10}, + {"address": "192.0.14.0/24", "cost": 12}, + ], + }, + ], + } self.assertEqual(sorted(gather_dict), sorted(result["gathered"])) def test_vyos_ospfv2_parsed(self): @@ -382,54 +331,28 @@ set protocols ospf redistribute bgp metric-type '2'""" "areas": [ { "area_id": "2", - "area_type": { - "normal": True - }, + "area_type": {"normal": True}, "authentication": "plaintext-password", - "shortcut": "enable" - }, - { - "area_id": "3", - "area_type": { - "nssa": { - "set": True - } - } + "shortcut": "enable", }, + {"area_id": "3", "area_type": {"nssa": {"set": True}}}, { "area_id": "4", - "area_type": { - "stub": { - "default_cost": 20, - "set": True - } - }, - "network": [ - { - "address": "192.0.2.0/24" - } - ], + "area_type": {"stub": {"default_cost": 20, "set": True}}, + "network": [{"address": "192.0.2.0/24"}], "range": [ - { - "address": "192.0.3.0/24", - "cost": 10 - }, - { - "address": "192.0.4.0/24", - "cost": 12 - } - ] - } + {"address": "192.0.3.0/24", "cost": 10}, + {"address": "192.0.4.0/24", "cost": 12}, + ], + }, ], - "auto_cost": { - "reference_bandwidth": 2 - }, + "auto_cost": {"reference_bandwidth": 2}, "default_information": { "originate": { "always": True, "metric": 10, "metric_type": 2, - "route_map": "ingress" + "route_map": "ingress", } }, "log_adjacency_changes": "detail", @@ -437,37 +360,27 @@ set protocols ospf redistribute bgp metric-type '2'""" "router_lsa": { "administrative": True, "on_shutdown": 10, - "on_startup": 10 + "on_startup": 10, } }, - "mpls_te": { - "enabled": True, - "router_address": "192.0.11.11" - }, + "mpls_te": {"enabled": True, "router_address": "192.0.11.11"}, "neighbor": [ { "neighbor_id": "192.0.11.12", "poll_interval": 10, - "priority": 2 + "priority": 2, } ], "parameters": { "abr_type": "cisco", "opaque_lsa": True, "rfc1583_compatibility": True, - "router_id": "192.0.1.1" + "router_id": "192.0.1.1", }, - "passive_interface": [ - "eth2", - "eth1" - ], + "passive_interface": ["eth2", "eth1"], "redistribute": [ - { - "metric": 10, - "metric_type": 2, - "route_type": "bgp" - } - ] + {"metric": 10, "metric_type": 2, "route_type": "bgp"} + ], } self.assertEqual(sorted(parsed_list), sorted(result["parsed"])) @@ -476,27 +389,23 @@ set protocols ospf redistribute bgp metric-type '2'""" dict( config=dict( log_adjacency_changes="detail", - mpls_te=dict(enabled=True, router_address='192.0.11.11'), + mpls_te=dict(enabled=True, router_address="192.0.11.11"), auto_cost=dict(reference_bandwidth=2), areas=[ dict( area_id="2", area_type=dict(normal=True), authentication="plaintext-password", - shortcut='enable', + shortcut="enable", ), dict( area_id="4", - area_type=dict( - stub=dict(default_cost=10) - ), - network=[ - dict(address="192.0.2.0/24"), - ], + area_type=dict(stub=dict(default_cost=10)), + network=[dict(address="192.0.2.0/24"),], range=[ dict(address="192.0.3.0/24", cost=10), - dict(address="192.0.4.0/24", cost=12) - ] + dict(address="192.0.4.0/24", cost=12), + ], ), ], ), @@ -518,7 +427,7 @@ set protocols ospf redistribute bgp metric-type '2'""" "set protocols ospf area 4 range 192.0.4.0/24", "set protocols ospf area 4 area-type stub default-cost 10", "set protocols ospf area '4'", - "set protocols ospf area 4 network 192.0.2.0/24" + "set protocols ospf area 4 network 192.0.2.0/24", ] result = self.execute_module(changed=False) self.assertEqual( diff --git a/tests/unit/modules/network/vyos/vyos_module.py b/tests/unit/modules/network/vyos/vyos_module.py index e0ab6992..49d46522 100644 --- a/tests/unit/modules/network/vyos/vyos_module.py +++ b/tests/unit/modules/network/vyos/vyos_module.py @@ -60,7 +60,7 @@ class TestVyosModule(ModuleTestCase): commands=None, sort=True, defaults=False, - filename=None + filename=None, ): self.load_fixtures(commands) -- cgit v1.2.3 From 50211d83f5a5814a7346ad92a459b288b4354fd2 Mon Sep 17 00:00:00 2001 From: Rohit Thakur Date: Mon, 11 May 2020 19:44:47 +0530 Subject: rebased Signed-off-by: Rohit Thakur --- plugins/module_utils/network/vyos/facts/facts.py | 1 + 1 file changed, 1 insertion(+) (limited to 'plugins') diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index f1221f6a..4c7b340d 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -42,6 +42,7 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firew ) 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, ) -- cgit v1.2.3 From 178ddf295e31c326e365bf840471b7d0c010a99a Mon Sep 17 00:00:00 2001 From: Rohit Thakur Date: Mon, 11 May 2020 20:03:54 +0530 Subject: linters fix Signed-off-by: Rohit Thakur --- plugins/module_utils/network/vyos/utils/utils.py | 1 - 1 file changed, 1 deletion(-) (limited to 'plugins') diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py index 8fa2bfd9..7e0f3cc3 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -247,7 +247,6 @@ def _bool_to_str(val): ) - def _is_w_same(w, h, key): """ This function checks whether the key value is same in desired and -- cgit v1.2.3 From dbdefd7223dc2334eb4efcedc3e1481ef9c32576 Mon Sep 17 00:00:00 2001 From: Rohit Thakur Date: Tue, 12 May 2020 11:20:34 +0530 Subject: fix pep8/pycodestyle E741 ambiguous variable name 'l' Signed-off-by: Rohit Thakur --- plugins/modules/vyos_vlan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'plugins') diff --git a/plugins/modules/vyos_vlan.py b/plugins/modules/vyos_vlan.py index a0aafb57..04f5856a 100644 --- a/plugins/modules/vyos_vlan.py +++ b/plugins/modules/vyos_vlan.py @@ -240,8 +240,8 @@ def map_config_to_obj(module): output = run_commands(module, "show interfaces") lines = output[0].strip().splitlines()[3:] - for l in lines: - splitted_line = re.split(r"\s{2,}", l.strip()) + for line in lines: + splitted_line = re.split(r"\s{2,}", line.strip()) obj = {} eth = splitted_line[0].strip("'") -- cgit v1.2.3