diff --git a/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py index de972bc..acda380 100644 --- a/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py +++ b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py @@ -1,465 +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_ospfv3 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, search_obj_in_list, ) 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 Ospfv3(ConfigBase): """ The vyos_ospfv3 class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "ospfv3", ] def __init__(self, module): super(Ospfv3, self).__init__(module) def get_ospfv3_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 ) ospfv3_facts = facts["ansible_network_resources"].get("ospfv3", {}) return ospfv3_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_ospfv3_facts = self.get_ospfv3_facts() else: existing_ospfv3_facts = {} if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_ospfv3_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_ospfv3_facts = self.get_ospfv3_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed" ) result["parsed"] = self.get_ospfv3_facts(data=running_config) else: changed_ospfv3_facts = {} if self.state in self.ACTION_STATES: result["before"] = existing_ospfv3_facts if result["changed"]: result["after"] = changed_ospfv3_facts elif self.state == "gathered": result["gathered"] = changed_ospfv3_facts result["warnings"] = warnings return result def set_config(self, existing_ospfv3_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_ospfv3_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 == "deleted": commands.extend(self._state_deleted(w, 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 _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 = [] if have: 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 = [] 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 have: commands.append("delete protocols ospfv3") 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)) if w: for key, val in iteritems(w): 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 == "areas": commands.extend(self._render_areas(key, w, h, opr=opr)) elif key == "parameters": commands.extend(self._render_dict_param(key, w, h, opr=opr)) elif key == "redistribute": commands.extend(self._render_list_dict_param(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 = {"parameters": "router_id"} leaf = leaf_dict[attr] for item, value in iteritems(want[attr]): if ( opr and item in leaf and not _is_w_same(want[attr], h, item) ): 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): commands.append( self._form_attr_cmd(key=attr, attr=item, opr=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", "range": "address", } leaf_dict = { "redistribute": ("route_map", "route_type"), "range": ("address", "advertise", "not_advertise"), } leaf = leaf_dict[attr] w = want.get(attr) or [] if have: h = have.get(attr) or [] if not opr and not h: commands.append(self._compute_command(attr=attr, opr=opr)) elif w: for w_item in w: for key, val in iteritems(w_item): if not cmd: cmd = self._compute_command(opr=opr) h_item = search_obj_in_list( w_item[name[attr]], h, name[attr] ) if ( opr and key in leaf and not _is_w_same(w_item, h_item, key) ): if key == "route_type" or ( key == "address" and "advertise" not in w_item and "not-advertise" not in w_item ): if not val: cmd = cmd.replace("set", "delete") commands.append(cmd + attr + " " + str(val)) elif key in leaf_dict["range"] and key != "address": commands.append( cmd + attr + " " + w_item[name[attr]] + " " + key.replace("_", "-") ) elif key == "route_map": 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", "address"): commands.append(cmd + attr + " " + str(val)) else: commands.append( cmd + (attr + " " + w_item[name[attr]] + " " + key) ) return commands 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_id", "export_list", "import_list") 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_id"]), opr=opr, ) + " " ) h_area = search_obj_in_list( w_area["area_id"], h_lst, "area_id" ) if not opr and not h_area: commands.append( self._form_attr_cmd( key="area", attr=w_area["area_id"], opr=opr ) ) else: for key, val in iteritems(w_area): if ( opr and key in l_set and not _is_w_same(w_area, h_area, key) ): if key == "area_id": commands.append( self._form_attr_cmd( attr="area", val=_bool_to_str(val), opr=opr, ) ) else: commands.append( cmd + key.replace("_", "-") + " " + _bool_to_str(val).replace("_", "-") ) elif not opr and key in l_set: if key == "area_id" and not _in_target( h_area, key ): commands.append(cmd) continue elif key != "area_id" and not _in_target( h_area, key ): commands.append(cmd + val + " " + key) elif key == "range": commands.extend( self._render_list_dict_param( key, w_area, h_area, cmd, opr ) ) 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 ospfv3 " else: cmd = "set protocols ospfv3 " if key: cmd += key.replace("_", "-") + " " if attr: cmd += attr.replace("_", "-") if val and opr: cmd += " '" + str(val) + "'" return cmd def _map_attrib(self, attrib): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ return "disable" if attrib == "disabled" else attrib.replace("_", "-") diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py index f2986aa..96feddd 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -1,270 +1,269 @@ # -*- 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_ospfv3.py b/plugins/modules/vyos_ospfv3.py index ae93500..e2d3ff7 100644 --- a/plugins/modules/vyos_ospfv3.py +++ b/plugins/modules/vyos_ospfv3.py @@ -1,670 +1,667 @@ #!/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_ospfv3 """ from __future__ import absolute_import, division, print_function __metaclass__ = type ANSIBLE_METADATA = {"metadata_version": "1.1", "supported_by": "Ansible"} DOCUMENTATION = """ --- module: vyos_ospfv3 version_added: 2.10 short_description: OSPFV3 resource module. description: This resource module configures and manages attributes of OSPFv3 routes on VyOS network devices. version_added: "1.0.0" notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Rohit Thakur (@rohitthakur2590) options: config: description: A provided OSPFv3 route configuration. type: dict suboptions: areas: description: OSPFv3 area. type: list elements: dict suboptions: area_id: description: OSPFv3 Area name/identity. type: str export_list: description: Name of export-list. type: str import_list: description: Name of import-list. type: str range: description: Summarize routes matching prefix (border routers only). type: list elements: dict suboptions: address: description: border router IPv4 address. type: str advertise: description: Advertise this range. type: bool not_advertise: description: Don't advertise this range. type: bool parameters: descriptions: OSPFv3 specific parameters. type: dict - suboptions: - router_id: + suboptions: + router_id: description: Override the default router identifier. type: str - redistribute: + 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', 'ripng', 'static'] + choices: ['bgp', 'connected', 'kernel', 'ripng', 'static'] route_map: description: Route map references. - type: str + type: str running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VyOS device by executing the command B(show configuration commands | grep ospfv3). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state the configuration should be left in. type: str choices: - merged - replaced - deleted - parsed - gathered - rendered default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@vyos# run show configuration commands | grep ospfv3 # # - name: Merge the provided configuration with the exisiting running configuration - vyos.vyos.vyos_ospfv3: - config: - redistribute: - - route_type: 'bgp' - parameters: - router_id: '192.0.2.10' - areas: - - area_id: '2' - export_list: 'export1' - import_list: 'import1' - - range: - - address: '2001:db10::/32' - - address: '2001:db20::/32' - - address: '2001:db30::/32' - - area_id: '3' - range: - - address: '2001:db40::/32' - state: merged + vyos.vyos.vyos_ospfv3: + config: + redistribute: + - route_type: 'bgp' + parameters: + router_id: '192.0.2.10' + areas: + - area_id: '2' + export_list: 'export1' + import_list: 'import1' + range: + - address: '2001:db10::/32' + - address: '2001:db20::/32' + - address: '2001:db30::/32' + - area_id: '3' + range: + - address: '2001:db40::/32' + state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": {} # # "commands": [ # "set protocols ospfv3 redistribute bgp", # "set protocols ospfv3 parameters router-id '192.0.2.10'", # "set protocols ospfv3 area 2 range 2001:db10::/32", # "set protocols ospfv3 area 2 range 2001:db20::/32", # "set protocols ospfv3 area 2 range 2001:db30::/32", # "set protocols ospfv3 area '2'", # "set protocols ospfv3 area 2 export-list export1", # "set protocols ospfv3 area 2 import-list import1", # "set protocols ospfv3 area '3'", # "set protocols ospfv3 area 3 range 2001:db40::/32" # ] # # "after": { # "areas": [ # { # "area_id": "2", # "export_list": "export1", # "import_list": "import1", # "range": [ # { # "address": "2001:db10::/32" # }, # { # "address": "2001:db20::/32" # }, # { # "address": "2001:db30::/32" # } # ] # }, # { # "area_id": "3", # "range": [ # { # "address": "2001:db40::/32" # } # ] # } # ], # "parameters": { # "router_id": "192.0.2.10" # }, # "redistribute": [ # { # "route_type": "bgp" # } # ] # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep ospfv3 # set protocols ospfv3 area 2 export-list 'export1' # set protocols ospfv3 area 2 import-list 'import1' # set protocols ospfv3 area 2 range '2001:db10::/32' # set protocols ospfv3 area 2 range '2001:db20::/32' # set protocols ospfv3 area 2 range '2001:db30::/32' # set protocols ospfv3 area 3 range '2001:db40::/32' # set protocols ospfv3 parameters router-id '192.0.2.10' # set protocols ospfv3 redistribute 'bgp' # Using replaced # # Before state: # ------------- # # vyos@192# run show configuration commands | grep ospfv3 # set protocols ospfv3 area 2 export-list 'export1' # set protocols ospfv3 area 2 import-list 'import1' # set protocols ospfv3 area 2 range '2001:db10::/32' # set protocols ospfv3 area 2 range '2001:db20::/32' # set protocols ospfv3 area 2 range '2001:db30::/32' # set protocols ospfv3 area 3 range '2001:db40::/32' # set protocols ospfv3 parameters router-id '192.0.2.10' # set protocols ospfv3 redistribute 'bgp' # - name: Replace ospfv3 routes attributes configuration. - vyos.vyos.vyos_ospfv3: - config: - redistribute: - - route_type: 'bgp' - parameters: - router_id: '192.0.2.10' - areas: - - area_id: '2' - export_list: 'export1' - import_list: 'import1' - - range: - - address: '2001:db10::/32' - - address: '2001:db30::/32' - - address: '2001:db50::/32' - - area_id: '4' - range: - - address: '2001:db60::/32' - state: replaced + vyos.vyos.vyos_ospfv3: + config: + redistribute: + - route_type: 'bgp' + parameters: + router_id: '192.0.2.10' + areas: + - area_id: '2' + export_list: 'export1' + import_list: 'import1' + range: + - address: '2001:db10::/32' + - address: '2001:db30::/32' + - address: '2001:db50::/32' + - area_id: '4' + range: + - address: '2001:db60::/32' + state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": { # "areas": [ # { # "area_id": "2", # "export_list": "export1", # "import_list": "import1", # "range": [ # { # "address": "2001:db10::/32" # }, # { # "address": "2001:db20::/32" # }, # { # "address": "2001:db30::/32" # } # ] # }, # { # "area_id": "3", # "range": [ # { # "address": "2001:db40::/32" # } # ] # } # ], # "parameters": { # "router_id": "192.0.2.10" # }, # "redistribute": [ # { # "route_type": "bgp" # } # ] # } # # "commands": [ # "delete protocols ospfv3 area 2 range 2001:db20::/32", # "delete protocols ospfv3 area 3", # "set protocols ospfv3 area 2 range 2001:db50::/32", # "set protocols ospfv3 area '4'", # "set protocols ospfv3 area 4 range 2001:db60::/32" # ] # # "after": { # "areas": [ # { # "area_id": "2", # "export_list": "export1", # "import_list": "import1", # "range": [ # { # "address": "2001:db10::/32" # }, # { # "address": "2001:db30::/32" # }, # { # "address": "2001:db50::/32" # } # ] # }, # { # "area_id": "4", # "range": [ # { # "address": "2001:db60::/32" # } # ] # } # ], # "parameters": { # "router_id": "192.0.2.10" # }, # "redistribute": [ # { # "route_type": "bgp" # } # ] # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep ospfv3 # set protocols ospfv3 area 2 export-list 'export1' # set protocols ospfv3 area 2 import-list 'import1' # set protocols ospfv3 area 2 range '2001:db10::/32' # set protocols ospfv3 area 2 range '2001:db30::/32' # set protocols ospfv3 area 2 range '2001:db50::/32' # set protocols ospfv3 area 4 range '2001:db60::/32' # set protocols ospfv3 parameters router-id '192.0.2.10' # set protocols ospfv3 redistribute 'bgp' # Using rendered # # - name: Render the commands for provided configuration - vyos.vyos.vyos_ospfv3: - config: - redistribute: - - route_type: 'bgp' - parameters: - router_id: '192.0.2.10' - areas: - - area_id: '2' - export_list: 'export1' - import_list: 'import1' - - range: - - address: '2001:db10::/32' - - address: '2001:db20::/32' - - address: '2001:db30::/32' - - area_id: '3' - range: - - address: '2001:db40::/32' - state: rendered + vyos.vyos.vyos_ospfv3: + config: + redistribute: + - route_type: 'bgp' + parameters: + router_id: '192.0.2.10' + areas: + - area_id: '2' + export_list: 'export1' + import_list: 'import1' + range: + - address: '2001:db10::/32' + - address: '2001:db20::/32' + - address: '2001:db30::/32' + - area_id: '3' + range: + - address: '2001:db40::/32' + state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # [ # "set protocols ospfv3 redistribute bgp", # "set protocols ospfv3 parameters router-id '192.0.2.10'", # "set protocols ospfv3 area 2 range 2001:db10::/32", # "set protocols ospfv3 area 2 range 2001:db20::/32", # "set protocols ospfv3 area 2 range 2001:db30::/32", # "set protocols ospfv3 area '2'", # "set protocols ospfv3 area 2 export-list export1", # "set protocols ospfv3 area 2 import-list import1", # "set protocols ospfv3 area '3'", # "set protocols ospfv3 area 3 range 2001:db40::/32" # ] # Using parsed # # - name: Parse the commands to provide structured configuration. - vyos.vyos.vyos_ospfv3: - running_config: - "set protocols ospfv3 area 2 export-list 'export1' + vyos.vyos.vyos_ospfv3: + running_config: + "set protocols ospfv3 area 2 export-list 'export1' set protocols ospfv3 area 2 import-list 'import1' set protocols ospfv3 area 2 range '2001:db10::/32' set protocols ospfv3 area 2 range '2001:db20::/32' set protocols ospfv3 area 2 range '2001:db30::/32' set protocols ospfv3 area 3 range '2001:db40::/32' set protocols ospfv3 parameters router-id '192.0.2.10' set protocols ospfv3 redistribute 'bgp'" - state: parsed + state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": { # "areas": [ # { # "area_id": "2", # "export_list": "export1", # "import_list": "import1", # "range": [ # { # "address": "2001:db10::/32" # }, # { # "address": "2001:db20::/32" # }, # { # "address": "2001:db30::/32" # } # ] # }, # { # "area_id": "3", # "range": [ # { # "address": "2001:db40::/32" # } # ] # } # ], # "parameters": { # "router_id": "192.0.2.10" # }, # "redistribute": [ # { # "route_type": "bgp" # } # ] # } # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep ospfv3 # set protocols ospfv3 area 2 export-list 'export1' # set protocols ospfv3 area 2 import-list 'import1' # set protocols ospfv3 area 2 range '2001:db10::/32' # set protocols ospfv3 area 2 range '2001:db20::/32' # set protocols ospfv3 area 2 range '2001:db30::/32' # set protocols ospfv3 area 3 range '2001:db40::/32' # set protocols ospfv3 parameters router-id '192.0.2.10' # set protocols ospfv3 redistribute 'bgp' # - name: Gather ospfv3 routes config with provided configurations - vyos.vyos.vyos_ospfv3: - config: - state: gathered + vyos.vyos.vyos_ospfv3: + config: + state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": { # "areas": [ # { # "area_id": "2", # "export_list": "export1", # "import_list": "import1", # "range": [ # { # "address": "2001:db10::/32" # }, # { # "address": "2001:db20::/32" # }, # { # "address": "2001:db30::/32" # } # ] # }, # { # "area_id": "3", # "range": [ # { # "address": "2001:db40::/32" # } # ] # } # ], # "parameters": { # "router_id": "192.0.2.10" # }, # "redistribute": [ # { # "route_type": "bgp" # } # ] # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep ospfv3 # set protocols ospfv3 area 2 export-list 'export1' # set protocols ospfv3 area 2 import-list 'import1' # set protocols ospfv3 area 2 range '2001:db10::/32' # set protocols ospfv3 area 2 range '2001:db20::/32' # set protocols ospfv3 area 2 range '2001:db30::/32' # set protocols ospfv3 area 3 range '2001:db40::/32' # set protocols ospfv3 parameters router-id '192.0.2.10' # set protocols ospfv3 redistribute 'bgp' # Using deleted # # Before state # ------------- # # vyos@192# run show configuration commands | grep ospfv3 # set protocols ospfv3 area 2 export-list 'export1' # set protocols ospfv3 area 2 import-list 'import1' # set protocols ospfv3 area 2 range '2001:db10::/32' # set protocols ospfv3 area 2 range '2001:db20::/32' # set protocols ospfv3 area 2 range '2001:db30::/32' # set protocols ospfv3 area 3 range '2001:db40::/32' # set protocols ospfv3 parameters router-id '192.0.2.10' # set protocols ospfv3 redistribute 'bgp' # - name: Delete attributes of ospfv3 routes. - vyos.vyos.vyos_ospfv3: - config: - state: deleted + vyos.vyos.vyos_ospfv3: + config: + state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": { # "areas": [ # { # "area_id": "2", # "export_list": "export1", # "import_list": "import1", # "range": [ # { # "address": "2001:db10::/32" # }, # { # "address": "2001:db20::/32" # }, # { # "address": "2001:db30::/32" # } # ] # }, # { # "area_id": "3", # "range": [ # { # "address": "2001:db40::/32" # } # ] # } # ], # "parameters": { # "router_id": "192.0.2.10" # }, # "redistribute": [ # { # "route_type": "bgp" # } # ] # } # "commands": [ # "delete protocols ospfv3" # ] # # "after": {} # After state # ------------ # vyos@192# run show configuration commands | grep ospfv3 """ 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: ['set protocols ospf parameters router-id 192.0.1.1', 'set protocols ospfv3 area 2 range '2001:db10::/32'] """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv3.ospfv3 import ( Ospfv3Args, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv3.ospfv3 import ( Ospfv3, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), ("state", "rendered", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Ospfv3Args.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Ospfv3(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/tests/integration/targets/vyos_ospfv3/tests/cli/deleted.yaml b/tests/integration/targets/vyos_ospfv3/tests/cli/deleted.yaml index d400ff1..55bec18 100644 --- a/tests/integration/targets/vyos_ospfv3/tests/cli/deleted.yaml +++ b/tests/integration/targets/vyos_ospfv3/tests/cli/deleted.yaml @@ -1,48 +1,48 @@ --- - debug: msg: Start vyos_ospfv3 deleted integration tests ansible_connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - - name: Delete attributes of firewall. + - name: Delete ospfv3 routes register: result vyos.vyos.vyos_ospfv3: &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) + - name: Delete ospfv3 routes (IDEMPOTENT) register: result vyos.vyos.vyos_ospfv3: *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_ospfv3/tests/cli/merged_update.yaml b/tests/integration/targets/vyos_ospfv3/tests/cli/merged_update.yaml index a65cfaf..0d506b3 100644 --- a/tests/integration/targets/vyos_ospfv3/tests/cli/merged_update.yaml +++ b/tests/integration/targets/vyos_ospfv3/tests/cli/merged_update.yaml @@ -1,61 +1,61 @@ --- - debug: msg: START vyos_ospfv3 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_ospfv3: &id001 config: areas: - area_id: '2' range: - address: '2001:db10::/32' - area_id: '3' range: - address: '2001:db40::/32' - address: '2001:db70::/32' parameters: router_id: '192.0.2.10' redistribute: - route_type: 'bgp' state: merged - name: Assert that before dicts were correctly generated assert: that: "{{ populate == 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_ospfv3: *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 + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_ospfv3/vars/main.yaml b/tests/integration/targets/vyos_ospfv3/vars/main.yaml index d384e2a..6ded89a 100644 --- a/tests/integration/targets/vyos_ospfv3/vars/main.yaml +++ b/tests/integration/targets/vyos_ospfv3/vars/main.yaml @@ -1,140 +1,142 @@ --- merged: before: {} commands: - set protocols ospfv3 redistribute bgp - set protocols ospfv3 parameters router-id '192.0.2.10' - set protocols ospfv3 area 2 range 2001:db10::/32 - set protocols ospfv3 area 2 range 2001:db20::/32 - set protocols ospfv3 area 2 range 2001:db30::/32 - set protocols ospfv3 area '2' - set protocols ospfv3 area 2 export-list export1 - set protocols ospfv3 area 2 import-list import1 - set protocols ospfv3 area '3' - set protocols ospfv3 area 3 range 2001:db40::/32 after: - areas: - - area_id: '2' - export_list: 'export1' - import_list: 'import1' - range: - - address: '2001:db10::/32' - - address: '2001:db20::/32' - - address: '2001:db30::/32' - - area_id: '3' - range: - - address: '2001:db40::/32' - parameters: - router_id: '192.0.2.10' - redistribute: - - route_type: 'bgp' + areas: + - area_id: '2' + export_list: 'export1' + import_list: 'import1' + range: + - address: '2001:db10::/32' + - address: '2001:db20::/32' + - address: '2001:db30::/32' + - area_id: '3' + range: + - address: '2001:db40::/32' + parameters: + router_id: '192.0.2.10' + redistribute: + - route_type: 'bgp' + merged_update: commands: - set protocols ospfv3 area 3 range 2001:db70::/32 after: - areas: - - area_id: '2' - export_list: 'export1' - import_list: 'import1' - range: - - address: '2001:db10::/32' - - address: '2001:db20::/32' - - address: '2001:db30::/32' - - area_id: '3' - range: - - address: '2001:db40::/32' - - address: '2001:db70::/32' - parameters: - router_id: '192.0.2.10' - redistribute: - - route_type: 'bgp' + areas: + - area_id: '2' + export_list: 'export1' + import_list: 'import1' + range: + - address: '2001:db10::/32' + - address: '2001:db20::/32' + - address: '2001:db30::/32' + - area_id: '3' + range: + - address: '2001:db40::/32' + - address: '2001:db70::/32' + parameters: + router_id: '192.0.2.10' + redistribute: + - route_type: 'bgp' + populate: areas: - area_id: '2' export_list: 'export1' import_list: 'import1' range: - address: '2001:db10::/32' - address: '2001:db20::/32' - address: '2001:db30::/32' - area_id: '3' range: - address: '2001:db40::/32' parameters: router_id: '192.0.2.10' redistribute: - route_type: 'bgp' replaced: commands: - delete protocols ospfv3 area 2 range 2001:db20::/32 - delete protocols ospfv3 area 3 - set protocols ospfv3 area 2 range 2001:db50::/32 - set protocols ospfv3 area '4' - set protocols ospfv3 area 4 range 2001:db60::/32 after: areas: - area_id: '2' export_list: 'export1' import_list: 'import1' range: - address: '2001:db10::/32' - address: '2001:db30::/32' - address: '2001:db50::/32' - area_id: '4' range: - address: '2001:db60::/32' parameters: router_id: '192.0.2.10' redistribute: - route_type: 'bgp' rendered: commands: - set protocols ospfv3 redistribute bgp - set protocols ospfv3 parameters router-id '192.0.2.10' - set protocols ospfv3 area 2 range 2001:db10::/32 - set protocols ospfv3 area 2 range 2001:db20::/32 - set protocols ospfv3 area 2 range 2001:db30::/32 - set protocols ospfv3 area '2' - set protocols ospfv3 area 2 export-list export1 - set protocols ospfv3 area 2 import-list import1 - set protocols ospfv3 area '3' - set protocols ospfv3 area 3 range 2001:db40::/32 parsed: after: areas: - area_id: '2' export_list: 'export1' import_list: 'import1' range: - address: '2001:db10::/32' - address: '2001:db20::/32' - address: '2001:db30::/32' - area_id: '3' range: - address: '2001:db40::/32' parameters: router_id: '192.0.2.10' redistribute: - route_type: 'bgp' deleted: commands: - 'delete protocols ospfv3' after: {} round_trip: after: areas: - area_id: '2' export_list: 'export1' import_list: 'import1' range: - address: '2001:db10::/32' - address: '2001:db30::/32' - address: '2001:db50::/32' - area_id: '4' range: - address: '2001:db60::/32' parameters: router_id: '192.0.2.10' redistribute: - route_type: 'bgp'