diff --git a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py index 1b11d3c..a2a6e04 100644 --- a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py @@ -1,359 +1,365 @@ # # -*- 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': { + 'areas': { 'elements': 'dict', 'options': { - 'area': { + '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' } }, 'type': 'dict' }, 'stub': { 'options': { 'default_cost': { 'type': 'int' }, 'no_summary': { 'type': 'bool' + }, + 'set': { + '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': { + 'elements': 'dict', 'options': { 'key_id': { 'type': 'int' }, 'md5_key': { 'type': 'str' } }, - 'type': 'dict' + 'type': 'list' }, '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' + 'type': 'dict' }, "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/ospfv2.py b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py index 0109ca1..13645cd 100644 --- a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py @@ -1,570 +1,729 @@ # # -*- 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_subset = ['!all', '!min'] - gather_network_resources = [ - 'ospfv2', - ] + 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) + + (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": + 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): """ 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": + 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): """ 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)) + 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): """ 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)) + commands.extend(self._render_ospf_param(want, have)) 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]: + 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 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" - ) + 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: 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"): + 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): """ 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")} + '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)) 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("_","-") + " " + 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']) + 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): """ 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") - } + 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: 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): + 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): """ 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"), - } + '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: 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]): + 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. :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") + 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)) 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("_","-")) + 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): """ 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 " + 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 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("_","-") + + 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 3457fac..0467b72 100644 --- a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py @@ -1,395 +1,464 @@ # # -*- 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 __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) 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 = [] + + 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): """ 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"] + + 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): """ 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 + cfg_dict = self.parse_attr(conf, ['timers'], match=attrib) + return cfg_dict 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) + 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") + + 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): + 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): """ 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) + + 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): """ 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) + + 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 :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") + + 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): """ 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("'")} + 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 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") + + 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): """ 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"] + '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 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 + 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: config[attrib] = False else: config[attrib] = True 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): 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("_","-") + + return ('disable' if attrib == 'disabled' else ('enable' + if attrib == 'enabled' else ('area' if attrib + == 'area_id' 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") + + 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): """ 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") + + 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 c7dc575..c539beb 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -1,264 +1,263 @@ # -*- 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) # utils 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, ) def search_obj_in_list(name, lst, key="name"): if lst: for item in lst: if item[key] == name: return item return None def get_interface_type(interface): """Gets the type of interface """ if interface.startswith("eth"): return "ethernet" elif interface.startswith("bond"): return "bonding" elif interface.startswith("vti"): return "vti" elif interface.startswith("lo"): return "loopback" def dict_delete(base, comparable): """ This function generates a dict containing key, value pairs for keys that are present in the `base` dict but not present in the `comparable` dict. :param base: dict object to base the diff on :param comparable: dict object to compare against base :returns: new dict object with key, value pairs that needs to be deleted. """ to_delete = dict() for key in base: if isinstance(base[key], dict): sub_diff = dict_delete(base[key], comparable.get(key, {})) if sub_diff: to_delete[key] = sub_diff else: if key not in comparable: to_delete[key] = base[key] return to_delete def diff_list_of_dicts(want, have): diff = [] set_w = set(tuple(d.items()) for d in want) set_h = set(tuple(d.items()) for d in have) difference = set_w.difference(set_h) for element in difference: diff.append(dict((x, y) for x, y in element)) return diff def get_lst_diff_for_dicts(want, have, lst): """ This function generates a list containing values that are only in want and not in list in have dict :param want: dict object to want :param have: dict object to have :param lst: list the diff on :return: new list object with values which are only in want. """ if not have: diff = want.get(lst) or [] else: want_elements = want.get(lst) or {} have_elements = have.get(lst) or {} diff = list_diff_want_only(want_elements, have_elements) return diff def get_lst_same_for_dicts(want, have, lst): """ This function generates a list containing values that are common for list in want and list in have dict :param want: dict object to want :param have: dict object to have :param lst: list the comparison on :return: new list object with values which are common in want and have. """ diff = None if want and have: want_list = want.get(lst) or {} have_list = have.get(lst) or {} diff = [ i for i in want_list and have_list if i in have_list and i in want_list ] return diff 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: :param have_list: :return: new list with values which are only in have list """ if have_list and not want_list: diff = have_list elif not have_list: diff = None else: diff = [ i for i in have_list + want_list if i in have_list and i not in want_list ] return diff def list_diff_want_only(want_list, have_list): """ This function generated the list containing values that are only in want list. :param want_list: :param have_list: :return: new list with values which are only in want list """ if have_list and not want_list: diff = None elif not have_list: diff = want_list else: diff = [ i for i in have_list + want_list if i in want_list and i not in have_list ] return diff def search_dict_tv_in_list(d_val1, d_val2, lst, key1, key2): """ This function return the dict object if it exist in list. :param d_val1: :param d_val2: :param lst: :param key1: :param key2: :return: """ obj = next( ( item for item in lst if item[key1] == d_val1 and item[key2] == d_val2 ), None, ) if obj: return obj else: return None def key_value_in_dict(have_key, have_value, want_dict): """ This function checks whether the key and values exist in dict :param have_key: :param have_value: :param want_dict: :return: """ for key, value in iteritems(want_dict): if key == have_key and value == have_value: return True return False def is_dict_element_present(dict, key): """ This function checks whether the key is present in dict. :param dict: :param key: :return: """ for item in dict: if item == key: return True return False def get_ip_address_version(address): """ This function returns the version of IP address :param address: IP address :return: """ try: address = unicode(address) except NameError: address = str(address) version = ipaddress.ip_address(address.split("/")[0]).version return version def get_route_type(address): """ This function returns the route type based on IP address :param address: :return: """ version = get_ip_address_version(address) if version == 6: return "route6" elif version == 4: return "route" def _bool_to_str(val): """ This function converts the bool value into string. :param val: bool value. :return: enable/disable. """ return "enable" if str(val) == "True" else "disable" if str(val) == "False" else val def _is_w_same(w, h, key): """ This function checks whether the key value is same in desired and target config dictionary. :param w: base config. :param h: target config. :param key:attribute name. :return: True/False. """ return True if h and key in h and h[key] == w[key] else False def _in_target(h, key): """ This function checks whether the target exist and key present in target config. :param h: target config. :param key: attribute name. :return: True/False. """ return True if h and key in h else False diff --git a/plugins/modules/vyos_ospfv2.py b/plugins/modules/vyos_ospfv2.py index 73528eb..866ed76 100644 --- a/plugins/modules/vyos_ospfv2.py +++ b/plugins/modules/vyos_ospfv2.py @@ -1,1369 +1,1981 @@ #!/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) +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: description: Area type. 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 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. + description: Stub OSPFv2 area. type: dict suboptions: + set: + description: Enabling stub. + type: bool 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. + 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). 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. + 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. 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. + description: OSPFv2 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. + description: Calculate OSPFv2 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. + 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. type: str default_metric: description: Metric of redistributed routes type: int distance: description: Administrative distance. 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: 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. + 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: 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. + description: OSPFv2 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. + description: OSPFv2 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 + 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. type: str choices: - merged - replaced - deleted - parsed - gathered - rendered default: merged """ EXAMPLES = """ # Using merged # # 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 # # # ------------------------- # 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 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 # # 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: +# 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 # # # ------------------------- # 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 """ 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. 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'] """ 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() 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 0000000..852a6be --- /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 0000000..7413320 --- /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 0000000..93eb2fe --- /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 0000000..a3db933 --- /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 0000000..9cc720b --- /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 0000000..9f358d5 --- /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 0000000..7360870 --- /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 0000000..150242a --- /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 0000000..a61f5a7 --- /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 0000000..4566bf4 --- /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 0000000..22c378b --- /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 0000000..6a58bb5 --- /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 0000000..453dbb0 --- /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 0000000..6fbe2f9 --- /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 0000000..86c07cf --- /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 0000000..07606f9 --- /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 0000000..7efc2a7 --- /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 0000000..76a54e3 --- /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 0000000..297671b --- /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 0000000..8e6b095 --- /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 49d4652..e0ab699 100644 --- a/tests/unit/modules/network/vyos/vyos_module.py +++ b/tests/unit/modules/network/vyos/vyos_module.py @@ -1,105 +1,105 @@ # (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 import os import json from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( AnsibleExitJson, AnsibleFailJson, ModuleTestCase, ) fixture_path = os.path.join(os.path.dirname(__file__), "fixtures") fixture_data = {} def load_fixture(name): path = os.path.join(fixture_path, name) if path in fixture_data: return fixture_data[path] with open(path) as f: data = f.read() try: data = json.loads(data) except Exception: pass fixture_data[path] = data return data class TestVyosModule(ModuleTestCase): def execute_module( self, failed=False, changed=False, commands=None, sort=True, defaults=False, - filename=None, + filename=None ): self.load_fixtures(commands) if failed: result = self.failed() self.assertTrue(result["failed"], result) else: result = self.changed(changed) self.assertEqual(result["changed"], changed, result) if commands is not None: if sort: self.assertEqual( sorted(commands), sorted(result["commands"]), result["commands"], ) else: self.assertEqual( commands, result["commands"], result["commands"] ) return result def failed(self): with self.assertRaises(AnsibleFailJson) as exc: self.module.main() result = exc.exception.args[0] self.assertTrue(result["failed"], result) return result def changed(self, changed=False): with self.assertRaises(AnsibleExitJson) as exc: self.module.main() result = exc.exception.args[0] self.assertEqual(result["changed"], changed, result) return result def load_fixtures(self, commands=None): pass