diff --git a/changelogs/fragments/T8511-black-formatting.yml b/changelogs/fragments/T8511-black-formatting.yml new file mode 100644 index 00000000..68a0db8d --- /dev/null +++ b/changelogs/fragments/T8511-black-formatting.yml @@ -0,0 +1,3 @@ +--- +trivial: + - Apply black formatting across the entire codebase to enforce consistent code style. diff --git a/plugins/action/vyos.py b/plugins/action/vyos.py index 148d7c64..df8e7127 100644 --- a/plugins/action/vyos.py +++ b/plugins/action/vyos.py @@ -1,55 +1,53 @@ # # (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 . # from __future__ import absolute_import, division, print_function - __metaclass__ = type from ansible.utils.display import Display from ansible_collections.ansible.netcommon.plugins.action.network import ( ActionModule as ActionNetworkModule, ) - display = Display() class ActionModule(ActionNetworkModule): def run(self, tmp=None, task_vars=None): del tmp # tmp no longer has any effect module_name = self._task.action.split(".")[-1] self._config_module = True if module_name in ["vyos_config", "config"] else False persistent_connection = self._play_context.connection.split(".")[-1] warnings = [] if persistent_connection != "network_cli": return { "failed": True, "msg": "Connection type %s is not valid for this module" % self._play_context.connection, } result = super(ActionModule, self).run(task_vars=task_vars) if warnings: if "warnings" in result: result["warnings"].extend(warnings) else: result["warnings"] = warnings return result diff --git a/plugins/cliconf/vyos.py b/plugins/cliconf/vyos.py index 17fcdf24..96c24c15 100644 --- a/plugins/cliconf/vyos.py +++ b/plugins/cliconf/vyos.py @@ -1,357 +1,356 @@ # (c) 2017 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 . # from __future__ import absolute_import, division, print_function - __metaclass__ = type DOCUMENTATION = """ author: Ansible Networking Team (@ansible-network) name: vyos short_description: Use vyos cliconf to run command on VyOS platform description: - This vyos plugin provides low level abstraction apis for sending and receiving CLI commands from VyOS network devices. version_added: 1.0.0 options: config_commands: description: - Specifies a list of commands that can make configuration changes to the target device. - When `ansible_network_single_user_mode` is enabled, if a command sent to the device is present in this list, the existing cache is invalidated. version_added: 2.0.0 type: list elements: str default: [] vars: - name: ansible_vyos_config_commands """ import json import re from ansible.errors import AnsibleConnectionFailure from ansible.module_utils._text import to_text from ansible.module_utils.common._collections_compat import Mapping from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( NetworkConfig, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list from ansible_collections.ansible.netcommon.plugins.plugin_utils.cliconf_base import CliconfBase class Cliconf(CliconfBase): __rpc__ = CliconfBase.__rpc__ + [ "commit", "discard_changes", "get_diff", "run_commands", ] def __init__(self, *args, **kwargs): super(Cliconf, self).__init__(*args, **kwargs) self._device_info = {} def get_device_info(self): if not self._device_info: device_info = {} device_info["network_os"] = "vyos" reply = self.get("show version") data = to_text(reply, errors="surrogate_or_strict").strip() match = re.search(r"Version:\s*(.*)", data) if match: device_info["network_os_version"] = match.group(1) if device_info["network_os_version"]: match = re.search(r"VyOS\s*(\d+\.\d+)", device_info["network_os_version"]) if match: device_info["network_os_major_version"] = match.group(1) match = re.search(r"(?:HW|Hardware) model:\s*(\S+)", data) if match: device_info["network_os_model"] = match.group(1) reply = self.get("show host name") device_info["network_os_hostname"] = to_text( reply, errors="surrogate_or_strict", ).strip() self._device_info = device_info return self._device_info def get_config(self, flags=None, format=None): if format: option_values = self.get_option_values() if format not in option_values["format"]: raise ValueError( "'format' value %s is invalid. Valid values of format are %s" % (format, ", ".join(option_values["format"])), ) if not flags: flags = [] if format == "text": command = "show configuration" else: command = "show configuration commands" command += " ".join(to_list(flags)) command = command.strip() out = self.send_command(command) return out def edit_config( self, candidate=None, commit=True, replace=None, diff=False, comment=None, confirm=None ): resp = {} operations = self.get_device_operations() self.check_edit_config_capability(operations, candidate, commit, replace, comment) results = [] requests = [] self.send_command("configure") for cmd in to_list(candidate): if not isinstance(cmd, Mapping): cmd = {"command": cmd} results.append(self.send_command(**cmd)) requests.append(cmd["command"]) out = self.get("compare") out = to_text(out, errors="surrogate_or_strict") diff_config = out if not out.startswith("No changes") else None if diff_config: if commit: try: self.commit(comment, confirm) except AnsibleConnectionFailure as e: msg = "commit failed: %s" % e.message self.discard_changes() raise AnsibleConnectionFailure(msg) else: self.send_command("exit") else: self.discard_changes() else: self.send_command("exit") if ( to_text(self._connection.get_prompt(), errors="surrogate_or_strict") .strip() .endswith("#") ): self.discard_changes() if diff_config: resp["diff"] = diff_config resp["response"] = results resp["request"] = requests return resp def get( self, command=None, prompt=None, answer=None, sendonly=False, newline=True, output=None, check_all=False, ): if not command: raise ValueError("must provide value of command to execute") if output: raise ValueError("'output' value %s is not supported for get" % output) return self.send_command( command=command, prompt=prompt, answer=answer, sendonly=sendonly, newline=newline, check_all=check_all, ) def commit(self, comment=None, confirm=None): if confirm: if comment: command = 'commit-confirm {0} comment "{1}"'.format(confirm, comment) else: - command = 'commit-confirm {0}'.format(confirm) + command = "commit-confirm {0}".format(confirm) self.send_command(command, "Proceed?", "\n") else: if comment: command = 'commit comment "{0}"'.format(comment) else: command = "commit" self.send_command(command) def discard_changes(self): self.send_command("exit discard") def get_diff( self, candidate=None, running=None, diff_match="line", diff_ignore_lines=None, path=None, diff_replace=None, ): diff = {} device_operations = self.get_device_operations() option_values = self.get_option_values() if candidate is None and device_operations["supports_generate_diff"]: raise ValueError("candidate configuration is required to generate diff") if diff_match not in option_values["diff_match"]: raise ValueError( "'match' value %s in invalid, valid values are %s" % (diff_match, ", ".join(option_values["diff_match"])), ) if diff_replace: raise ValueError("'replace' in diff is not supported") if diff_ignore_lines: raise ValueError("'diff_ignore_lines' in diff is not supported") if path: raise ValueError("'path' in diff is not supported") set_format = candidate.startswith("set") or candidate.startswith("delete") candidate_obj = NetworkConfig(indent=4, contents=candidate) if not set_format: config = [c.line for c in candidate_obj.items] commands = list() # this filters out less specific lines for item in config: for index, entry in enumerate(commands): if item.startswith(entry): del commands[index] break commands.append(item) candidate_commands = ["set %s" % cmd.replace(" {", "") for cmd in commands] else: candidate_commands = str(candidate).strip().split("\n") if diff_match == "none": diff["config_diff"] = list(candidate_commands) return diff running_commands = [str(c).replace("'", "") for c in running.splitlines()] updates = list() visited = set() for line in candidate_commands: item = str(line).replace("'", "") if not item.startswith("set") and not item.startswith("delete"): raise ValueError("line must start with either `set` or `delete`") elif item.startswith("set") and item not in running_commands: updates.append(line) elif item.startswith("delete"): if not running_commands: updates.append(line) else: item = re.sub(r"delete", "set", item) for entry in running_commands: if re.match(rf"^{re.escape(item)}\b", entry) and line not in visited: updates.append(line) visited.add(line) diff["config_diff"] = list(updates) return diff def run_commands(self, commands=None, check_rc=True): if commands is None: raise ValueError("'commands' value is required") responses = list() for cmd in to_list(commands): if not isinstance(cmd, Mapping): cmd = {"command": cmd} output = cmd.pop("output", None) if output: raise ValueError("'output' value %s is not supported for run_commands" % output) try: out = self.send_command(**cmd) except AnsibleConnectionFailure as e: if check_rc: raise out = getattr(e, "err", e) responses.append(out) return responses def get_device_operations(self): return { "supports_diff_replace": False, "supports_commit": True, "supports_rollback": False, "supports_defaults": False, "supports_onbox_diff": True, "supports_commit_comment": True, "supports_multiline_delimiter": False, "supports_diff_match": True, "supports_diff_ignore_lines": False, "supports_generate_diff": False, "supports_replace": False, } def get_option_values(self): return { "format": ["text", "set"], "diff_match": ["line", "none"], "diff_replace": [], "output": [], } def get_capabilities(self): result = super(Cliconf, self).get_capabilities() result["device_operations"] = self.get_device_operations() result.update(self.get_option_values()) return json.dumps(result) def set_cli_prompt_context(self): """ Make sure we are in the operational cli mode :return: None """ if self._connection.connected: self._update_cli_prompt_context(config_context="#", exit_command="exit discard") diff --git a/plugins/doc_fragments/vyos.py b/plugins/doc_fragments/vyos.py index aaa7bf71..698d0f5f 100644 --- a/plugins/doc_fragments/vyos.py +++ b/plugins/doc_fragments/vyos.py @@ -1,17 +1,16 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function - __metaclass__ = type # Copyright: (c) 2015, Peter Sprygada # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) class ModuleDocFragment(object): # Standard files documentation fragment DOCUMENTATION = r"""options: {} notes: - For more information on using Ansible to manage network devices see the :ref:`Ansible Network Guide ` """ diff --git a/plugins/module_utils/network/vyos/argspec/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/argspec/bgp_address_family/bgp_address_family.py index 13f9fab0..946f6861 100644 --- a/plugins/module_utils/network/vyos/argspec/bgp_address_family/bgp_address_family.py +++ b/plugins/module_utils/network/vyos/argspec/bgp_address_family/bgp_address_family.py @@ -1,195 +1,194 @@ # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type ############################################# # WARNING # ############################################# # # This file is auto generated by the # cli_rm_builder. # # Manually editing this file is not advised. # # To update the argspec make the desired changes # in the module docstring and re-run # cli_rm_builder. # ############################################# """ The arg spec for the vyos_bgp_address_family module """ class Bgp_address_familyArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_bgp_address_family module""" argument_spec = { "config": { "type": "dict", "options": { "as_number": {"type": "int"}, "address_family": { "type": "list", "elements": "dict", "options": { "afi": {"type": "str", "choices": ["ipv4", "ipv6"]}, "aggregate_address": { "type": "list", "elements": "dict", "options": { "prefix": {"type": "str"}, "as_set": {"type": "bool"}, "summary_only": {"type": "bool"}, }, }, "networks": { "type": "list", "elements": "dict", "options": { "prefix": {"type": "str"}, "path_limit": {"type": "int"}, "backdoor": {"type": "bool"}, "route_map": {"type": "str"}, }, }, "redistribute": { "type": "list", "elements": "dict", "options": { "protocol": { "type": "str", "choices": [ "connected", "kernel", "ospf", "ospfv3", "rip", "ripng", "static", ], }, "table": {"type": "str"}, "route_map": {"type": "str"}, "metric": {"type": "int"}, }, }, }, }, "neighbors": { "type": "list", "elements": "dict", "options": { "neighbor_address": {"type": "str"}, "address_family": { "type": "list", "elements": "dict", "options": { "afi": { "type": "str", "choices": ["ipv4", "ipv6"], }, "allowas_in": {"type": "int"}, "as_override": {"type": "bool"}, "attribute_unchanged": { "type": "dict", "options": { "as_path": {"type": "bool"}, "med": {"type": "bool"}, "next_hop": {"type": "bool"}, }, }, "capability": { "type": "dict", "options": { "dynamic": {"type": "bool"}, "orf": { "type": "str", "choices": ["send", "receive"], }, }, }, "default_originate": {"type": "str"}, "distribute_list": { "type": "list", "elements": "dict", "options": { "action": { "type": "str", "choices": ["export", "import"], }, "acl": {"type": "int"}, }, }, "filter_list": { "type": "list", "elements": "dict", "options": { "action": { "type": "str", "choices": ["export", "import"], }, "path_list": {"type": "str"}, }, }, "maximum_prefix": {"type": "int"}, "nexthop_local": {"type": "bool"}, "nexthop_self": {"type": "bool"}, "peer_group": {"type": "str"}, "prefix_list": { "type": "list", "elements": "dict", "options": { "action": { "type": "str", "choices": ["export", "import"], }, "prefix_list": {"type": "str"}, }, }, "remove_private_as": {"type": "bool"}, "route_map": { "type": "list", "elements": "dict", "options": { "action": { "type": "str", "choices": ["export", "import"], }, "route_map": {"type": "str"}, }, }, "route_reflector_client": {"type": "bool"}, "route_server_client": {"type": "bool"}, "soft_reconfiguration": {"type": "bool"}, "unsupress_map": {"type": "str"}, "weight": {"type": "int"}, }, }, }, }, }, }, "running_config": {"type": "str"}, "state": { "type": "str", "choices": [ "merged", "replaced", "deleted", "gathered", "parsed", "rendered", "purged", "overridden", ], "default": "merged", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/bgp_global/bgp_global.py b/plugins/module_utils/network/vyos/argspec/bgp_global/bgp_global.py index 42fb5abf..13cca54d 100644 --- a/plugins/module_utils/network/vyos/argspec/bgp_global/bgp_global.py +++ b/plugins/module_utils/network/vyos/argspec/bgp_global/bgp_global.py @@ -1,173 +1,172 @@ # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type ############################################# # WARNING # ############################################# # # This file is auto generated by the # cli_rm_builder. # # Manually editing this file is not advised. # # To update the argspec make the desired changes # in the module docstring and re-run # cli_rm_builder. # ############################################# """ The arg spec for the vyos_bgp_global module """ class Bgp_globalArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_bgp_global module""" argument_spec = { "config": { "type": "dict", "options": { "as_number": {"type": "int"}, "neighbor": { "type": "list", "elements": "dict", "options": { "address": {"type": "str"}, "advertisement_interval": {"type": "int"}, "capability": { "type": "dict", "options": { "dynamic": {"type": "bool"}, "extended_nexthop": {"type": "bool"}, }, }, "default_originate": {"type": "str"}, "description": {"type": "str"}, "disable_capability_negotiation": {"type": "bool"}, "disable_connected_check": {"type": "bool"}, "disable_send_community": { "type": "str", "choices": ["extended", "standard"], }, "ebgp_multihop": {"type": "int"}, "local_as": {"type": "int"}, "override_capability": {"type": "bool"}, "passive": {"type": "bool"}, "password": {"type": "str", "no_log": True}, "peer_group_name": {"type": "str"}, "peer_group": {"type": "bool"}, "port": {"type": "int"}, "remote_as": {"type": "int"}, "solo": {"type": "bool"}, "shutdown": {"type": "bool"}, "strict_capability_match": {"type": "bool"}, "timers": { "type": "dict", "options": { "connect": {"type": "int"}, "holdtime": {"type": "int"}, "keepalive": {"type": "int"}, }, }, "ttl_security": {"type": "int"}, "update_source": {"type": "str"}, }, }, "timers": { "type": "dict", "options": { "keepalive": {"type": "int"}, "holdtime": {"type": "int"}, }, }, "bgp_params": { "type": "dict", "options": { "always_compare_med": {"type": "bool"}, "bestpath": { "type": "dict", "options": { "as_path": { "type": "str", "choices": ["confed", "ignore"], }, "compare_routerid": {"type": "bool"}, "med": { "type": "str", "choices": ["confed", "missing-as-worst"], }, }, }, "cluster_id": {"type": "str"}, "confederation": { "type": "list", "elements": "dict", "options": { "identifier": {"type": "int"}, "peers": {"type": "int"}, }, }, "dampening": { "type": "dict", "options": { "half_life": {"type": "int"}, "max_suppress_time": {"type": "int"}, "re_use": {"type": "int"}, "start_suppress_time": {"type": "int"}, }, }, "default": { "type": "dict", "options": { "local_pref": {"type": "int"}, "no_ipv4_unicast": {"type": "bool"}, }, }, "deterministic_med": {"type": "bool"}, "disable_network_import_check": {"type": "bool"}, "distance": { "type": "list", "elements": "dict", "options": { "type": { "type": "str", "choices": ["external", "internal", "local"], }, "value": {"type": "int"}, "prefix": {"type": "int"}, }, }, "enforce_first_as": {"type": "bool"}, "graceful_restart": {"type": "int"}, "log_neighbor_changes": {"type": "bool"}, "no_client_to_client_reflection": {"type": "bool"}, "no_fast_external_failover": {"type": "bool"}, "router_id": {"type": "str"}, "scan_time": {"type": "int"}, }, }, }, }, "state": { "type": "str", "choices": [ "deleted", "merged", "purged", "replaced", "gathered", "rendered", "parsed", ], "default": "merged", }, "running_config": {"type": "str"}, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/facts/facts.py b/plugins/module_utils/network/vyos/argspec/facts/facts.py index b274c507..ddcd8d14 100644 --- a/plugins/module_utils/network/vyos/argspec/facts/facts.py +++ b/plugins/module_utils/network/vyos/argspec/facts/facts.py @@ -1,23 +1,23 @@ # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The arg spec for the vyos facts module. """ -from __future__ import absolute_import, division, print_function +from __future__ import absolute_import, division, print_function __metaclass__ = type class FactsArgs(object): # pylint: disable=R0903 """The arg spec for the vyos facts module""" def __init__(self, **kwargs): pass argument_spec = { "gather_subset": dict(default=["min"], type="list", elements="str"), "gather_network_resources": dict(type="list", elements="str"), "available_network_resources": {"type": "bool", "default": False}, } diff --git a/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py index 8421b6de..aeef42bc 100644 --- a/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py +++ b/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py @@ -1,206 +1,204 @@ # # -*- 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_firewall_global module """ - from __future__ import absolute_import, division, print_function - __metaclass__ = type class Firewall_globalArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_firewall_global module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "options": { "config_trap": {"type": "bool"}, "group": { "options": { "address_group": { "elements": "dict", "options": { "afi": { "choices": [ "ipv4", "ipv6", ], "default": "ipv4", "type": "str", }, "description": {"type": "str"}, "members": { "elements": "dict", "options": {"address": {"type": "str"}}, "type": "list", }, "name": { "required": True, "type": "str", }, }, "type": "list", }, "network_group": { "elements": "dict", "options": { "afi": { "choices": [ "ipv4", "ipv6", ], "default": "ipv4", "type": "str", }, "description": {"type": "str"}, "members": { "elements": "dict", "options": {"address": {"type": "str"}}, "type": "list", }, "name": { "required": True, "type": "str", }, }, "type": "list", }, "port_group": { "elements": "dict", "options": { "description": {"type": "str"}, "members": { "elements": "dict", "options": {"port": {"type": "str"}}, "type": "list", }, "name": { "required": True, "type": "str", }, }, "type": "list", }, }, "type": "dict", }, "log_martians": {"type": "bool"}, "ping": { "options": { "all": {"type": "bool"}, "broadcast": {"type": "bool"}, }, "type": "dict", }, "route_redirects": { "elements": "dict", "options": { "afi": { "choices": [ "ipv4", "ipv6", ], "required": True, "type": "str", }, "icmp_redirects": { "options": { "receive": {"type": "bool"}, "send": {"type": "bool"}, }, "type": "dict", }, "ip_src_route": {"type": "bool"}, }, "type": "list", }, "state_policy": { "elements": "dict", "options": { "action": { "choices": [ "accept", "drop", "reject", ], "type": "str", }, "connection_type": { "choices": [ "established", "invalid", "related", ], "type": "str", }, "log": {"type": "bool"}, "log_level": { "choices": [ "emerg", "alert", "crit", "err", "warn", "notice", "info", "debug", ], "type": "str", }, }, "type": "list", }, "syn_cookies": {"type": "bool"}, "twa_hazards_protection": {"type": "bool"}, "validation": { "choices": [ "strict", "loose", "disable", ], "type": "str", }, }, "type": "dict", }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "deleted", "gathered", "rendered", "parsed", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/firewall_interfaces/firewall_interfaces.py b/plugins/module_utils/network/vyos/argspec/firewall_interfaces/firewall_interfaces.py index 93c898e8..d925a7f4 100644 --- a/plugins/module_utils/network/vyos/argspec/firewall_interfaces/firewall_interfaces.py +++ b/plugins/module_utils/network/vyos/argspec/firewall_interfaces/firewall_interfaces.py @@ -1,93 +1,91 @@ # # -*- 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_firewall_interfaces module """ - from __future__ import absolute_import, division, print_function - __metaclass__ = type class Firewall_interfacesArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_firewall_interfaces module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "elements": "dict", "options": { "access_rules": { "elements": "dict", "options": { "afi": { "choices": [ "ipv4", "ipv6", ], "required": True, "type": "str", }, "rules": { "elements": "dict", "options": { "direction": { "choices": [ "in", "local", "out", ], "required": True, "type": "str", }, "name": {"type": "str"}, }, "type": "list", }, }, "type": "list", }, "name": {"required": True, "type": "str"}, }, "type": "list", }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "overridden", "deleted", "parsed", "rendered", "gathered", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py index d1cf4fe8..b847d2b0 100644 --- a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py +++ b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py @@ -1,351 +1,349 @@ # # -*- 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_firewall_rules module """ - from __future__ import absolute_import, division, print_function - __metaclass__ = type class Firewall_rulesArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_firewall_rules module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "elements": "dict", "options": { "afi": { "choices": ["ipv4", "ipv6"], "required": True, "type": "str", }, "rule_sets": { "elements": "dict", "options": { "default_action": { "choices": [ "drop", "reject", "accept", "jump", ], "type": "str", }, "default_jump_target": {"type": "str"}, "description": {"type": "str"}, "enable_default_log": {"type": "bool"}, "filter": { "choices": [ "input", "output", "forward", ], "type": "str", }, "name": {"type": "str"}, "rules": { "elements": "dict", "options": { "action": { "choices": [ "drop", "reject", "accept", "inspect", "continue", "return", "jump", "offload", "queue", "synproxy", ], "type": "str", }, "description": {"type": "str"}, "destination": { "options": { "address": {"type": "str"}, "group": { "options": { "address_group": {"type": "str"}, "network_group": {"type": "str"}, "port_group": {"type": "str"}, }, "type": "dict", }, "port": {"type": "str"}, }, "type": "dict", }, "disable": { "aliases": ["disabled"], "type": "bool", }, "fragment": { "choices": [ "match-frag", "match-non-frag", ], "type": "str", }, "icmp": { "options": { "code": {"type": "int"}, "type": {"type": "int"}, "type_name": { "choices": [ "any", "echo-reply", "destination-unreachable", "network-unreachable", "host-unreachable", "protocol-unreachable", "port-unreachable", "fragmentation-needed", "source-route-failed", "network-unknown", "host-unknown", "network-prohibited", "host-prohibited", "TOS-network-unreachable", "TOS-host-unreachable", "communication-prohibited", "host-precedence-violation", "precedence-cutoff", "source-quench", "redirect", "network-redirect", "host-redirect", "TOS-network-redirect", "TOS-host-redirect", "echo-request", "router-advertisement", "router-solicitation", "time-exceeded", "ttl-zero-during-transit", "ttl-zero-during-reassembly", "parameter-problem", "ip-header-bad", "required-option-missing", "timestamp-request", "timestamp-reply", "address-mask-request", "address-mask-reply", "ping", "pong", "ttl-exceeded", ], "type": "str", }, }, "type": "dict", }, "inbound_interface": { "options": { "group": {"type": "str"}, "name": {"type": "str"}, }, "type": "dict", }, "ipsec": { "choices": [ "match-ipsec", "match-none", "match-ipsec-in", "match-ipsec-out", "match-none-in", "match-none-out", ], "type": "str", }, "jump_target": {"type": "str"}, "limit": { "options": { "burst": {"type": "int"}, "rate": { "options": { "number": {"type": "int"}, "unit": {"type": "str"}, }, "type": "dict", }, }, "type": "dict", }, "log": { "choices": [ "disable", "enable", ], "type": "str", }, "number": { "required": True, "type": "int", }, "offload_target": {"type": "str"}, "outbound_interface": { "options": { "group": {"type": "str"}, "name": {"type": "str"}, }, "type": "dict", }, "packet_length": { "elements": "dict", "options": {"length": {"type": "str"}}, "type": "list", }, "packet_length_exclude": { "elements": "dict", "options": {"length": {"type": "str"}}, "type": "list", }, "packet_type": { "choices": [ "broadcast", "multicast", "host", "other", ], "type": "str", }, "protocol": {"type": "str"}, "queue": {"type": "str"}, "queue_options": { "choices": [ "bypass", "fanout", ], "type": "str", }, "recent": { "options": { "count": {"type": "int"}, "time": {"type": "str"}, }, "type": "dict", }, "source": { "options": { "address": {"type": "str"}, "fqdn": {"type": "str"}, "group": { "options": { "address_group": {"type": "str"}, "network_group": {"type": "str"}, "port_group": {"type": "str"}, }, "type": "dict", }, "mac_address": {"type": "str"}, "port": {"type": "str"}, }, "type": "dict", }, "state": { "options": { "established": {"type": "bool"}, "invalid": {"type": "bool"}, "new": {"type": "bool"}, "related": {"type": "bool"}, }, "type": "dict", }, "synproxy": { "options": { "mss": {"type": "int"}, "window_scale": {"type": "int"}, }, "type": "dict", }, "tcp": { "options": { "flags": { "elements": "dict", "options": { "flag": { "choices": [ "ack", "cwr", "ecn", "fin", "psh", "rst", "syn", "urg", "all", ], "type": "str", }, "invert": {"type": "bool"}, }, "type": "list", }, }, "type": "dict", }, "time": { "options": { "monthdays": {"type": "str"}, "startdate": {"type": "str"}, "starttime": {"type": "str"}, "stopdate": {"type": "str"}, "stoptime": {"type": "str"}, "utc": {"type": "bool"}, "weekdays": {"type": "str"}, }, "type": "dict", }, }, "type": "list", }, }, "type": "list", }, }, "type": "list", }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "overridden", "deleted", "gathered", "rendered", "parsed", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/hostname/hostname.py b/plugins/module_utils/network/vyos/argspec/hostname/hostname.py index 12864e66..b1af7b9c 100644 --- a/plugins/module_utils/network/vyos/argspec/hostname/hostname.py +++ b/plugins/module_utils/network/vyos/argspec/hostname/hostname.py @@ -1,50 +1,49 @@ # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type ############################################# # WARNING # ############################################# # # This file is auto generated by the # cli_rm_builder. # # Manually editing this file is not advised. # # To update the argspec make the desired changes # in the module docstring and re-run # cli_rm_builder. # ############################################# """ The arg spec for the vyos_hostname module """ class HostnameArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_hostname module""" argument_spec = { "config": {"type": "dict", "options": {"hostname": {"type": "str"}}}, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "overridden", "deleted", "gathered", "parsed", "rendered", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py b/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py index 14b67c28..66e046bf 100644 --- a/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py +++ b/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py @@ -1,99 +1,97 @@ # # -*- 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_interfaces module """ - from __future__ import absolute_import, division, print_function - __metaclass__ = type class InterfacesArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_interfaces module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "elements": "dict", "options": { "description": {"type": "str"}, "duplex": { "choices": ["full", "half", "auto"], "type": "str", }, "enabled": { "aliases": ["enable"], "default": True, "type": "bool", }, "mtu": {"type": "int"}, "name": {"required": True, "type": "str"}, "speed": { "choices": [ "auto", "10", "100", "1000", "2500", "10000", ], "type": "str", }, "vifs": { "elements": "dict", "options": { "description": {"type": "str"}, "enabled": { "aliases": ["enable"], "default": True, "type": "bool", }, "mtu": {"type": "int"}, "vlan_id": {"type": "int"}, }, "type": "list", }, }, "type": "list", }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "overridden", "deleted", "rendered", "gathered", "parsed", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py index 4dee518e..d6d9e298 100644 --- a/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py +++ b/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py @@ -1,90 +1,88 @@ # # -*- 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_l3_interfaces module """ - from __future__ import absolute_import, division, print_function - __metaclass__ = type class L3_interfacesArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_l3_interfaces module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "elements": "dict", "options": { "ipv4": { "elements": "dict", "options": {"address": {"type": "str"}}, "type": "list", }, "ipv6": { "elements": "dict", "options": {"address": {"type": "str"}}, "type": "list", }, "name": {"required": True, "type": "str"}, "vifs": { "elements": "dict", "options": { "ipv4": { "elements": "dict", "options": {"address": {"type": "str"}}, "type": "list", }, "ipv6": { "elements": "dict", "options": {"address": {"type": "str"}}, "type": "list", }, "vlan_id": {"type": "int"}, }, "type": "list", }, }, "type": "list", }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "overridden", "deleted", "rendered", "gathered", "parsed", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py index 956c4385..7744eea0 100644 --- a/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py +++ b/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py @@ -1,99 +1,97 @@ # # -*- 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_lag_interfaces module """ - from __future__ import absolute_import, division, print_function - __metaclass__ = type class Lag_interfacesArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_lag_interfaces module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "elements": "dict", "options": { "arp_monitor": { "options": { "interval": {"type": "int"}, "target": { "elements": "str", "type": "list", }, }, "type": "dict", }, "hash_policy": { "choices": [ "layer2", "layer2+3", "layer3+4", ], "type": "str", }, "members": { "elements": "dict", "options": {"member": {"type": "str"}}, "type": "list", }, "mode": { "choices": [ "802.3ad", "active-backup", "broadcast", "round-robin", "transmit-load-balance", "adaptive-load-balance", "xor-hash", ], "type": "str", }, "name": {"required": True, "type": "str"}, "primary": {"type": "str"}, }, "type": "list", }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "overridden", "deleted", "parsed", "gathered", "rendered", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py b/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py index d79de617..6d1129e8 100644 --- a/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py +++ b/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py @@ -1,78 +1,76 @@ # # -*- 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_lldp_global module """ - from __future__ import absolute_import, division, print_function - __metaclass__ = type class Lldp_globalArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_lldp_global module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "options": { "address": {"type": "str"}, "addresses": { "elements": "str", "type": "list", }, "enable": {"type": "bool"}, "legacy_protocols": { "choices": [ "cdp", "edp", "fdp", "sonmp", ], "elements": "str", "type": "list", }, "snmp": {"type": "str"}, }, "type": "dict", "mutually_exclusive": [["addresses", "address"]], }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "deleted", "gathered", "rendered", "parsed", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py b/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py index fd6c6271..4bb742f5 100644 --- a/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py +++ b/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py @@ -1,92 +1,90 @@ # # -*- 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_lldp_interfaces module """ - from __future__ import absolute_import, division, print_function - __metaclass__ = type class Lldp_interfacesArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_lldp_interfaces module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "elements": "dict", "options": { "enable": {"default": True, "type": "bool"}, "location": { "options": { "coordinate_based": { "options": { "altitude": {"type": "int"}, "datum": { "choices": [ "WGS84", "NAD83", "MLLW", ], "type": "str", }, "latitude": { "required": True, "type": "str", }, "longitude": { "required": True, "type": "str", }, }, "type": "dict", }, "elin": {"type": "str"}, }, "type": "dict", }, "name": {"required": True, "type": "str"}, }, "type": "list", }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "overridden", "deleted", "rendered", "parsed", "gathered", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/logging_global/logging_global.py b/plugins/module_utils/network/vyos/argspec/logging_global/logging_global.py index 734d190e..f6800e9b 100644 --- a/plugins/module_utils/network/vyos/argspec/logging_global/logging_global.py +++ b/plugins/module_utils/network/vyos/argspec/logging_global/logging_global.py @@ -1,379 +1,378 @@ # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type ############################################# # WARNING # ############################################# # # This file is auto generated by the # cli_rm_builder. # # Manually editing this file is not advised. # # To update the argspec make the desired changes # in the module docstring and re-run # cli_rm_builder. # ############################################# """ The arg spec for the vyos_logging_global module """ class Logging_globalArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_logging_global module""" argument_spec = { "config": { "type": "dict", "options": { "console": { "type": "dict", "options": { "state": { "type": "str", "choices": ["enabled", "disabled"], }, "facilities": { "type": "list", "elements": "dict", "options": { "facility": { "type": "str", "choices": [ "all", "auth", "authpriv", "cron", "daemon", "kern", "lpr", "mail", "mark", "news", "protocols", "security", "syslog", "user", "uucp", "local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7", ], }, "severity": { "type": "str", "choices": [ "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug", "all", ], }, }, }, }, }, "files": { "type": "list", "elements": "dict", "options": { "path": {"type": "str"}, "archive": { "type": "dict", "options": { "state": { "type": "str", "choices": ["enabled", "disabled"], }, "file_num": {"type": "int"}, "size": {"type": "int"}, }, }, "facilities": { "type": "list", "elements": "dict", "options": { "facility": { "type": "str", "choices": [ "all", "auth", "authpriv", "cron", "daemon", "kern", "lpr", "mail", "mark", "news", "protocols", "security", "syslog", "user", "uucp", "local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7", ], }, "severity": { "type": "str", "choices": [ "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug", "all", ], }, }, }, }, }, "global_params": { "type": "dict", "options": { "state": { "type": "str", "choices": ["enabled", "disabled"], }, "archive": { "type": "dict", "options": { "state": { "type": "str", "choices": ["enabled", "disabled"], }, "file_num": {"type": "int"}, "size": {"type": "int"}, }, "removed_in_version": "7.0.0", "removed_from_collection": "vyos.vyos", }, "facilities": { "type": "list", "elements": "dict", "options": { "facility": { "type": "str", "choices": [ "all", "auth", "authpriv", "cron", "daemon", "kern", "lpr", "mail", "mark", "news", "protocols", "security", "syslog", "user", "uucp", "local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7", ], }, "severity": { "type": "str", "choices": [ "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug", "all", ], }, }, }, "marker_interval": {"type": "int"}, "preserve_fqdn": {"type": "bool"}, }, }, "hosts": { "type": "list", "elements": "dict", "options": { "port": {"type": "int"}, "facilities": { "type": "list", "elements": "dict", "options": { "facility": { "type": "str", "choices": [ "all", "auth", "authpriv", "cron", "daemon", "kern", "lpr", "mail", "mark", "news", "protocols", "security", "syslog", "user", "uucp", "local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7", ], }, "severity": { "type": "str", "choices": [ "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug", "all", ], }, "protocol": { "type": "str", "choices": ["udp", "tcp"], "removed_in_version": "7.0.0", "removed_from_collection": "vyos.vyos", }, }, }, "hostname": {"type": "str"}, "protocol": { "type": "str", "choices": ["udp", "tcp"], }, }, }, "syslog": { "type": "dict", "options": { "state": { "type": "str", "choices": ["enabled", "disabled"], }, }, }, "users": { "type": "list", "elements": "dict", "options": { "username": {"type": "str"}, "facilities": { "type": "list", "elements": "dict", "options": { "facility": { "type": "str", "choices": [ "all", "auth", "authpriv", "cron", "daemon", "kern", "lpr", "mail", "mark", "news", "protocols", "security", "syslog", "user", "uucp", "local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7", ], }, "severity": { "type": "str", "choices": [ "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug", "all", ], }, }, }, }, }, }, }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "overridden", "deleted", "gathered", "parsed", "rendered", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/ntp_global/ntp_global.py b/plugins/module_utils/network/vyos/argspec/ntp_global/ntp_global.py index 6940fb7e..5e10d05b 100644 --- a/plugins/module_utils/network/vyos/argspec/ntp_global/ntp_global.py +++ b/plugins/module_utils/network/vyos/argspec/ntp_global/ntp_global.py @@ -1,77 +1,76 @@ # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type ############################################# # WARNING # ############################################# # # This file is auto generated by the # cli_rm_builder. # # Manually editing this file is not advised. # # To update the argspec make the desired changes # in the module docstring and re-run # cli_rm_builder. # ############################################# """ The arg spec for the vyos_ntp_global module """ class Ntp_globalArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_ntp_global module""" argument_spec = { "config": { "type": "dict", "options": { "allow_clients": {"type": "list", "elements": "str"}, "listen_addresses": {"type": "list", "elements": "str"}, "servers": { "type": "list", "elements": "dict", "options": { "server": {"type": "str"}, "options": { "type": "list", "elements": "str", "choices": [ "dynamic", "noselect", "pool", "preempt", "prefer", "nts", "ptp", "interleave", ], }, }, }, }, }, "running_config": {"type": "str"}, "state": { "type": "str", "choices": [ "deleted", "merged", "overridden", "replaced", "gathered", "rendered", "parsed", ], "default": "merged", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/ospf_interfaces/ospf_interfaces.py b/plugins/module_utils/network/vyos/argspec/ospf_interfaces/ospf_interfaces.py index 0b5814be..ced9f98e 100644 --- a/plugins/module_utils/network/vyos/argspec/ospf_interfaces/ospf_interfaces.py +++ b/plugins/module_utils/network/vyos/argspec/ospf_interfaces/ospf_interfaces.py @@ -1,96 +1,95 @@ # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type ############################################# # WARNING # ############################################# # # This file is auto generated by the # cli_rm_builder. # # Manually editing this file is not advised. # # To update the argspec make the desired changes # in the module docstring and re-run # cli_rm_builder. # ############################################# """ The arg spec for the vyos_ospf_interfaces module """ class Ospf_interfacesArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_ospf_interfaces module""" argument_spec = { "config": { "type": "list", "elements": "dict", "options": { "name": {"type": "str"}, "address_family": { "type": "list", "elements": "dict", "options": { "afi": { "type": "str", "choices": ["ipv4", "ipv6"], "required": True, }, "authentication": { "type": "dict", "options": { "plaintext_password": { "type": "str", "no_log": True, }, "md5_key": { "type": "dict", "no_log": False, "options": { "key_id": {"type": "int"}, "key": {"type": "str", "no_log": True}, }, }, }, }, "bandwidth": {"type": "int"}, "cost": {"type": "int"}, "dead_interval": {"type": "int"}, "hello_interval": {"type": "int"}, "mtu_ignore": {"type": "bool"}, "network": {"type": "str"}, "priority": {"type": "int"}, "retransmit_interval": {"type": "int"}, "transmit_delay": {"type": "int"}, "ifmtu": {"type": "int"}, "instance": {"type": "str"}, "passive": {"type": "bool"}, }, }, }, }, "running_config": {"type": "str"}, "state": { "type": "str", "choices": [ "merged", "replaced", "overridden", "deleted", "gathered", "parsed", "rendered", ], "default": "merged", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py index 0a422e89..a939652b 100644 --- a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py @@ -1,296 +1,294 @@ # # -*- 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 """ - from __future__ import absolute_import, division, print_function - __metaclass__ = type class Ospfv2Args(object): # pylint: disable=R0903 """The arg spec for the vyos_ospfv2 module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "options": { "areas": { "elements": "dict", "options": { "area_id": {"type": "str"}, "area_type": { "options": { "normal": {"type": "bool"}, "nssa": { "options": { "default_cost": {"type": "int"}, "no_summary": {"type": "bool"}, "set": {"type": "bool"}, "translate": { "choices": [ "always", "candidate", "never", ], "type": "str", }, }, "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": { "no_log": True, "type": "str", }, }, "type": "list", }, "plaintext_password": { "no_log": True, "type": "str", }, }, "type": "dict", }, "dead_interval": {"type": "int"}, "hello_interval": {"type": "int"}, "retransmit_interval": {"type": "int"}, "transmit_delay": {"type": "int"}, }, "type": "list", }, }, "type": "list", }, "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", }, "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": { "elements": "str", "type": "list", }, "passive_interface_exclude": { "elements": "str", "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": {"elements": "str", "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": "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/argspec/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py index 61704345..77a17d79 100644 --- a/plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py +++ b/plugins/module_utils/network/vyos/argspec/ospfv3/ospfv3.py @@ -1,105 +1,103 @@ # # -*- 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_ospfv3 module """ - from __future__ import absolute_import, division, print_function - __metaclass__ = type class Ospfv3Args(object): # pylint: disable=R0903 """The arg spec for the vyos_ospfv3 module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "options": { "areas": { "elements": "dict", "options": { "area_id": {"type": "str"}, "export_list": {"type": "str"}, "import_list": {"type": "str"}, "interface": { "aliases": ["interfaces"], "type": "list", "elements": "dict", "options": {"name": {"type": "str"}}, }, "range": { "elements": "dict", "options": { "address": {"type": "str"}, "advertise": {"type": "bool"}, "not_advertise": {"type": "bool"}, }, "type": "list", }, }, "type": "list", }, "parameters": { "options": {"router_id": {"type": "str"}}, "type": "dict", }, "redistribute": { "elements": "dict", "options": { "route_map": {"type": "str"}, "route_type": { "choices": [ "bgp", "connected", "kernel", "ripng", "static", ], "type": "str", }, }, "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/argspec/prefix_lists/prefix_lists.py b/plugins/module_utils/network/vyos/argspec/prefix_lists/prefix_lists.py index b01a3e82..c74400ba 100644 --- a/plugins/module_utils/network/vyos/argspec/prefix_lists/prefix_lists.py +++ b/plugins/module_utils/network/vyos/argspec/prefix_lists/prefix_lists.py @@ -1,83 +1,82 @@ # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type ############################################# # WARNING # ############################################# # # This file is auto generated by the # cli_rm_builder. # # Manually editing this file is not advised. # # To update the argspec make the desired changes # in the module docstring and re-run # cli_rm_builder. # ############################################# """ The arg spec for the vyos_prefix_lists module """ class Prefix_listsArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_prefix_lists module""" argument_spec = { "config": { "type": "list", "elements": "dict", "options": { "afi": { "type": "str", "choices": ["ipv4", "ipv6"], "required": True, }, "prefix_lists": { "type": "list", "elements": "dict", "options": { "name": {"type": "str", "required": True}, "description": {"type": "str"}, "entries": { "type": "list", "elements": "dict", "options": { "sequence": {"type": "int", "required": True}, "description": {"type": "str"}, "action": { "type": "str", "choices": ["permit", "deny"], }, "ge": {"type": "int"}, "le": {"type": "int"}, "prefix": {"type": "str"}, }, }, }, }, }, }, "running_config": {"type": "str"}, "state": { "type": "str", "choices": [ "merged", "replaced", "overridden", "deleted", "gathered", "rendered", "parsed", ], "default": "merged", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py b/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py index 5b6d404d..58ffa3e5 100644 --- a/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py +++ b/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py @@ -1,241 +1,240 @@ # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type ############################################# # WARNING # ############################################# # # This file is auto generated by the # cli_rm_builder. # # Manually editing this file is not advised. # # To update the argspec make the desired changes # in the module docstring and re-run # cli_rm_builder. # ############################################# """ The arg spec for the vyos_route_maps module """ class Route_mapsArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_route_maps module""" argument_spec = { "config": { "type": "list", "elements": "dict", "options": { "route_map": {"type": "str"}, "entries": { "aliases": ["rules"], "type": "list", "elements": "dict", "options": { "sequence": {"type": "int"}, "call": {"type": "str"}, "description": {"type": "str"}, "action": {"type": "str", "choices": ["deny", "permit"]}, "continue_sequence": {"type": "int"}, "set": { "type": "dict", "options": { "aggregator": { "type": "dict", "options": { "ip": {"type": "str"}, "as": {"type": "str"}, }, }, "as_path_exclude": {"type": "str"}, "as_path_prepend": {"type": "str"}, "atomic_aggregate": {"type": "bool"}, "bgp_extcommunity_rt": {"type": "str"}, "comm_list": { "type": "dict", "options": { "comm_list": {"type": "str"}, "delete": {"type": "bool"}, }, }, "community": { "type": "dict", "options": {"value": {"type": "str"}}, }, "extcommunity_rt": {"type": "str"}, "extcommunity_soo": {"type": "str"}, "extcommunity_bandwidth": {"type": "str"}, "extcommunity_bandwidth_non_transitive": {"type": "bool"}, "ip_next_hop": {"type": "str"}, "ipv6_next_hop": { "type": "dict", "options": { "ip_type": { "type": "str", "choices": ["global", "local"], }, "value": {"type": "str"}, }, }, "large_community": {"type": "str"}, "local_preference": {"type": "str"}, "metric": {"type": "str"}, "metric_type": { "type": "str", "choices": ["type-1", "type-2"], }, "origin": { "type": "str", "choices": ["egp", "igp", "incomplete"], }, "originator_id": {"type": "str"}, "src": {"type": "str"}, "tag": {"type": "str"}, "weight": {"type": "str"}, "table": {"type": "str"}, }, }, "match": { "type": "dict", "options": { "as_path": {"type": "str"}, "community": { "type": "dict", "options": { "community_list": {"type": "str"}, "exact_match": {"type": "bool"}, }, }, "extcommunity": {"type": "str"}, "interface": {"type": "str"}, "ip": { "type": "dict", "options": { "address": { "type": "dict", "options": { "list_type": { "type": "str", "choices": [ "access-list", "prefix-list", ], }, "value": {"type": "str"}, }, }, "next_hop": { "type": "dict", "options": { "list_type": { "type": "str", "choices": [ "access-list", "prefix-list", ], }, "value": {"type": "str"}, }, }, "route_source": { "type": "dict", "options": { "list_type": { "type": "str", "choices": [ "access-list", "prefix-list", ], }, "value": {"type": "str"}, }, }, }, }, "ipv6": { "type": "dict", "options": { "address": { "type": "dict", "options": { "list_type": { "type": "str", "choices": [ "access-list", "prefix-list", ], }, "value": {"type": "str"}, }, }, "next_hop": {"type": "str"}, }, }, "protocol": { "type": "str", "choices": [ "babel", "bgp", "connected", "isis", "kernel", "ospf", "ospfv3", "rip", "ripng", "static", "table", "vnc", ], }, "large_community_large_community_list": { "type": "str", }, "metric": {"type": "int"}, "origin": { "type": "str", "choices": ["ebgp", "ibgp", "incomplete"], }, "peer": {"type": "str"}, "rpki": { "type": "str", "choices": ["notfound", "invalid", "valid"], }, }, }, "on_match": { "type": "dict", "options": { "next": {"type": "bool"}, "goto": {"type": "int"}, }, }, }, }, }, }, "running_config": {"type": "str"}, "state": { "type": "str", "choices": [ "deleted", "merged", "overridden", "replaced", "gathered", "rendered", "parsed", ], "default": "merged", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/snmp_server/snmp_server.py b/plugins/module_utils/network/vyos/argspec/snmp_server/snmp_server.py index b94c2639..53516c4f 100644 --- a/plugins/module_utils/network/vyos/argspec/snmp_server/snmp_server.py +++ b/plugins/module_utils/network/vyos/argspec/snmp_server/snmp_server.py @@ -1,211 +1,210 @@ # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type ############################################# # WARNING # ############################################# # # This file is auto generated by the # cli_rm_builder. # # Manually editing this file is not advised. # # To update the argspec make the desired changes # in the module docstring and re-run # cli_rm_builder. # ############################################# """ The arg spec for the vyos_snmp_server module """ class Snmp_serverArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_snmp_server module""" argument_spec = { "config": { "type": "dict", "options": { "communities": { "type": "list", "elements": "dict", "options": { "name": {"type": "str"}, "clients": {"type": "list", "elements": "str"}, "networks": {"type": "list", "elements": "str"}, "authorization_type": { "type": "str", "choices": ["ro", "rw"], }, }, }, "contact": {"type": "str"}, "description": {"type": "str"}, "listen_addresses": { "type": "list", "elements": "dict", "options": { "address": {"type": "str"}, "port": {"type": "int"}, }, }, "location": {"type": "str"}, "smux_peer": {"type": "str"}, "trap_source": {"type": "str"}, "trap_target": { "type": "dict", "options": { "address": {"type": "str"}, "community": {"type": "str"}, "port": {"type": "int"}, }, }, "snmp_v3": { "type": "dict", "options": { "engine_id": {"type": "str"}, "groups": { "type": "list", "elements": "dict", "options": { "group": {"type": "str"}, "mode": {"type": "str", "choices": ["ro", "rw"]}, "seclevel": { "type": "str", "choices": ["auth", "priv"], }, "view": {"type": "str"}, }, }, "trap_targets": { "type": "list", "elements": "dict", "options": { "address": {"type": "str"}, "authentication": { "type": "dict", "options": { "type": { "type": "str", "choices": ["md5", "sha"], }, "encrypted_key": { "type": "str", "no_log": True, }, "plaintext_key": { "type": "str", "no_log": True, }, }, }, "port": {"type": "int"}, "privacy": { "type": "dict", "options": { "type": { "type": "str", "choices": ["des", "aes"], }, "encrypted_key": { "type": "str", "no_log": True, }, "plaintext_key": { "type": "str", "no_log": True, }, }, }, "protocol": { "type": "str", "choices": ["tcp", "udp"], }, "type": { "type": "str", "choices": ["inform", "trap"], }, }, }, "users": { "type": "list", "elements": "dict", "options": { "user": {"type": "str"}, "authentication": { "type": "dict", "options": { "type": { "type": "str", "choices": ["md5", "sha"], }, "encrypted_key": { "type": "str", "no_log": True, }, "plaintext_key": { "type": "str", "no_log": True, }, }, }, "group": {"type": "str"}, "mode": {"type": "str", "choices": ["ro", "rw"]}, "privacy": { "type": "dict", "options": { "type": { "type": "str", "choices": ["des", "aes"], }, "encrypted_key": { "type": "str", "no_log": True, }, "plaintext_key": { "type": "str", "no_log": True, }, }, }, "tsm_key": {"type": "str", "no_log": True}, }, }, "views": { "type": "list", "elements": "dict", "options": { "view": {"type": "str"}, "oid": {"type": "str"}, "exclude": {"type": "str"}, "mask": {"type": "str"}, }, }, }, }, }, }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "overridden", "deleted", "gathered", "parsed", "rendered", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py b/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py index 365df48a..d8a4f11a 100644 --- a/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py +++ b/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py @@ -1,105 +1,103 @@ # # -*- 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_static_routes module """ - from __future__ import absolute_import, division, print_function - __metaclass__ = type class Static_routesArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_static_routes module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "elements": "dict", "options": { "address_families": { "elements": "dict", "options": { "afi": { "choices": [ "ipv4", "ipv6", ], "required": True, "type": "str", }, "routes": { "elements": "dict", "options": { "blackhole_config": { "options": { "distance": {"type": "int"}, "type": {"type": "str"}, }, "type": "dict", }, "dest": { "required": True, "type": "str", }, "next_hops": { "elements": "dict", "options": { "admin_distance": {"type": "int"}, "enabled": {"type": "bool"}, "forward_router_address": { "type": "str", }, "interface": {"type": "str"}, }, "type": "list", }, }, "type": "list", }, }, "type": "list", }, }, "type": "list", }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "overridden", "deleted", "gathered", "rendered", "parsed", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/vrf/vrf.py b/plugins/module_utils/network/vyos/argspec/vrf/vrf.py index e24fd537..20947f9d 100644 --- a/plugins/module_utils/network/vyos/argspec/vrf/vrf.py +++ b/plugins/module_utils/network/vyos/argspec/vrf/vrf.py @@ -1,131 +1,130 @@ # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type ############################################# # WARNING # ############################################# # # This file is auto generated by the # cli_rm_builder. # # Manually editing this file is not advised. # # To update the argspec make the desired changes # in the module docstring and re-run # cli_rm_builder. # ############################################# """ The arg spec for the vyos_vrf module """ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_global.bgp_global import ( Bgp_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import ( Ospfv2Args, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv3.ospfv3 import ( Ospfv3Args, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.static_routes.static_routes import ( Static_routesArgs, ) class VrfArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_vrf module""" bgp_argument_spec = Bgp_globalArgs.argument_spec["config"] static_routes_argument_spec = Static_routesArgs.argument_spec["config"] ospfv2_argument_spec = Ospfv2Args.argument_spec["config"] ospfv3_argument_spec = Ospfv3Args.argument_spec["config"] argument_spec = { "config": { "type": "dict", "options": { "bind_to_all": {"type": "bool", "default": False}, "instances": { "type": "list", "elements": "dict", "options": { "name": {"required": True, "type": "str"}, "description": {"type": "str"}, "disable": { "aliases": ["disabled"], "default": False, "type": "bool", }, "table_id": {"type": "int"}, "vni": {"type": "int"}, "address_family": { "type": "list", "elements": "dict", "options": { "afi": { "type": "str", "choices": ["ipv4", "ipv6"], }, "disable_forwarding": {"type": "bool", "default": False}, "nht_no_resolve_via_default": {"type": "bool", "default": False}, "route_maps": { "type": "list", "elements": "dict", "options": { "rm_name": {"type": "str", "required": True}, "protocol": { "type": "str", "choices": [ "any", "babel", "bgp", "eigrp", "isis", "ospf", "rip", "static", ], }, }, }, }, }, "protocols": { # "type": "list", # sanity # "elements": "dict", "type": "dict", "options": { "bgp": bgp_argument_spec, "ospf": ospfv2_argument_spec, "ospfv3": ospfv3_argument_spec, "static": static_routes_argument_spec, }, }, }, }, }, }, "state": { "type": "str", "choices": [ "deleted", "merged", "replaced", "overridden", "gathered", "rendered", "parsed", ], "default": "merged", }, "running_config": {"type": "str"}, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py index 2d6481c2..f1494698 100644 --- a/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py +++ b/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py @@ -1,405 +1,404 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos_bgp_address_family config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( dict_merge, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family import ( Bgp_address_familyTemplate, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family_14 import ( Bgp_address_familyTemplate14, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Bgp_address_family(ResourceModule): """ The vyos_bgp_address_family config class """ def __init__(self, module): super(Bgp_address_family, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="bgp_address_family", tmplt=Bgp_address_familyTemplate(), ) self.parsers = [] def _validate_template(self): version = get_os_version(self._module) if LooseVersion(version) >= LooseVersion("1.4"): self._tmplt = Bgp_address_familyTemplate14() else: self._tmplt = Bgp_address_familyTemplate() def parse(self): """override parse to check template""" self._validate_template() return super().parse() def get_parser(self, name): """get_parsers""" self._validate_template() return super().get_parser(name) def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ self._validate_template() if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = {} haved = {} if ( self.want.get("as_number") == self.have.get("as_number") or not self.have or LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") ): if self.want: wantd = {self.want["as_number"]: self.want} if self.have: haved = {self.have["as_number"]: self.have} else: self._module.fail_json(msg="Only one bgp instance is allowed per device") # turn all lists of dicts into dicts prior to merge for entry in wantd, haved: self._bgp_af_list_to_dict(entry) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": for k, have in haved.items(): self._delete_af(wantd, have) wantd = {} if self.state == "overridden": for k, have in haved.items(): if k not in wantd: self._compare(want={}, have=have) for k, want in wantd.items(): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Bgp_address_family network resource. """ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): self._compare_asn(want, have) self._compare_af(want, have) self._compare_neighbors(want, have) # Do the negation first command_set = [] for cmd in self.commands: if cmd not in command_set: if "delete" in cmd: command_set.insert(0, cmd) else: command_set.append(cmd) self.commands = command_set def _compare_af(self, want, have): waf = want.get("address_family", {}) haf = have.get("address_family", {}) for name, entry in waf.items(): self._compare_lists( entry, have=haf.get(name, {}), as_number=want["as_number"], afi=name, ) for name, entry in haf.items(): if name not in waf.keys() and self.state == "replaced": continue self._compare_lists({}, entry, as_number=have["as_number"], afi=name) def _delete_af(self, want, have): for as_num, entry in want.items(): for afi, af_entry in entry.get("address_family", {}).items(): if have.get("address_family"): for hafi, hentry in have["address_family"].items(): if hafi == afi: self.commands.append( self._tmplt.render( { "as_number": as_num, "address_family": {"afi": afi}, }, "address_family", True, ), ) for neigh, neigh_entry in entry.get("neighbors", {}).items(): if have.get("neighbors"): for hneigh, hnentry in have["neighbors"].items(): if hneigh == neigh: if not neigh_entry.get("address_family"): self.commands.append( self._tmplt.render( { "as_number": as_num, "neighbors": {"neighbor_address": neigh}, }, "neighbors", True, ), ) else: for k in neigh_entry["address_family"].keys(): if ( hnentry.get("address_family") and k in hnentry["address_family"].keys() ): self.commands.append( self._tmplt.render( { "as_number": as_num, "neighbors": { "neighbor_address": neigh, "address_family": {"afi": k}, }, }, "neighbors.address_family", True, ), ) def _compare_neighbors(self, want, have): parsers = [ "neighbors.allowas_in", "neighbors.as_override", "neighbors.attribute_unchanged.as_path", "neighbors.attribute_unchanged.med", "neighbors.attribute_unchanged.next_hop", "neighbors.capability_dynamic", "neighbors.capability_orf", "neighbors.default_originate", "neighbors.distribute_list", "neighbors.prefix_list", "neighbors.filter_list", "neighbors.maximum_prefix", "neighbors.nexthop_local", "neighbors.nexthop_self", "neighbors.peer_group", "neighbors.remove_private_as", "neighbors.route_map", "neighbors.route_reflector_client", "neighbors.route_server_client", "neighbors.soft_reconfiguration", "neighbors.unsuppress_map", "neighbors.weight", ] wneigh = want.get("neighbors", {}) hneigh = have.get("neighbors", {}) for name, entry in wneigh.items(): for afi, af_entry in entry.get("address_family").items(): for k, val in af_entry.items(): w = { "as_number": want["as_number"], "neighbors": { "neighbor_address": name, "address_family": {"afi": afi, k: val}, }, } h = {} if hneigh.get(name): if hneigh[name]["address_family"].get(afi): if hneigh[name]["address_family"][afi].get(k): h = { "as_number": want["as_number"], "neighbors": { "neighbor_address": name, "address_family": { "afi": afi, k: hneigh[name]["address_family"][afi].pop(k, {}), }, }, } self.compare( parsers=parsers, want=w, have=h, ) for name, entry in hneigh.items(): if name not in wneigh.keys(): # remove surplus config for overridden and replaced if self.state != "replaced": self.commands.append( self._tmplt.render( { "as_number": have["as_number"], "neighbors": {"neighbor_address": name}, }, "neighbors", True, ), ) continue for hafi, haf_entry in entry.get("address_family").items(): # remove surplus configs for given neighbor - replace and overridden for k, val in haf_entry.items(): h = { "as_number": have["as_number"], "neighbors": { "neighbor_address": name, "address_family": {"afi": hafi, k: val}, }, } self.compare(parsers=parsers, want={}, have=h) def _compare_lists(self, want, have, as_number, afi): parsers = [ "aggregate_address", "network", "network.backdoor", "network.path_limit", "network.route_map", "redistribute", "redistribute.metric", "redistribute.route_map", "redistribute.table", ] if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): delete_asn = "" else: delete_asn = " " + str(as_number) for attrib in ["redistribute", "networks", "aggregate_address"]: wdict = want.pop(attrib, {}) hdict = have.pop(attrib, {}) for key, entry in wdict.items(): if entry != hdict.get(key, {}): self.compare( parsers=parsers, want={ "as_number": as_number, "address_family": {"afi": afi, attrib: entry}, }, have={ "as_number": as_number, "address_family": { "afi": afi, attrib: hdict.pop(key, {}), }, }, ) hdict.pop(key, {}) # remove remaining items in have for replaced if not wdict and hdict: attrib = re.sub("_", "-", attrib) attrib = re.sub("networks", "network", attrib) self.commands.append( "delete protocols bgp" + delete_asn + " " + "address-family " + afi + " " + attrib, ) hdict = {} for key, entry in hdict.items(): self.compare( parsers=parsers, want={}, have={ "as_number": as_number, "address_family": {"afi": afi, attrib: entry}, }, ) # de-duplicate child commands if parent command is present for val in self.commands: for val2 in self.commands: if val != val2 and val2.startswith(val): self.commands.remove(val2) def _compare_asn(self, want, have): if want.get("as_number") and not have.get("as_number"): self.commands.append( "set protocols bgp " + "system-as " + str(want.get("as_number")), ) def _bgp_af_list_to_dict(self, entry): for name, proc in entry.items(): if "address_family" in proc: af_dict = {} for entry in proc.get("address_family"): if "networks" in entry: network_dict = {} for n_entry in entry.get("networks", []): network_dict.update({n_entry["prefix"]: n_entry}) entry["networks"] = network_dict if "aggregate_address" in entry: agg_dict = {} for a_entry in entry.get("aggregate_address", []): agg_dict.update({a_entry["prefix"]: a_entry}) entry["aggregate_address"] = agg_dict if "redistribute" in entry: redis_dict = {} for r_entry in entry.get("redistribute", []): proto_key = r_entry.get("protocol", "table") redis_dict.update({proto_key: r_entry}) entry["redistribute"] = redis_dict for af in proc.get("address_family"): af_dict.update({af["afi"]: af}) proc["address_family"] = af_dict if "neighbors" in proc: neigh_dict = {} for entry in proc.get("neighbors", []): neigh_dict.update({entry["neighbor_address"]: entry}) proc["neighbors"] = neigh_dict self._bgp_af_list_to_dict(proc["neighbors"]) diff --git a/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py b/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py index 075c8fd2..2d7d3e63 100644 --- a/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py +++ b/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py @@ -1,416 +1,415 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos_bgp_global config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( dict_merge, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_global import ( Bgp_globalTemplate, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_global_14 import ( Bgp_globalTemplate14, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Bgp_global(ResourceModule): """ The vyos_bgp_global config class """ def __init__(self, module): super(Bgp_global, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="bgp_global", tmplt=Bgp_globalTemplate(), ) self.parsers = [] def _validate_template(self): version = get_os_version(self._module) if LooseVersion(version) >= LooseVersion("1.4"): self._tmplt = Bgp_globalTemplate14() else: self._tmplt = Bgp_globalTemplate() def parse(self): """override parse to check template""" self._validate_template() return super().parse() def get_parser(self, name): """get_parsers""" self._validate_template() return super().get_parser(name) def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ version = get_os_version(self._module) if LooseVersion(version) >= LooseVersion("1.4"): self._asn_mod = "" else: self._asn_mod = " " + str(self.have.get("as_number")) self._validate_template() if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = {} haved = {} if ( self.want.get("as_number") == self.have.get("as_number") or not self.have or LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") ): if self.want: wantd = {self.want["as_number"]: self.want} if self.have: haved = {self.have["as_number"]: self.have} else: self._module.fail_json(msg="Only one bgp instance is allowed per device") # turn all lists of dicts into dicts prior to merge for entry in wantd, haved: self._bgp_global_list_to_dict(entry) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "purged": h_del = {} for k, v in haved.items(): if k in wantd or not wantd: h_del.update({k: v}) for num, entry in h_del.items(): self.commands.append(self._tmplt.render({"as_number": num}, "router", True)) wantd = {} if self.state == "deleted": self._compare(want={}, have=self.have) wantd = {} for k, want in wantd.items(): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Bgp_global network resource. """ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): self._compare_asn(want, have) parsers = ["maximum_paths", "timers"] self._compare_neighbor(want, have) self._compare_bgp_params(want, have) for name, entry in want.items(): if name != "as_number": self.compare( parsers=parsers, want={"as_number": want["as_number"], name: entry}, have={ "as_number": want["as_number"], name: have.pop(name, {}), }, ) for name, entry in have.items(): if name != "as_number": self.compare( parsers=parsers, want={}, have={"as_number": have["as_number"], name: entry}, ) # Do the negation first command_set = [] for cmd in self.commands: if cmd not in command_set: if "delete" in cmd: command_set.insert(0, cmd) else: command_set.append(cmd) self.commands = command_set def _compare_neighbor(self, want, have): parsers = [ "neighbor.advertisement_interval", "neighbor.allowas_in", "neighbor.as_override", "neighbor.attribute_unchanged.as_path", "neighbor.attribute_unchanged.med", "neighbor.attribute_unchanged.next_hop", "neighbor.capability_dynamic", "neighbor.capability_orf", "neighbor.default_originate", "neighbor.description", "neighbor.disable_capability_negotiation", "neighbor.disable_connected_check", "neighbor.disable_send_community", "neighbor.distribute_list", "neighbor.ebgp_multihop", "neighbor.filter_list", "neighbor.local_as", "neighbor.maximum_prefix", "neighbor.nexthop_self", "neighbor.override_capability", "neighbor.passive", "neighbor.password", "neighbor.peer_group_name", "neighbor.port", "neighbor.prefix_list", "neighbor.remote_as", "neighbor.remove_private_as", "neighbor.route_map", "neighbor.route_reflector_client", "neighbor.route_server_client", "neighbor.shutdown", "neighbor.soft_reconfiguration", "neighbor.strict_capability_match", "neighbor.unsuppress_map", "neighbor.update_source", "neighbor.weight", "neighbor.ttl_security", "neighbor.timers", ] wneigh = want.pop("neighbor", {}) hneigh = have.pop("neighbor", {}) self._compare_neigh_lists(wneigh, hneigh) for name, entry in wneigh.items(): for k, v in entry.items(): if k == "address": continue if hneigh.get(name): h = {"address": name, k: hneigh[name].pop(k, {})} else: h = {} self.compare( parsers=parsers, want={ "as_number": want["as_number"], "neighbor": {"address": name, k: v}, }, have={"as_number": want["as_number"], "neighbor": h}, ) for name, entry in hneigh.items(): if name not in wneigh.keys(): if self._check_af(name): msg = "Use the _bgp_address_family module to delete the address_family under neighbor {0}, before replacing/deleting the neighbor.".format( name, ) self._module.fail_json(msg=msg) else: self.commands.append( "delete protocols bgp" + self._asn_mod + " neighbor " + name, ) continue for k, v in entry.items(): self.compare( parsers=parsers, want={}, have={ "as_number": have["as_number"], "neighbor": {"address": name, k: v}, }, ) def _compare_bgp_params(self, want, have): parsers = [ "bgp_params.always_compare_med", "bgp_params.bestpath.as_path", "bgp_params.bestpath.compare_routerid", "bgp_params.bestpath.med", "bgp_params.cluster_id", "bgp_params.confederation", "bgp_params.dampening_half_life", "bgp_params.dampening_max_suppress_time", "bgp_params.dampening_re_use", "bgp_params.dampening_start_suppress_time", "bgp_params.default", "bgp_params.deterministic_med", "bgp_params.disbale_network_import_check", "bgp_params.enforce_first_as", "bgp_params.graceful_restart", "bgp_params.log_neighbor_changes", "bgp_params.no_client_to_client_reflection", "bgp_params.no_fast_external_failover", "bgp_params.routerid", "bgp_params.scan_time", ] wbgp = want.pop("bgp_params", {}) hbgp = have.pop("bgp_params", {}) for name, entry in wbgp.items(): if name == "confederation": if entry != hbgp.pop(name, {}): self.addcmd( { "as_number": want["as_number"], "bgp_params": {name: entry}, }, "bgp_params.confederation", False, ) elif name == "distance": if entry != hbgp.pop(name, {}): distance_parsers = [ "bgp_params.distance.global", "bgp_params.distance.prefix", ] for distance_type in entry: self.compare( parsers=distance_parsers, want={ "as_number": want["as_number"], "bgp_params": {name: distance_type}, }, have={ "as_number": want["as_number"], "bgp_params": {name: hbgp.pop(name, {})}, }, ) else: self.compare( parsers=parsers, want={ "as_number": want["as_number"], "bgp_params": {name: entry}, }, have={ "as_number": want["as_number"], "bgp_params": {name: hbgp.pop(name, {})}, }, ) if not wbgp and hbgp: self.commands.append("delete protocols bgp" + self._asn_mod + " parameters") hbgp = {} for name, entry in hbgp.items(): if name == "confederation": self.commands.append( "delete protocols bgp" + self._asn_mod + " parameters confederation", ) elif name == "distance": distance_parsers = [ "bgp_params.distance.global", "bgp_params.distance.prefix", ] self.compare( parsers=distance_parsers, want={}, have={ "as_number": have["as_number"], "bgp_params": {name: entry[0]}, }, ) else: self.compare( parsers=parsers, want={}, have={ "as_number": have["as_number"], "bgp_params": {name: entry}, }, ) def _compare_neigh_lists(self, want, have): for attrib in [ "distribute_list", "filter_list", "prefix_list", "route_map", ]: wdict = want.pop(attrib, {}) hdict = have.pop(attrib, {}) for key, entry in wdict.items(): if entry != hdict.pop(key, {}): self.addcmd(entry, "neighbor.{0}".format(attrib), False) # remove remaining items in have for replaced for entry in hdict.values(): self.addcmd(entry, "neighbor.{0}".format(attrib), True) def _bgp_global_list_to_dict(self, entry): for name, proc in entry.items(): if "neighbor" in proc: neigh_dict = {} for entry in proc.get("neighbor", []): neigh_dict.update({entry["address"]: entry}) proc["neighbor"] = neigh_dict if "network" in proc: network_dict = {} for entry in proc.get("network", []): network_dict.update({entry["address"]: entry}) proc["network"] = network_dict if "aggregate_address" in proc: agg_dict = {} for entry in proc.get("aggregate_address", []): agg_dict.update({entry["prefix"]: entry}) proc["aggregate_address"] = agg_dict if "redistribute" in proc: redis_dict = {} for entry in proc.get("redistribute", []): redis_dict.update({entry["protocol"]: entry}) proc["redistribute"] = redis_dict def _compare_asn(self, want, have): if want.get("as_number") and not have.get("as_number"): self.commands.append( "set protocols bgp " + "system-as" + " " + str(want.get("as_number")), ) def _check_af(self, neighbor): af_present = False if self._connection: config_lines = self._get_config(self._connection).splitlines() for line in config_lines: if neighbor in line: if "address-family" in line: af_present = True return af_present def _get_config(self, connection): return connection.get( 'show configuration commands | match "set protocols bgp .*neighbor"', ) diff --git a/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py index d4e173c1..fcacb913 100644 --- a/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py +++ b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py @@ -1,810 +1,812 @@ # # -*- 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_firewall_global 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 +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 ( remove_empties, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( in_target_not_none, list_diff_want_only, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( get_os_version, load_config, ) class Firewall_global(ConfigBase): """ The vyos_firewall_global class """ gather_subset = ["!all", "!min"] gather_network_resources = ["firewall_global"] def __init__(self, module): super(Firewall_global, self).__init__(module) def get_firewall_global_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, ) firewall_global_facts = facts["ansible_network_resources"].get("firewall_global") if not firewall_global_facts: return [] return firewall_global_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() try: self._module.params["comment"] except KeyError: comment = [] else: comment = self._module.params["comment"] if self.state in self.ACTION_STATES: existing_firewall_global_facts = self.get_firewall_global_facts() else: existing_firewall_global_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_firewall_global_facts)) if commands and self._module._diff: commit = not self._module.check_mode diff = load_config(self._module, commands, commit=commit, comment=comment) if diff: result["diff"] = {"prepared": str(diff)} 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_firewall_global_facts = self.get_firewall_global_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_firewall_global_facts(data=running_config) else: changed_firewall_global_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_firewall_global_facts if result["changed"]: result["after"] = changed_firewall_global_facts elif self.state == "gathered": result["gathered"] = changed_firewall_global_facts result["warnings"] = warnings return result def set_config(self, existing_firewall_global_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_firewall_global_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", "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(want=None, have=h)) elif w: if self.state == "merged" or self.state == "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, w, h): """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 h: commands.extend(self._state_deleted(h, w)) commands.extend(self._state_merged(w, h)) 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._add_global_attr(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 = [] b_set = ( "config_trap", "validation", "log_martians", "syn_cookies", "twa_hazards_protection", ) if want: for key, val in want.items(): if val and key in b_set and not have: commands.append(self._form_attr_cmd(attr=key, opr=False)) elif val and key in b_set and have and key in have and have[key] != val: commands.append(self._form_attr_cmd(attr=key, opr=False)) else: commands.extend(self._render_attr_config(want, have, key)) elif not want and have: commands.append(self._compute_command(opr=False)) elif have: for key, val in have.items(): if val and key in b_set: commands.append(self._form_attr_cmd(attr=key, opr=False)) else: commands.extend(self._render_attr_config(want, have, key)) return commands def _render_attr_config(self, w, h, key, opr=False): """ 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 == "ping": commands.extend(self._render_ping(key, w, h, opr=opr)) elif key == "group": commands.extend(self._render_group(key, w, h, opr=opr)) elif key == "state_policy": commands.extend(self._render_state_policy(key, w, h, opr=opr)) elif key == "route_redirects": commands.extend(self._render_route_redirects(key, w, h, opr=opr)) return commands def _add_global_attr(self, w, h, opr=True): """ This function forms the set/delete commands based on the 'opr' type for firewall_global attributes. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated commands list. """ commands = [] w_fg = deepcopy(remove_empties(w)) l_set = ( "config_trap", "validation", "log_martians", "syn_cookies", "twa_hazards_protection", ) if w_fg: for key, val in w_fg.items(): if opr and key in l_set and not (h and self._is_w_same(w_fg, h, key)): commands.append( self._form_attr_cmd(attr=key, val=self._bool_to_str(val), opr=opr), ) elif not opr: if key and self._is_del(l_set, h): commands.append( self._form_attr_cmd(attr=key, key=self._bool_to_str(val), opr=opr), ) continue if key in l_set and not self._in_target(h, key) and not self._is_del(l_set, h): commands.append( self._form_attr_cmd(attr=key, val=self._bool_to_str(val), opr=opr), ) else: commands.extend(self._render_attr_config(w_fg, h, key, opr)) return commands def _render_ping(self, attr, w, h, opr): """ This function forms the commands for 'ping' attributes based on the 'opr'. :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_ping = {} l_set = ("all", "broadcast") if h: h_ping = h.get(attr) or {} if self._is_root_del(w[attr], h_ping, attr): for item, value in h[attr].items(): if not opr and item in l_set: commands.append(self._form_attr_cmd(attr=item, opr=opr)) elif w[attr]: if h and attr in h.keys(): h_ping = h.get(attr) or {} for item, value in w[attr].items(): if ( opr and item in l_set and not (h_ping and self._is_w_same(w[attr], h_ping, item)) ): commands.append( self._form_attr_cmd(attr=item, val=self._bool_to_str(value), opr=opr), ) elif ( not opr and item in l_set and not (h_ping and self._is_w_same(w[attr], h_ping, item)) ): commands.append(self._form_attr_cmd(attr=item, opr=opr)) return commands def _render_group(self, attr, w, h, opr): """ This function forms the commands for 'group' attribute based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param opr: True/False. :return: generated list of commands. """ commands = [] h_grp = {} if not opr and self._is_root_del(h, w, attr): commands.append(self._form_attr_cmd(attr=attr, opr=opr)) else: if h: h_grp = h.get("group") or {} if w: commands.extend(self._render_grp_mem("port_group", w["group"], h_grp, opr)) commands.extend(self._render_grp_mem("address_group", w["group"], h_grp, opr)) commands.extend(self._render_grp_mem("network_group", w["group"], h_grp, opr)) return commands def _render_grp_mem(self, attr, w, h, opr): """ This function forms the commands for group list/members attributes based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] h_grp = [] w_grp = [] l_set = ("name", "description") if w: w_grp = w.get(attr) or [] if h: h_grp = h.get(attr) or [] if w_grp: for want in w_grp: h = self.search_attrib_in_have(h_grp, want, "name") if "afi" in want and want["afi"] == "ipv6": cmd = self._compute_command(key="group", attr="ipv6-" + attr, opr=opr) else: cmd = self._compute_command(key="group", attr=attr, opr=opr) for key, val in want.items(): if val: if opr and key in l_set and not (h and self._is_w_same(want, h, key)): if key == "name": commands.append(cmd + " " + str(val)) else: commands.append( cmd + " " + want["name"] + " " + key + " '" + str(want[key]) + "'", ) elif not opr and key in l_set: if key == "name" and self._is_grp_del(h, want, key): - if len(commands) > 0 and commands[-1] == cmd + " " + want["name"] + " " + self._grp_type( + if len(commands) > 0 and commands[-1] == cmd + " " + want[ + "name" + ] + " " + self._grp_type( attr, ): commands.pop() commands.append(cmd + " " + want["name"]) continue if not (h and in_target_not_none(h, key)) and not self._is_grp_del( h, want, "name", ): commands.append(cmd + " " + want["name"] + " " + key) elif key == "members": commands.extend( self._render_ports_addrs( key, want, h, opr, cmd, want["name"], attr, ), ) return commands def _render_ports_addrs(self, attr, w, h, opr, cmd, name, type): """ This function forms the commands for port/address/network group members based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param name: name of group. :param type: group type. :return: generated list of commands. """ commands = [] have = [] if w: want = w.get(attr) or [] if h: have = h.get(attr) or [] if want: if opr: members = list_diff_want_only(want, have) for member in members: commands.append( cmd + " " + name + " " + self._grp_type(type) + " " + member[self._get_mem_type(type)], ) elif not opr and have: members = list_diff_want_only(want, have) for member in members: commands.append( cmd + " " + name + " " + self._grp_type(type) + " " + member[self._get_mem_type(type)], ) elif not opr and not have: commands.append( cmd + " " + name + " " + self._grp_type(type), ) return commands def _get_mem_type(self, group): """ This function returns the member type based on the type of group. """ return "port" if group == "port_group" else "address" def _render_state_policy(self, attr, w, h, opr): """ This function forms the commands for 'state-policy' attributes based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] have = [] if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): l_set = ("log", "action", "connection_type", "log_level") else: l_set = ("log", "action", "connection_type") if not opr and self._is_root_del(h, w, attr): commands.append(self._form_attr_cmd(attr=attr, opr=opr)) else: w_sp = deepcopy(remove_empties(w)) want = w_sp.get(attr) or [] if h: have = h.get(attr) or [] if want: for w in want: h = self.search_attrib_in_have(have, w, "connection_type") for key, val in w.items(): if val and key != "connection_type": if opr and key in l_set and not (h and self._is_w_same(w, h, key)): if key == "log" and LooseVersion( get_os_version(self._module), ) >= LooseVersion("1.4"): commands.append( self._form_attr_cmd( key=attr + " " + w["connection_type"], attr=key, opr=opr, ), ) else: commands.append( self._form_attr_cmd( key=attr + " " + w["connection_type"], attr=key, val=self._bool_to_str(val), opr=opr, ), ) elif not opr and key in l_set: if not h: commands.append( self._form_attr_cmd( attr=attr + " " + w["connection_type"], opr=opr, ), ) break # delete the whole thing and move on if (not self._in_target(h, key) or h[key] is None) and ( self._in_target(w, key) and w[key] ): # delete if not being replaced and value currently exists commands.append( self._form_attr_cmd( attr=attr + " " + w["connection_type"] + " " + key, val=self._bool_to_str(val), opr=opr, ), ) return commands def _render_route_redirects(self, attr, w, h, opr): """ This function forms the commands for 'route_redirects' attributes based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] have = [] l_set = ("afi", "ip_src_route") if w: want = w.get(attr) or [] if h: have = h.get(attr) or [] if want: for w in want: h = self.search_attrib_in_have(have, w, "afi") if "afi" in w: afi = w["afi"] else: if h and "afi" in h: afi = h["afi"] else: afi = None afi = None for key, val in w.items(): if val is not None and key != "afi": if opr and key in l_set and not (h and self._is_w_same(w, h, key)): commands.append( self._form_attr_cmd( attr=key, val=self._bool_to_str(val), opr=opr, type=afi, ), ) elif not opr and key in l_set: if self._is_del(l_set, h): commands.append( self._form_attr_cmd( attr=key, val=self._bool_to_str(val), opr=opr, type=afi, ), ) continue if not (h and self._in_target(h, key)) and not self._is_del(l_set, h): commands.append( self._form_attr_cmd( attr=key, val=self._bool_to_str(val), opr=opr, type=afi, ), ) elif key == "icmp_redirects": commands.extend(self._render_icmp_redirects(key, w, h, opr)) return commands def _render_icmp_redirects(self, attr, w, h, opr): """ This function forms the commands for 'icmp_redirects' attributes based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] h_red = {} l_set = ("send", "receive") if w and "afi" in w: afi = w["afi"] else: if h and "afi" in h: afi = h["afi"] else: afi = None if w[attr]: if h and attr in h.keys(): h_red = h.get(attr) or {} for item, value in w[attr].items(): if opr and item in l_set and not (h_red and self._is_w_same(w[attr], h_red, item)): commands.append( self._form_attr_cmd( attr=item, val=self._bool_to_str(value), opr=opr, type=afi, ), ) elif ( not opr and item in l_set and not (h_red and self._is_w_same(w[attr], h_red, item)) ): commands.append(self._form_attr_cmd(attr=item, opr=opr, type=afi)) return commands def search_attrib_in_have(self, have, want, attr): """ This function returns the attribute if it is present in target config. :param have: the target config. :param want: the desired config. :param attr: attribute name . :return: attribute/None """ if have: for h in have: if h[attr] == want[attr]: return h return None def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True, type=None): """ This function forms the command for leaf attribute. :param key: parent key. :param attr: attribute name :param value: value :param opr: True/False. :param type: AF type of attribute. :return: generated command. """ command = self._compute_command( key=key, attr=self._map_attrib(attr, type=type), val=val, opr=opr, ) return command 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 remove: True/False. :param opr: True/False. :return: generated command. """ if remove or not opr: cmd = "delete firewall " else: cmd = "set firewall " if ( attr and key != "group" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") ): cmd += "global-options " if key: cmd += key.replace("_", "-") + " " if attr: cmd += attr.replace("_", "-") if val and opr: if key == "state_policy" and LooseVersion(get_os_version(self._module)) >= LooseVersion( "1.4", ): cmd += "" else: cmd += " '" + str(val) + "'" return cmd.strip() def _bool_to_str(self, 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 _grp_type(self, val): """ This function returns the group member type based on value argument. :param val: value. :return: member type. """ return ( "address" if val == "address_group" else "network" if val == "network_group" else "port" ) def _is_w_same(self, 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(self, 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 def _is_grp_del(self, w, h, key): """ This function checks whether group needed to be deleted based on desired and target configs. :param w: the desired config. :param h: the target config. :param key: group name. :return: True/False. """ return True if h and key in h and (not w or key not in w or not w[key]) else False def _is_root_del(self, w, h, key): """ This function checks whether a root attribute which can have further child attributes needed to be deleted. :param w: the desired config. :param h: the target config. :param key: attribute name. :return: True/False. """ return True if h and key in h and (not w or key not in w or not w[key]) else False def _is_del(self, b_set, h, key="number"): """ This function checks whether attribute needs to be deleted when operation is false and attribute present in present target config. :param b_set: attribute set. :param h: target config. :param key: number. :return: True/False. """ return key in b_set and not self._in_target(h, key) def _map_attrib(self, attrib, type=None): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ regex = attrib.replace("_", "-") if attrib == "send": if type == "ipv6": regex = "ipv6-send-redirects" else: regex = "send-redirects" elif attrib == "ip_src_route": if type == "ipv6": regex = "ipv6-src-route" elif attrib == "receive": if type == "ipv6": regex = "ipv6-receive-redirects" else: regex = "receive-redirects" elif attrib == "disabled": regex = "disable" elif attrib == "all": regex = "all-ping" elif attrib == "broadcast": regex = "broadcast-ping" elif attrib == "validation": regex = "source-validation" return regex diff --git a/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py b/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py index 85a8042f..ec1aaef1 100644 --- a/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py +++ b/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py @@ -1,431 +1,431 @@ # # -*- 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_firewall_interfaces 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 +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 ( remove_empties, search_obj_in_list, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( get_interface_type, ) class Firewall_interfaces(ConfigBase): """ The vyos_firewall_interfaces class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "firewall_interfaces", ] def __init__(self, module): super(Firewall_interfaces, self).__init__(module) def get_firewall_interfaces_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, ) firewall_interfaces_facts = facts["ansible_network_resources"].get("firewall_interfaces") if not firewall_interfaces_facts: return [] return firewall_interfaces_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_firewall_interfaces_facts = self.get_firewall_interfaces_facts() else: existing_firewall_interfaces_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_firewall_interfaces_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_firewall_interfaces_facts = self.get_firewall_interfaces_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_firewall_interfaces_facts(data=running_config) else: changed_firewall_interfaces_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_firewall_interfaces_facts if result["changed"]: result["after"] = changed_firewall_interfaces_facts elif self.state == "gathered": result["gathered"] = changed_firewall_interfaces_facts result["warnings"] = warnings return result def set_config(self, existing_firewall_interfaces_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_firewall_interfaces_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, w, h): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if self.state in ("merged", "replaced", "overridden", "rendered") and not w: self._module.fail_json( msg="value of config parameter must not be empty for state {0}".format(self.state), ) if self.state == "overridden": commands.extend(self._state_overridden(w, h)) elif self.state == "deleted": commands.extend(self._state_deleted(w, h)) elif w: if self.state == "merged" or self.state == "rendered": 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: for h in have: w = search_obj_in_list(h["name"], want) commands.extend(self._render_access_rules(h, w, opr=False)) commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: for h_ar in have: w_ar = search_obj_in_list(h_ar["name"], want) if not w_ar and "access_rules" in h_ar: commands.append(self._compute_command(name=h_ar["name"], opr=False)) else: h_rules = h_ar.get("access_rules") or [] key = "direction" if w_ar: w_rules = w_ar.get("access_rules") or [] if not w_rules and h_rules: commands.append(self._compute_command(name=h_ar["name"], opr=False)) if h_rules: for h_rule in h_rules: w_rule = search_obj_in_list(h_rule["afi"], w_rules, key="afi") have_rules = h_rule.get("rules") or [] if w_rule: want_rules = w_rule.get("rules") or [] for h in have_rules: if key in h: w = search_obj_in_list(h[key], want_rules, key=key) if ( not w or key not in w or ("name" in h and w and "name" not in w) ): commands.append( self._compute_command( afi=h_rule["afi"], name=h_ar["name"], attrib=h[key], opr=False, ), ) commands.extend(self._state_merged(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 = [] for w in want: h = search_obj_in_list(w["name"], have) commands.extend(self._render_access_rules(w, h)) 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: h = search_obj_in_list(w["name"], have) if h and "access_rules" in h: commands.extend(self._delete_access_rules(w, h, opr=False)) elif have: for h in have: if "access_rules" in h: commands.append(self._compute_command(name=h["name"], opr=False)) return commands def _delete_access_rules(self, want, have, opr=False): """ This function forms the delete commands based on the 'opr' type for 'access_rules' attributes. :param want: desired config. :param have: target config. :param opr: True/False. :return: generated commands list. """ commands = [] h_rules = {} w_rs = deepcopy(remove_empties(want)) w_rules = w_rs.get("access_rules") or [] if have: h_rs = deepcopy(remove_empties(have)) h_rules = h_rs.get("access_rules") or [] # if all firewall config needed to be deleted for specific interface # when operation is delete. if not w_rules and h_rules: commands.append(self._compute_command(name=want["name"], opr=opr)) if w_rules: for w in w_rules: h = search_obj_in_list(w["afi"], h_rules, key="afi") commands.extend(self._delete_rules(want["name"], w, h)) return commands def _delete_rules(self, name, want, have, opr=False): """ This function forms the delete commands based on the 'opr' type for rules attributes. :param name: interface id/name. :param want: desired config. :param have: target config. :param opr: True/False. :return: generated commands list. """ commands = [] h_rules = [] key = "direction" w_rules = want.get("rules") or [] if have: h_rules = have.get("rules") or [] # when rule set needed to be removed on # (inbound|outbound|local interface) if h_rules and not w_rules: for h in h_rules: if key in h: commands.append( self._compute_command(afi=want["afi"], name=name, attrib=h[key], opr=opr), ) for w in w_rules: h = search_obj_in_list(w[key], h_rules, key=key) if ( key in w and h and key in h and "name" in w and "name" in h and w["name"] == h["name"] ): commands.append( self._compute_command( afi=want["afi"], name=name, attrib=w[key], value=w["name"], opr=opr, ), ) return commands def _render_access_rules(self, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for 'access_rules' attributes. :param want: desired config. :param have: target config. :param opr: True/False. :return: generated commands list. """ commands = [] h_rules = {} w_rs = deepcopy(remove_empties(want)) w_rules = w_rs.get("access_rules") or [] if have: h_rs = deepcopy(remove_empties(have)) h_rules = h_rs.get("access_rules") or [] if w_rules: for w in w_rules: h = search_obj_in_list(w["afi"], h_rules, key="afi") commands.extend(self._render_rules(want["name"], w, h, opr)) return commands def _render_rules(self, name, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for rules attributes. :param name: interface id/name. :param want: desired config. :param have: target config. :param opr: True/False. :return: generated commands list. """ commands = [] h_rules = [] key = "direction" w_rules = want.get("rules") or [] if have: h_rules = have.get("rules") or [] for w in w_rules: h = search_obj_in_list(w[key], h_rules, key=key) if key in w: if opr: if "name" in w and not (h and h[key] == w[key] and h["name"] == w["name"]): commands.append( self._compute_command( afi=want["afi"], name=name, attrib=w[key], value=w["name"], ), ) elif not (h and key in h): commands.append( self._compute_command(afi=want["afi"], name=name, attrib=w[key]), ) elif not opr: if not h or key not in h or ("name" in w and h and "name" not in h): commands.append( self._compute_command( afi=want["afi"], name=name, attrib=w[key], opr=opr, ), ) return commands def _compute_command(self, afi=None, name=None, attrib=None, value=None, opr=True): """ This function construct the add/delete command based on passed attributes. :param afi: address type. :param name: interface name. :param attrib: attribute name. :param value: attribute value. :param opr: operation flag. :return: generated command. """ # Append vif if interface contains a dot vlan = None interface_real = name if "." in name: interface_real, vlan = name.split(".") if vlan is not None: interface_real = interface_real + " vif " + vlan # if interface name is bondX, then it's a bonding interface. Everything else is an ethernet iftype = get_interface_type(interface_real) if not opr: cmd = "delete interfaces " + iftype + " " + interface_real + " firewall" else: cmd = "set interfaces " + iftype + " " + interface_real + " firewall" if attrib: cmd += " " + attrib if afi: cmd += " " + self._get_fw_type(afi) if value: cmd += " '" + str(value) + "'" return cmd def _get_fw_type(self, afi): """ This function returns the firewall rule-set type based on IP address. :param afi: address type :return: rule-set type. """ return "ipv6-name" if afi == "ipv6" else "name" diff --git a/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py index 3c768e32..be36d3fe 100644 --- a/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py +++ b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py @@ -1,1186 +1,1186 @@ # # -*- 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_firewall_rules 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 +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 ( remove_empties, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( list_diff_want_only, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( get_os_version, load_config, ) class Firewall_rules(ConfigBase): """ The vyos_firewall_rules class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "firewall_rules", ] def __init__(self, module): super(Firewall_rules, self).__init__(module) def get_firewall_rules_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, ) firewall_rules_facts = facts["ansible_network_resources"].get("firewall_rules") if not firewall_rules_facts: return [] return firewall_rules_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() diff = None try: self._module.params["comment"] except KeyError: comment = [] else: comment = self._module.params["comment"] if self.state in self.ACTION_STATES: existing_firewall_rules_facts = self.get_firewall_rules_facts() else: existing_firewall_rules_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(deepcopy(existing_firewall_rules_facts))) if commands and self._module._diff: commit = not self._module.check_mode diff = load_config(self._module, commands, commit=commit, comment=comment) if diff: result["diff"] = {"prepared": str(diff)} 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_firewall_rules_facts = self.get_firewall_rules_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_firewall_rules_facts(data=running_config) else: changed_firewall_rules_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_firewall_rules_facts if result["changed"]: result["after"] = changed_firewall_rules_facts elif self.state == "gathered": result["gathered"] = changed_firewall_rules_facts result["warnings"] = warnings return result def set_config(self, existing_firewall_rules_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"] self._prune_stubs(want) have = existing_firewall_rules_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, w, h): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if self.state in ("merged", "replaced", "overridden", "rendered") and not w: self._module.fail_json( msg="value of config parameter must not be empty for state {0}".format(self.state), ) if self.state == "overridden": commands.extend(self._state_overridden(w, h)) elif self.state == "deleted": commands.extend(self._state_deleted(w, h)) elif w: if self.state == "merged" or self.state == "rendered": 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: # Iterate over the afi rule sets we already have. for h in have: r_sets = self._get_r_sets(h) # Iterate over each rule set we already have. for rs in r_sets: # In the desired configuration, search for the rule set we # already have (to be replaced by our desired # configuration's rule set). rs_id = self._rs_id(rs, h["afi"]) wanted_rule_set = self.search_r_sets_in_have(want, rs_id, "r_list") if self._is_same_rs(remove_empties(wanted_rule_set), remove_empties(rs)): continue if wanted_rule_set is not None: # Remove the rules that we already have if the wanted # rules exist under the same name. commands.extend( self._add_r_sets( h["afi"], want=rs, have=wanted_rule_set, opr=False, ), ) # Merge the desired configuration into what we already have. commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: for h in have: have_r_sets = self._get_r_sets(h) for rs in have_r_sets: rs_id = self._rs_id(rs, h["afi"]) w = self.search_r_sets_in_have(want, rs_id, "r_list") if self._is_same_rs(remove_empties(w), remove_empties(rs)): continue else: commands.append(self._compute_command(rs_id, remove=True)) # Blank out the only rule set that it is removed. for entry in have: if entry["afi"] == rs_id["afi"] and rs_id["name"]: entry["rule_sets"] = [ rule_set for rule_set in entry["rule_sets"] if rule_set.get("name") != rs_id["name"] ] elif entry["afi"] == rs_id["afi"] and rs_id["filter"]: entry["rule_sets"] = [ rule_set for rule_set in entry["rule_sets"] if rule_set.get("filter") != rs_id["filter"] ] commands.extend(self._state_merged(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 = [] for w in want: r_sets = self._get_r_sets(w) for rs in r_sets: rs_id = self._rs_id(rs, w["afi"]) h = self.search_r_sets_in_have(have, rs_id, "r_list") if self._is_same_rs(remove_empties(h), remove_empties(rs)): continue else: commands.extend(self._add_r_sets(w["afi"], rs, h)) 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: r_sets = self._get_r_sets(w) if r_sets: for rs in r_sets: rs_id = self._rs_id(rs, w["afi"]) h = self.search_r_sets_in_have(have, rs_id, "r_list") if h: commands.append(self._compute_command(rs_id, remove=True)) elif have: for h in have: if h["afi"] == w["afi"]: commands.append( self._compute_command(self._rs_id(None, w["afi"]), remove=True), ) elif have: for h in have: r_sets = self._get_r_sets(h) if r_sets: commands.append(self._compute_command(self._rs_id(None, h["afi"]), remove=True)) return commands def _add_r_sets(self, afi, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for rule-sets attributes. :param afi: address type. :param want: desired config. :param have: target config. :param opr: True/False. :return: generated commands list. """ commands = [] l_set = ("description", "default_action", "default_jump_target", "enable_default_log") h_rs = {} h_rules = {} w_rs = deepcopy(remove_empties(want)) w_rules = w_rs.pop("rules", None) rs_id = self._rs_id(want, afi=afi) if have: h_rs = deepcopy(remove_empties(have)) h_rules = h_rs.pop("rules", None) if w_rs: for key, val in w_rs.items(): if opr and key in l_set and not (h_rs and self._is_w_same(w_rs, h_rs, key)): if key == "enable_default_log": if val and (not h_rs or key not in h_rs or not h_rs[key]): commands.append(self._add_rs_base_attrib(rs_id, key, w_rs)) else: commands.append(self._add_rs_base_attrib(rs_id, key, w_rs)) elif not opr and key in l_set: if ( key == "enable_default_log" and val and h_rs and (key not in h_rs or not h_rs[key]) ): commands.append(self._add_rs_base_attrib(rs_id, key, w_rs, opr)) elif not (h_rs and self._in_target(h_rs, key)): commands.append(self._add_rs_base_attrib(rs_id, key, w_rs, opr)) commands.extend(self._add_rules(rs_id, w_rules, h_rules, opr)) if h_rules: have["rules"] = h_rules if w_rules: want["rules"] = w_rules return commands def _add_rules(self, rs_id, w_rules, h_rules, opr=True): """ This function forms the set/delete commands based on the 'opr' type for rules attributes. :param rs_id: rule-set identifier. :param w_rules: desired config. :param h_rules: target config. :param opr: True/False. :return: generated commands list. """ commands = [] l_set = ( "ipsec", "action", "number", "protocol", "fragment", "disable", "description", "jump_target", "offload_target", ) if w_rules: for w in w_rules: cmd = self._compute_command(rs_id, w["number"], opr=opr) h = self.search_rules_in_have_rs(h_rules, w["number"]) if w != h and self.state == "replaced": h = {} for key, val in w.items(): if val: if opr and key in l_set and not (h and self._is_w_same(w, h, key)): if key == "disable": if not (not val and (not h or key not in h or not h[key])): commands.append(self._add_r_base_attrib(rs_id, key, w)) else: commands.append(self._add_r_base_attrib(rs_id, key, w)) elif not opr: # Note: if you are experiencing sticky configuration on replace # you may need to add an explicit check for the key here. Anything that # doesn't have a custom operation is taken care of by the `l_set` check # below, but I'm not sure how any of the others work. # It's possible that historically the delete was forced (but now it's # checked). if key == "number" and self._is_del(l_set, h): commands.append(self._add_r_base_attrib(rs_id, key, w, opr=opr)) continue if ( key == "tcp" and val and h and (key not in h or not h[key] or h[key] != w[key]) ): commands.extend(self._add_tcp(key, w, h, cmd, opr)) if ( key == "state" and val and h and (key not in h or not h[key] or h[key] != w[key]) ): commands.extend(self._add_state(key, w, h, cmd, opr)) if ( key == "icmp" and val and h and (key not in h or not h[key] or h[key] != w[key]) ): commands.extend(self._add_icmp(key, w, h, cmd, opr)) if ( key in ("packet_length", "packet_length_exclude") and val and h and (key not in h or not h[key] or h[key] != w[key]) ): commands.extend(self._add_packet_length(key, w, h, cmd, opr)) elif key == "disable" and val and h and (key not in h or not h[key]): commands.append(self._add_r_base_attrib(rs_id, key, w, opr=opr)) if ( key in ("inbound_interface", "outbound_interface") and val and h and (key not in h or not h[key] or h[key] != w[key]) ): commands.extend(self._add_interface(key, w, h, cmd, opr)) elif ( key in l_set and not (h and self._in_target(h, key)) and not self._is_del(l_set, h) ): commands.append(self._add_r_base_attrib(rs_id, key, w, opr=opr)) elif key == "tcp": commands.extend(self._add_tcp(key, w, h, cmd, opr)) elif key == "time": commands.extend(self._add_time(key, w, h, cmd, opr)) elif key == "icmp": commands.extend(self._add_icmp(key, w, h, cmd, opr)) elif key == "state": commands.extend(self._add_state(key, w, h, cmd, opr)) elif key == "log": commands.extend(self._add_log(key, w, h, cmd, opr)) elif key == "limit": commands.extend(self._add_limit(key, w, h, cmd, opr)) elif key == "recent": commands.extend(self._add_recent(key, w, h, cmd, opr)) elif key == "destination" or key == "source": commands.extend(self._add_src_or_dest(key, w, h, cmd, opr)) elif key in ("packet_length", "packet_length_exclude"): commands.extend(self._add_packet_length(key, w, h, cmd, opr)) elif key in ("inbound_interface", "outbound_interface"): commands.extend(self._add_interface(key, w, h, cmd, opr)) return commands def _add_state(self, attr, w, h, cmd, opr): """ This function forms the command for 'state' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ h_state = {} commands = [] l_set = ("new", "invalid", "related", "established") if w[attr]: if h and attr in h.keys(): h_state = h.get(attr) or {} for item, val in w[attr].items(): if ( opr and item in l_set and not (h_state and self._is_w_same(w[attr], h_state, item)) ): if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): commands.append(cmd + (" " + attr + " " + item)) else: commands.append( cmd + (" " + attr + " " + item + " " + self._bool_to_str(val)), ) elif not opr and item in l_set and not self._in_target(h_state, item): commands.append(cmd + (" " + attr + " " + item)) return commands def _add_log(self, attr, w, h, cmd, opr): """ This function forms the command for 'log' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ h_state = {} commands = [] if w[attr]: if h and attr in h.keys(): h_state = h.get(attr) or {} if ( LooseVersion(get_os_version(self._module)) < LooseVersion("1.4") and opr and not (h and self._is_w_same(w, h, attr)) ): commands.append(cmd + " " + attr + " '" + w[attr] + "'") elif ( LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") and opr and not (h and self._is_w_same(w, h, attr)) ): commands.append(cmd + " " + attr) elif not opr and not self._in_target(h_state, w[attr]): commands.append(cmd + (" " + attr + " '" + w[attr] + "'")) return commands def _add_recent(self, attr, w, h, cmd, opr): """ This function forms the command for 'recent' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_recent = {} l_set = ("count", "time") if w[attr]: if h and attr in h.keys(): h_recent = h.get(attr) or {} for item, val in w[attr].items(): if ( opr and item in l_set and not (h_recent and self._is_w_same(w[attr], h_recent, item)) ): commands.append(cmd + (" " + attr + " " + item + " " + str(val))) elif ( not opr and item in l_set and not (h_recent and self._in_target(h_recent, item)) ): commands.append(cmd + (" " + attr + " " + item)) return commands def _add_icmp(self, attr, w, h, cmd, opr): """ This function forms the commands for 'icmp' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_icmp = {} l_set = ("code", "type", "type_name") if w[attr]: if h and attr in h.keys(): h_icmp = h.get(attr) or {} for item, val in w[attr].items(): if ( opr and item in l_set and not (h_icmp and self._is_w_same(w[attr], h_icmp, item)) ): if item == "type_name": if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): param_name = "type-name" else: param_name = "type" if "ipv6" in cmd: # ipv6-name or ipv6 commands.append(cmd + (" " + "icmpv6" + " " + param_name + " " + val)) else: commands.append( cmd + (" " + attr + " " + item.replace("_", "-") + " " + val), ) else: if "ipv6" in cmd: # ipv6-name or ipv6 commands.append(cmd + (" " + "icmpv6" + " " + item + " " + str(val))) else: commands.append(cmd + (" " + attr + " " + item + " " + str(val))) elif not opr and item in l_set and not self._in_target(h_icmp, item): commands.append( cmd + (" " + attr + " " + item.replace("_", "-") + " " + str(val)), ) return commands def _add_interface(self, attr, w, h, cmd, opr): """ This function forms the commands for 'interface' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_if = {} l_set = ("name", "group") if w[attr]: if h and attr in h.keys(): h_if = h.get(attr) or {} for item, val in w[attr].items(): if opr and item in l_set and not (h_if and self._is_w_same(w[attr], h_if, item)): commands.append( cmd + (" " + attr.replace("_", "-") + " " + item.replace("_", "-") + " " + val), ) elif not opr and item in l_set and not (h_if and self._in_target(h_if, item)): commands.append( cmd + (" " + attr.replace("_", "-") + " " + item.replace("_", "-")), ) return commands def _add_time(self, attr, w, h, cmd, opr): """ This function forms the commands for 'time' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_time = {} l_set = ( "utc", "stopdate", "stoptime", "weekdays", "monthdays", "startdate", "starttime", ) if w[attr]: if h and attr in h.keys(): h_time = h.get(attr) or {} for item, val in w[attr].items(): if ( opr and item in l_set and not (h_time and self._is_w_same(w[attr], h_time, item)) ): if item == "utc": if not (not val and (not h_time or item not in h_time)): commands.append(cmd + (" " + attr + " " + item)) else: commands.append(cmd + (" " + attr + " " + item + " " + val)) elif ( not opr and item in l_set and not (h_time and self._is_w_same(w[attr], h_time, item)) ): commands.append(cmd + (" " + attr + " " + item)) return commands def _add_tcp_1_4(self, attr, w, h, cmd, opr): """ This function forms the commands for 'tcp' attributes based on the 'opr'. Version 1.4+ :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] have = [] key = "flags" want = [] if w: if w.get(attr): want = w.get(attr).get(key) or [] if h: if h.get(attr): have = h.get(attr).get(key) or [] if want: if opr: flags = list_diff_want_only(want, have) for flag in flags: invert = flag.get("invert", False) commands.append( cmd + (" " + attr + " flags " + ("not " if invert else "") + flag["flag"]), ) elif not opr: flags = list_diff_want_only(want, have) for flag in flags: invert = flag.get("invert", False) commands.append( cmd + (" " + attr + " flags " + ("not " if invert else "") + flag["flag"]), ) return commands def _add_packet_length(self, attr, w, h, cmd, opr): """ This function forms the commands for 'packet_length[_exclude]' attributes based on the 'opr'. If < 1.4, handle tcp attributes. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] have = [] want = [] if w: if w.get(attr): want = w.get(attr) or [] if h: if h.get(attr): have = h.get(attr) or [] attr = attr.replace("_", "-") if want: if opr: lengths = list_diff_want_only(want, have) for l_rec in lengths: commands.append(cmd + " " + attr + " " + str(l_rec["length"])) elif not opr: lengths = list_diff_want_only(want, have) for l_rec in lengths: commands.append(cmd + " " + attr + " " + str(l_rec["length"])) return commands def _tcp_flags_string(self, flags): """ This function forms the tcp flags string. :param flags: flags list. :return: flags string or None. """ if not flags: return "" flag_str = "" for flag in flags: this_flag = flag["flag"].upper() if flag.get("invert", False): this_flag = "!" + this_flag if len(flag_str) > 0: flag_str = ",".join([flag_str, this_flag]) else: flag_str = this_flag return flag_str def _add_tcp(self, attr, w, h, cmd, opr): """ This function forms the commands for 'tcp' attributes based on the 'opr'. If < 1.4, handle tcp attributes. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): return self._add_tcp_1_4(attr, w, h, cmd, opr) h_tcp = {} commands = [] if w[attr]: key = "flags" flags = w[attr].get(key) or {} if flags: if h and key in h[attr].keys(): h_tcp = h[attr].get(key) or {} if flags: flag_str = self._tcp_flags_string(flags) if opr and not (h_tcp and flags == h_tcp): commands.append(cmd + (" " + attr + " " + "flags" + " " + flag_str)) if not opr and not (h_tcp and flags == h_tcp): commands.append(cmd + (" " + attr + " " + "flags" + " " + flag_str)) return commands def _add_limit(self, attr, w, h, cmd, opr): """ This function forms the commands for 'limit' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ h_limit = {} commands = [] if w[attr]: key = "burst" if ( opr and key in w[attr].keys() and not (h and attr in h.keys() and self._is_w_same(w[attr], h[attr], key)) ): commands.append(cmd + (" " + attr + " " + key + " " + str(w[attr].get(key)))) elif ( not opr and key in w[attr].keys() and not (h and attr in h.keys() and self._in_target(h[attr], key)) ): commands.append(cmd + (" " + attr + " " + key + " " + str(w[attr].get(key)))) key = "rate" rate = w[attr].get(key) or {} if rate: if h and key in h[attr].keys(): h_limit = h[attr].get(key) or {} if "unit" in rate and "number" in rate: if opr and not ( h_limit and self._is_w_same(rate, h_limit, "unit") and self._is_w_same(rate, h_limit, "number") ): commands.append( cmd + ( " " + attr + " " + key + " " + str(rate["number"]) + "/" + rate["unit"] ), ) if not opr and not ( h_limit and self._is_w_same(rate, h_limit, "unit") and self._is_w_same(rate, h_limit, "number") ): commands.append(cmd + (" " + attr + " " + key)) return commands def _add_src_or_dest(self, attr, w, h, cmd, opr=True): """ This function forms the commands for 'src/dest' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_group = {} g_set = ("port_group", "address_group", "network_group") if w[attr]: keys = ("address", "mac_address", "port") for key in keys: if ( opr and key in w[attr].keys() and not (h and attr in h.keys() and self._is_w_same(w[attr], h[attr], key)) ): commands.append( cmd + (" " + attr + " " + key.replace("_", "-") + " " + w[attr].get(key)), ) elif ( not opr and key in w[attr].keys() and not (h and attr in h.keys() and self._in_target(h[attr], key)) ): commands.append(cmd + (" " + attr + " " + key)) key = "group" group = w[attr].get(key) or {} if group: h_group = {} if h and h.get(attr) and key in h[attr].keys(): h_group = h[attr].get(key) for item, val in group.items(): if val: if ( opr and item in g_set and not (h_group and self._is_w_same(group, h_group, item)) ): commands.append( cmd + ( " " + attr + " " + key + " " + item.replace("_", "-") + " " + val ), ) elif ( not opr and item in g_set and not (h_group and self._in_target(h_group, item)) ): commands.append( cmd + (" " + attr + " " + key + " " + item.replace("_", "-")), ) return commands def search_rules_in_have_rs(self, have_rules, r_number): """ This function returns the rule if it is present in target config. :param have: target config. :param rs_id: rule-set identifier. :param r_number: rule-number. :return: rule. """ if have_rules: key = "number" for r in have_rules: if key in r and r[key] == r_number: return r return None def search_r_sets_in_have(self, have, rs_id, type="rule_sets"): """ This function returns the rule-set/rule if it is present in target config. :param have: target config. :param rs_id: rule-identifier. :param type: rule_sets if searching a rule_set and r_list if searching from a rule_list. :return: rule-set/rule. """ if "afi" in rs_id: afi = rs_id["afi"] else: afi = None if rs_id["filter"]: key = "filter" w_value = rs_id["filter"] elif rs_id["name"]: key = "name" w_value = rs_id["name"] else: raise ValueError("id must be specific to name or filter") if type not in ("r_list", "rule_sets"): raise ValueError("type must be rule_sets or r_list") if have: if type == "r_list": for h in have: if h["afi"] == afi: r_sets = self._get_r_sets(h) for rs in r_sets: if key in rs and rs[key] == w_value: return rs else: # searching a ruleset for rs in have: if key in rs and rs[key] == w_value: return rs return None def _get_r_sets(self, item): """ This function returns the list of rule-sets. :param item: config dictionary. :return: list of rule-sets/rules. """ rs_list = [] type = "rule_sets" r_sets = item[type] if r_sets: for rs in r_sets: rs_list.append(rs) return rs_list def _compute_command( self, rs_id, number=None, attrib=None, value=None, remove=False, opr=True, ): """ This function construct the add/delete command based on passed attributes. :param rs_id: rule-set identifier. :param number: rule-number. :param attrib: attribute name. :param value: value. :param remove: True if delete command needed to be construct. :param opr: operation flag. :return: generated command. """ if rs_id["name"] and rs_id["filter"]: raise ValueError("name and filter cannot be used together") if remove or not opr: cmd = "delete firewall " + self._get_fw_type(rs_id["afi"]) else: cmd = "set firewall " + self._get_fw_type(rs_id["afi"]) if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): if rs_id["name"]: cmd += " name " + rs_id["name"] elif rs_id["filter"]: cmd += " " + rs_id["filter"] + " filter" elif rs_id["name"]: cmd += " " + rs_id["name"] if number: cmd += " rule " + str(number) if attrib: if ( LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") and attrib == "enable_default_log" ): cmd += " " + "default-log" else: cmd += " " + attrib.replace("_", "-") if value and opr and attrib != "enable_default_log" and attrib != "disable": cmd += " '" + str(value) + "'" return cmd def _add_r_base_attrib(self, rs_id, attr, rule, opr=True): """ This function forms the command for 'rules' attributes which doesn't have further sub attributes. :param rs_id: rule-set identifier. :param attrib: attribute name :param rule: rule config dictionary. :param opr: True/False. :return: generated command. """ if attr == "number": command = self._compute_command(rs_id, number=rule["number"], opr=opr) else: command = self._compute_command( rs_id=rs_id, number=rule["number"], attrib=attr, value=rule[attr], opr=opr, ) return command def _rs_id(self, have, afi, name=None, filter=None): """ This function returns the rule-set identifier based on the example rule, overriding the components as specified. :param have: example rule. :param afi: address type. :param name: rule-set name. :param filter: filter name. :return: rule-set identifier. """ identifier = {"name": None, "filter": None} if afi: identifier["afi"] = afi else: raise ValueError("afi must be provided") if name: identifier["name"] = name return identifier elif filter: identifier["filter"] = filter return identifier if have: if "name" in have and have["name"]: identifier["name"] = have["name"] return identifier if "filter" in have and have["filter"]: identifier["filter"] = have["filter"] return identifier # raise ValueError("name or filter must be provided or present in have") # unless we want a wildcard return identifier def _add_rs_base_attrib(self, rs_id, attrib, rule, opr=True): """ This function forms the command for 'rule-sets' attributes which don't have further sub attributes. :param rs_id: rule-set identifier. :param attrib: attribute name :param rule: rule config dictionary. :param opr: True/False. :return: generated command. """ command = self._compute_command( rs_id=rs_id, attrib=attrib, value=rule[attrib], opr=opr, ) return command def _bool_to_str(self, val): """ This function converts the bool value into string. :param val: bool value. :return: enable/disable. """ return "enable" if val else "disable" def _get_fw_type(self, afi): """ This function returns the firewall rule-set type based on IP address. :param afi: address type :return: rule-set type. """ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): return "ipv6" if afi == "ipv6" else "ipv4" return "ipv6-name" if afi == "ipv6" else "name" def _is_del(self, l_set, h, key="number"): """ This function checks whether rule needs to be deleted based on the rule number. :param l_set: attribute set. :param h: target config. :param key: number. :return: True/False. """ return key in l_set and not (h and self._in_target(h, key)) def _is_w_same(self, w, h, key): """ This function checks whether the key value is same in base 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(self, h, key): """ This function checks whether the target exists 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 def _prune_stubs(self, rs): if isinstance(rs, list): for item in rs: self._prune_stubs(item) elif isinstance(rs, dict): keys_to_remove = [ key for key, value in rs.items() if ( (key == "disable" and value is False) or ( key == "log" and value == "disable" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") ) or ( key in ["new", "invalid", "related", "established"] and value is False and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") ) ) ] for key in keys_to_remove: del rs[key] for key in rs: self._prune_stubs(rs[key]) def _is_same_rs(self, w, rs): if isinstance(w, dict) and isinstance(rs, dict): if w.keys() != rs.keys(): return False for key in w: if not self._is_same_rs(w[key], rs[key]): return False return True elif isinstance(w, list) and isinstance(rs, list): try: def comparison(x): if "name" in x: return x["name"] if "number" in x: return x["number"] return str(x) sorted_list1 = sorted(w, key=comparison) sorted_list2 = sorted(rs, key=comparison) except TypeError: return False if len(sorted_list1) != len(sorted_list2): return False return all(self._is_same_rs(x, y) for x, y in zip(sorted_list1, sorted_list2)) else: return w == rs diff --git a/plugins/module_utils/network/vyos/config/hostname/hostname.py b/plugins/module_utils/network/vyos/config/hostname/hostname.py index 36aba74f..8b30a693 100644 --- a/plugins/module_utils/network/vyos/config/hostname/hostname.py +++ b/plugins/module_utils/network/vyos/config/hostname/hostname.py @@ -1,75 +1,74 @@ # # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos_hostname config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.hostname import ( HostnameTemplate, ) class Hostname(ResourceModule): """ The vyos_hostname config class """ def __init__(self, module): super(Hostname, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="hostname", tmplt=HostnameTemplate(), ) self.parsers = [] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = self.want haved = self.have if self.state == "deleted": wantd = {} self._compare(want=wantd, have=haved) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Hostname network resource. """ self.compare(parsers="hostname", want=want, have=have) diff --git a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py index 0a55564e..944f7f42 100644 --- a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py +++ b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py @@ -1,340 +1,339 @@ # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_interfaces 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 ( dict_diff, remove_empties, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( dict_delete, get_interface_type, search_obj_in_list, ) class Interfaces(ConfigBase): """ The vyos_interfaces class """ gather_subset = [ "!all", "!min", ] gather_network_resources = ["interfaces"] def __init__(self, module): super(Interfaces, self).__init__(module) def get_interfaces_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, ) interfaces_facts = facts["ansible_network_resources"].get("interfaces") if not interfaces_facts: return [] return interfaces_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} commands = list() warnings = list() if self.state in self.ACTION_STATES: existing_interfaces_facts = self.get_interfaces_facts() else: existing_interfaces_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_interfaces_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_interfaces_facts = self.get_interfaces_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_interfaces_facts(data=running_config) else: changed_interfaces_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_interfaces_facts if result["changed"]: result["after"] = changed_interfaces_facts elif self.state == "gathered": result["gathered"] = changed_interfaces_facts result["warnings"] = warnings return result def set_config(self, existing_interfaces_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_interfaces_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """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 want: 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(want=want, have=have)) elif self.state == "deleted": if not want: for intf in have: commands.extend(self._state_deleted({"name": intf["name"]}, intf)) else: for item in want: obj_in_have = search_obj_in_list(item["name"], have) commands.extend(self._state_deleted(item, obj_in_have)) else: for item in want: name = item["name"] enable_state = item["enabled"] obj_in_have = search_obj_in_list(name, have) if not obj_in_have: obj_in_have = {"name": name, "enabled": enable_state} if self.state in ("merged", "rendered"): commands.extend(self._state_merged(item, obj_in_have)) elif self.state == "replaced": commands.extend(self._state_replaced(item, obj_in_have)) 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._state_deleted(want, have)) commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] for intf in have: intf_in_want = search_obj_in_list(intf["name"], want) if not intf_in_want: commands.extend(self._state_deleted({"name": intf["name"]}, intf)) for intf in want: intf_in_have = search_obj_in_list(intf["name"], have) if not intf_in_have: intf_in_have = { "name": intf["name"], "enabled": intf["enabled"], } commands.extend(self._state_replaced(intf, intf_in_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 = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(have) want_vifs = want_copy.pop("vifs", []) have_vifs = have_copy.pop("vifs", []) updates = dict_diff(have_copy, want_copy) if updates: for key, value in updates.items(): commands.append( self._compute_commands(key=key, value=value, interface=want_copy["name"]), ) if want_vifs: for want_vif in want_vifs: have_vif = search_obj_in_list(want_vif["vlan_id"], have_vifs, key="vlan_id") if not have_vif: have_vif = { "vlan_id": want_vif["vlan_id"], "enabled": True, } vif_updates = dict_diff(have_vif, want_vif) if vif_updates: for key, value in vif_updates.items(): commands.append( self._compute_commands( key=key, value=value, interface=want_copy["name"], vif=want_vif["vlan_id"], ), ) 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 = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(have) want_vifs = want_copy.pop("vifs", []) have_vifs = have_copy.pop("vifs", []) for key in dict_delete(have_copy, want_copy).keys(): if key == "enabled": continue commands.append( self._compute_commands(key=key, interface=want_copy["name"], remove=True), ) if have_copy["enabled"] is False and not ( "enabled" in want_copy and want_copy["enabled"] is False ): commands.append( self._compute_commands(key="enabled", value=True, interface=want_copy["name"]), ) if have_vifs: for have_vif in have_vifs: want_vif = search_obj_in_list(have_vif["vlan_id"], want_vifs, key="vlan_id") if not want_vif: commands.append( self._compute_commands( key="", interface=want_copy["name"], vif=have_vif["vlan_id"], remove=True, ), ) continue for key in dict_delete(have_vif, want_vif).keys(): if key == "enabled": continue commands.append( self._compute_commands( key=key, interface=want_copy["name"], vif=want_vif["vlan_id"], remove=True, ), ) return commands def _compute_commands(self, interface, key, vif=None, value=None, remove=False): interface_type = get_interface_type(interface) if not interface_type: self._module.fail_json( msg="interface {0} is not a valid interface type".format(interface), ) intf_context = "interfaces {0} {1}".format(interface_type, interface) set_cmd = "set {0}".format(intf_context) del_cmd = "delete {0}".format(intf_context) if vif: set_cmd = set_cmd + (" vif {0}".format(vif)) del_cmd = del_cmd + (" vif {0}".format(vif)) if key == "" or key is None: if not remove: command = "{0}".format(set_cmd) else: command = "{0}".format(del_cmd) elif key == "enabled": if not value: command = "{0} disable".format(set_cmd) else: command = "{0} disable".format(del_cmd) else: if not remove: command = "{0} {1} '{2}'".format(set_cmd, key, value) else: command = "{0} {1}".format(del_cmd, key) return command diff --git a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py index a23494cd..862e0272 100644 --- a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py +++ b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py @@ -1,354 +1,353 @@ # # -*- 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_l3_interfaces 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 ( remove_empties, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( diff_list_of_dicts, get_interface_type, search_obj_in_list, ) class L3_interfaces(ConfigBase): """ The vyos_l3_interfaces class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "l3_interfaces", ] def __init__(self, module): super(L3_interfaces, self).__init__(module) def get_l3_interfaces_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, ) l3_interfaces_facts = facts["ansible_network_resources"].get("l3_interfaces") if not l3_interfaces_facts: return [] return l3_interfaces_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_l3_interfaces_facts = self.mutate_autoconfig(self.get_l3_interfaces_facts()) else: existing_l3_interfaces_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_l3_interfaces_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_l3_interfaces_facts = self.mutate_autoconfig(self.get_l3_interfaces_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.mutate_autoconfig( self.get_l3_interfaces_facts(data=running_config), ) else: changed_l3_interfaces_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_l3_interfaces_facts if result["changed"]: result["after"] = changed_l3_interfaces_facts elif self.state == "gathered": result["gathered"] = changed_l3_interfaces_facts result["warnings"] = warnings return result def set_config(self, existing_l3_interfaces_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_l3_interfaces_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """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 = [] state = self._module.params["state"] if state in ("merged", "replaced", "overridden", "rendered") and not want: self._module.fail_json( msg="value of config parameter must not be empty for state {0}".format(state), ) if state == "overridden": commands.extend(self._state_overridden(want=want, have=have)) elif state == "deleted": if not want: for intf in have: commands.extend(self._state_deleted({"name": intf["name"]}, intf)) else: for item in want: obj_in_have = search_obj_in_list(item["name"], have) commands.extend(self._state_deleted(item, obj_in_have)) else: for item in want: name = item["name"] obj_in_have = search_obj_in_list(name, have) if not obj_in_have: obj_in_have = {"name": item["name"]} if state in ("merged", "rendered"): commands.extend(self._state_merged(item, obj_in_have)) elif state == "replaced": commands.extend(self._state_replaced(item, obj_in_have)) commands = [command.replace("auto-config", "autoconf") for command in commands] 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._state_deleted(want, have)) commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] for intf in have: intf_in_want = search_obj_in_list(intf["name"], want) if not intf_in_want: commands.extend(self._state_deleted({"name": intf["name"]}, intf)) for intf in want: intf_in_have = search_obj_in_list(intf["name"], have) commands.extend(self._state_replaced(intf, intf_in_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 = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(remove_empties(have)) want_vifs = want_copy.pop("vifs", []) have_vifs = have_copy.pop("vifs", []) for update in self._get_updates(want_copy, have_copy): for key, value in update.items(): commands.append( self._compute_commands(key=key, value=value, interface=want_copy["name"]), ) if want_vifs: for want_vif in want_vifs: have_vif = search_obj_in_list(want_vif["vlan_id"], have_vifs, key="vlan_id") if not have_vif: have_vif = {} for update in self._get_updates(want_vif, have_vif): for key, value in update.items(): commands.append( self._compute_commands( key=key, value=value, interface=want_copy["name"], vif=want_vif["vlan_id"], ), ) 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 = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(have) if have_copy is not None: if all(v in (None, {}, []) for k, v in want_copy.items() if k != "name"): commands.append( self._compute_commands( key=None, value=None, interface=want_copy["name"], remove=True, ), ) return commands want_vifs = want_copy.pop("vifs", []) have_vifs = have_copy.pop("vifs", []) if have_vifs: for have_vif in have_vifs: want_vif = search_obj_in_list(have_vif["vlan_id"], want_vifs, key="vlan_id") if not want_vif: want_vif = {"vlan_id": have_vif["vlan_id"]} for update in self._get_updates(have_vif, want_vif): for key, value in update.items(): commands.append( self._compute_commands( key=key, interface=want_copy["name"], value=value, vif=want_vif["vlan_id"], remove=True, ), ) for update in self._get_updates(have_copy, want_copy): for key, value in update.items(): commands.append( self._compute_commands( key=key, value=value, interface=want_copy["name"], remove=True, ), ) return commands def _compute_commands(self, interface, key, vif=None, value=None, remove=False): if value == "auto-config" and vif is None: intf_context = "interfaces {0} {1} ipv6".format( get_interface_type(interface), interface, ) else: intf_context = "interfaces {0} {1}".format(get_interface_type(interface), interface) set_cmd = "set {0}".format(intf_context) del_cmd = "delete {0}".format(intf_context) if vif: suffix = " ipv6" if value == "auto-config" else "" set_cmd += f" vif {vif}{suffix}" del_cmd += f" vif {vif}{suffix}" if remove and key and value: command = "{0} {1} '{2}'".format(del_cmd, key, value) elif remove and not (key and value): command = "{0}".format(del_cmd) else: command = "{0} {1} '{2}'".format(set_cmd, key, value) return command def _get_updates(self, want, have): updates = [] updates = diff_list_of_dicts(want.get("ipv4", []), have.get("ipv4", [])) updates.extend(diff_list_of_dicts(want.get("ipv6", []), have.get("ipv6", []))) return updates def mutate_autoconfig(self, obj): if isinstance(obj, dict): return dict(map(lambda kv: (kv[0], self.mutate_autoconfig(kv[1])), obj.items())) if isinstance(obj, list): return list(map(self.mutate_autoconfig, obj)) if isinstance(obj, str): return obj.replace("autoconf", "auto-config") return obj diff --git a/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py index 62270503..203519e4 100644 --- a/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py +++ b/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py @@ -1,433 +1,433 @@ # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_lag_interfaces 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 +from __future__ import absolute_import, division, print_function __metaclass__ = type 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 ( dict_diff, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( get_lst_diff_for_dicts, list_diff_have_only, list_diff_want_only, search_obj_in_list, ) class Lag_interfaces(ConfigBase): """ The vyos_lag_interfaces class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "lag_interfaces", ] params = [ "arp_monitor", "hash_policy", "members", "mode", "name", "primary", ] def __init__(self, module): super(Lag_interfaces, self).__init__(module) def get_lag_interfaces_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, ) lag_interfaces_facts = facts["ansible_network_resources"].get("lag_interfaces") if not lag_interfaces_facts: return [] return lag_interfaces_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_lag_interfaces_facts = self.get_lag_interfaces_facts() else: existing_lag_interfaces_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_lag_interfaces_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_lag_interfaces_facts = self.get_lag_interfaces_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_lag_interfaces_facts(data=running_config) else: changed_lag_interfaces_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_lag_interfaces_facts if result["changed"]: result["after"] = changed_lag_interfaces_facts elif self.state == "gathered": result["gathered"] = changed_lag_interfaces_facts result["warnings"] = warnings return result def set_config(self, existing_lag_interfaces_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_lag_interfaces_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """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 want: 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(want, have)) elif self.state == "deleted": if want: for want_item in want: name = want_item["name"] obj_in_have = search_obj_in_list(name, have) commands.extend(self._state_deleted(obj_in_have)) else: for have_item in have: commands.extend(self._state_deleted(have_item)) else: for want_item in want: name = want_item["name"] obj_in_have = search_obj_in_list(name, have) if self.state in ("merged", "rendered"): commands.extend(self._state_merged(want_item, obj_in_have)) elif self.state == "replaced": commands.extend(self._state_replaced(want_item, obj_in_have)) 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_del_commands(want, have)) commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] for have_item in have: lag_name = have_item["name"] obj_in_want = search_obj_in_list(lag_name, want) if not obj_in_want: commands.extend(self._purge_attribs(have_item)) for want_item in want: name = want_item["name"] obj_in_have = search_obj_in_list(name, have) commands.extend(self._state_replaced(want_item, obj_in_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 = [] if have: commands.extend(self._render_updates(want, have)) else: commands.extend(self._render_set_commands(want)) return commands def _state_deleted(self, 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.extend(self._purge_attribs(have)) return commands def _render_updates(self, want, have): commands = [] temp_have_members = have.pop("members", None) temp_want_members = want.pop("members", None) updates = dict_diff(have, want) if temp_have_members: have["members"] = temp_have_members if temp_want_members: want["members"] = temp_want_members commands.extend(self._add_bond_members(want, have)) if updates: for key, value in updates.items(): if value: if key == "arp_monitor": commands.extend(self._add_arp_monitor(updates, key, want, have)) else: commands.append(self._compute_command(have["name"], key, str(value))) return commands def _render_set_commands(self, want): commands = [] have = [] params = Lag_interfaces.params for attrib in params: value = want[attrib] if value: if attrib == "arp_monitor": commands.extend(self._add_arp_monitor(want, attrib, want, have)) elif attrib == "members": commands.extend(self._add_bond_members(want, have)) elif attrib != "name": commands.append(self._compute_command(want["name"], attrib, value=str(value))) return commands def _purge_attribs(self, have): commands = [] for item in Lag_interfaces.params: if have.get(item): if item == "members": commands.extend(self._delete_bond_members(have)) elif item != "name": commands.append(self._compute_command(have["name"], attrib=item, remove=True)) return commands def _render_del_commands(self, want, have): commands = [] params = Lag_interfaces.params for attrib in params: if attrib == "members": commands.extend(self._update_bond_members(attrib, want, have)) elif attrib == "arp_monitor": commands.extend(self._update_arp_monitor(attrib, want, have)) elif have.get(attrib) and not want.get(attrib): commands.append(self._compute_command(have["name"], attrib, remove=True)) return commands def _add_bond_members(self, want, have): commands = [] diff_members = get_lst_diff_for_dicts(want, have, "members") if diff_members: for key in diff_members: commands.append( self._compute_command( want["name"], "member interface", key["member"], ), ) return commands def _add_arp_monitor(self, updates, key, want, have): commands = [] arp_monitor = updates.get(key) or {} diff_targets = self._get_arp_monitor_target_diff(want, have, key, "target") if "interval" in arp_monitor: commands.append( self._compute_command( key=want["name"] + " arp-monitor", attrib="interval", value=str(arp_monitor["interval"]), ), ) if diff_targets: for target in diff_targets: commands.append( self._compute_command( key=want["name"] + " arp-monitor", attrib="target", value=target, ), ) return commands def _delete_bond_members(self, have): commands = [] for member in have["members"]: commands.append( self._compute_command( have["name"], "member interface", member["member"], remove=True, ), ) return commands def _update_arp_monitor(self, key, want, have): commands = [] want_arp_target = [] have_arp_target = [] want_arp_monitor = want.get(key) or {} have_arp_monitor = have.get(key) or {} if want_arp_monitor and "target" in want_arp_monitor: want_arp_target = want_arp_monitor["target"] if have_arp_monitor and "target" in have_arp_monitor: have_arp_target = have_arp_monitor["target"] if "interval" in have_arp_monitor and not want_arp_monitor: commands.append( self._compute_command( key=have["name"] + " arp-monitor", attrib="interval", remove=True, ), ) if "target" in have_arp_monitor: target_diff = list_diff_have_only(want_arp_target, have_arp_target) if target_diff: for target in target_diff: commands.append( self._compute_command( key=have["name"] + " arp-monitor", attrib="target", value=target, remove=True, ), ) return commands def _update_bond_members(self, key, want, have): commands = [] want_members = want.get(key) or [] have_members = have.get(key) or [] members_diff = list_diff_have_only(want_members, have_members) if members_diff: for member in members_diff: commands.append( self._compute_command( member["member"], "bond-group", have["name"], True, "ethernet", ), ) return commands def _get_arp_monitor_target_diff(self, want_list, have_list, dict_name, lst): want_arp_target = [] have_arp_target = [] want_arp_monitor = want_list.get(dict_name) or {} if want_arp_monitor and lst in want_arp_monitor: want_arp_target = want_arp_monitor[lst] if not have_list: diff = want_arp_target else: have_arp_monitor = have_list.get(dict_name) or {} if have_arp_monitor and lst in have_arp_monitor: have_arp_target = have_arp_monitor[lst] diff = list_diff_want_only(want_arp_target, have_arp_target) return diff def _compute_command(self, key, attrib, value=None, remove=False, type="bonding"): if remove: cmd = "delete interfaces " + type else: cmd = "set interfaces " + type cmd += " " + key if attrib == "arp_monitor": attrib = "arp-monitor" elif attrib == "hash_policy": attrib = "hash-policy" cmd += " " + attrib if value: cmd += " '" + value + "'" return cmd diff --git a/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py b/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py index 39724f1b..82a35a54 100644 --- a/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py +++ b/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py @@ -1,307 +1,307 @@ # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_lldp_global 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 +from __future__ import absolute_import, division, print_function __metaclass__ = type 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 ( dict_diff, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( get_lst_diff_for_dicts, list_diff_have_only, ) class Lldp_global(ConfigBase): """ The vyos_lldp_global class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "lldp_global", ] # address intentionally omitted since it's coerced to addresses params = ["enable", "addresses", "snmp", "legacy_protocols"] def __init__(self, module): super(Lldp_global, self).__init__(module) def get_lldp_global_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, ) lldp_global_facts = facts["ansible_network_resources"].get("lldp_global") if not lldp_global_facts: return [] return lldp_global_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() # fix for new name/type if self._module.params["config"]: temp_have_address = self._module.params["config"].pop("address", None) if temp_have_address: self._module.params["config"]["addresses"] = [temp_have_address] if self.state in self.ACTION_STATES: existing_lldp_global_facts = self.get_lldp_global_facts() else: existing_lldp_global_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_lldp_global_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_lldp_global_facts = self.get_lldp_global_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_lldp_global_facts(data=running_config) else: changed_lldp_global_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_lldp_global_facts if result["changed"]: result["after"] = changed_lldp_global_facts elif self.state == "gathered": result["gathered"] = changed_lldp_global_facts result["warnings"] = warnings return result def set_config(self, existing_lldp_global_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_lldp_global_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """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", "rendered") and not want: 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(want=None, have=have)) elif self.state in ("merged", "rendered"): commands.extend(self._state_merged(want=want, have=have)) elif self.state == "replaced": commands.extend(self._state_replaced(want=want, have=have)) 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._state_deleted(want, have)) commands.extend(self._state_merged(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_updates(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 item in Lldp_global.params: if item == "legacy_protocols": commands.extend(self._update_lldp_protocols(want, have)) if item == "addresses": commands.extend(self._update_management_addresses(want, have)) elif have.get(item) and not want.get(item) and item != "enable": commands.append(Lldp_global.del_cmd + item) elif have: if have.get("enable"): commands.append(self._compute_command(remove=True)) return commands for item in Lldp_global.params: if have.get(item): if item == "legacy_protocols": commands.append(self._compute_command("legacy-protocols", remove=True)) elif item == "addresses": commands.append(self._compute_command("management-address", remove=True)) elif item == "snmp": commands.append(self._compute_command(item, remove=True)) return commands def _render_updates(self, want, have): commands = [] if have: temp_have_legacy_protos = have.pop("legacy_protocols", None) temp_have_addreses = have.pop("addresses", None) temp_have_address = have.pop("address", None) if temp_have_address: temp_have_addresses = [temp_have_address] else: have = {} temp_want_legacy_protos = want.pop("legacy_protocols", None) temp_want_addreses = want.pop("addresses", None) temp_want_address = want.pop("address", None) if temp_want_address: temp_want_addresses = [temp_want_address] updates = dict_diff(have, want) if have and temp_have_legacy_protos: have["legacy_protocols"] = temp_have_legacy_protos if not have and temp_want_legacy_protos: want["legacy_protocols"] = temp_want_legacy_protos if have and temp_have_addreses: have["addresses"] = temp_have_addreses if not have and temp_want_addreses: want["addresses"] = temp_want_addreses commands.extend(self._add_lldp_protocols(want, have)) commands.extend(self._add_management_addresses(want, have)) if updates: for key, value in updates.items(): if value is not None: if key == "enable": if value is False: commands.append(self._compute_command(remove=True)) return commands else: commands.append(self._compute_command()) elif key == "snmp": if value == "disable": commands.append(self._compute_command(key, remove=True)) else: commands.append(self._compute_command(key, str(value))) return commands def _add_lldp_protocols(self, want, have): commands = [] diff_members = get_lst_diff_for_dicts(want, have, "legacy_protocols") if diff_members: for key in diff_members: commands.append(self._compute_command("legacy-protocols", key)) return commands def _add_management_addresses(self, want, have): commands = [] diff_members = get_lst_diff_for_dicts(want, have, "addresses") if diff_members: for key in diff_members: commands.append(self._compute_command("management-address", key)) return commands def _update_lldp_protocols(self, want_item, have_item): commands = [] want_protocols = want_item.get("legacy_protocols") or [] have_protocols = have_item.get("legacy_protocols") or [] members_diff = list_diff_have_only(want_protocols, have_protocols) if members_diff: for member in members_diff: commands.append(self._compute_command("legacy-protocols", member, remove=True)) return commands def _update_management_addresses(self, want_item, have_item): commands = [] want_addresses = want_item.get("addresses") or [] have_addresses = have_item.get("addresses") or [] members_diff = list_diff_have_only(want_addresses, have_addresses) if members_diff: for member in members_diff: commands.append(self._compute_command("management-address", member, remove=True)) return commands def _compute_command(self, key=None, value=None, remove=False): if remove: cmd = "delete service lldp" else: cmd = "set service lldp" if key: cmd += " " + key if value: cmd += " '" + value + "'" return cmd diff --git a/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py b/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py index 97fcbce5..36bf1b2d 100644 --- a/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py +++ b/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py @@ -1,348 +1,347 @@ # # -*- 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_lldp_interfaces 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 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 ( dict_diff, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( is_dict_element_present, key_value_in_dict, search_obj_in_list, ) class Lldp_interfaces(ConfigBase): """ The vyos_lldp_interfaces class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "lldp_interfaces", ] params = ["enable", "location", "name"] def __init__(self, module): super(Lldp_interfaces, self).__init__(module) def get_lldp_interfaces_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, ) lldp_interfaces_facts = facts["ansible_network_resources"].get("lldp_interfaces") if not lldp_interfaces_facts: return [] return lldp_interfaces_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_lldp_interfaces_facts = self.get_lldp_interfaces_facts() else: existing_lldp_interfaces_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_lldp_interfaces_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_lldp_interfaces_facts = self.get_lldp_interfaces_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_lldp_interfaces_facts(data=running_config) else: changed_lldp_interfaces_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_lldp_interfaces_facts if result["changed"]: result["after"] = changed_lldp_interfaces_facts elif self.state == "gathered": result["gathered"] = changed_lldp_interfaces_facts result["warnings"] = warnings return result def set_config(self, existing_lldp_interfaces_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_lldp_interfaces_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """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 want: 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(want=want, have=have)) elif self.state == "deleted": if want: for item in want: name = item["name"] have_item = search_obj_in_list(name, have) commands.extend(self._state_deleted(want=None, have=have_item)) else: for have_item in have: commands.extend(self._state_deleted(want=None, have=have_item)) else: for want_item in want: name = want_item["name"] have_item = search_obj_in_list(name, have) if self.state in ("merged", "rendered"): commands.extend(self._state_merged(want=want_item, have=have_item)) if self.state == "replaced": commands.extend(self._state_replaced(want=want_item, have=have_item)) 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._state_deleted(want, have)) commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] for have_item in have: lldp_name = have_item["name"] lldp_in_want = search_obj_in_list(lldp_name, want) if not lldp_in_want: commands.append(self._compute_command(have_item["name"], remove=True)) for want_item in want: name = want_item["name"] lldp_in_have = search_obj_in_list(name, have) commands.extend(self._state_replaced(want_item, lldp_in_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 = [] if have: commands.extend(self._render_updates(want, have)) else: commands.extend(self._render_set_commands(want)) 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: params = Lldp_interfaces.params for attrib in params: if attrib == "location": commands.extend(self._update_location(have["name"], want, have)) elif have: commands.append(self._compute_command(have["name"], remove=True)) return commands def _render_updates(self, want, have): commands = [] lldp_name = have["name"] commands.extend(self._configure_status(lldp_name, want, have)) commands.extend(self._add_location(lldp_name, want, have)) return commands def _render_set_commands(self, want): commands = [] have = {} lldp_name = want["name"] params = Lldp_interfaces.params for attrib in params: value = want[attrib] if value: if attrib == "location": commands.extend(self._add_location(lldp_name, want, have)) elif attrib == "enable": if not value: commands.append(self._compute_command(lldp_name, value="disable")) else: commands.append(self._compute_command(lldp_name)) return commands def _configure_status(self, name, want_item, have_item): commands = [] if is_dict_element_present(have_item, "enable"): temp_have_item = False else: temp_have_item = True if want_item["enable"] != temp_have_item: if want_item["enable"]: commands.append(self._compute_command(name, value="disable", remove=True)) else: commands.append(self._compute_command(name, value="disable")) return commands def _add_location(self, name, want_item, have_item): commands = [] have_dict = {} have_ca = {} set_cmd = name + " location " want_location_type = want_item.get("location") or {} have_location_type = have_item.get("location") or {} if want_location_type["coordinate_based"]: want_dict = want_location_type.get("coordinate_based") or {} if is_dict_element_present(have_location_type, "coordinate_based"): have_dict = have_location_type.get("coordinate_based") or {} location_type = "coordinate-based" updates = dict_diff(have_dict, want_dict) for key, value in updates.items(): if value: commands.append(self._compute_command(set_cmd + location_type, key, str(value))) elif want_location_type["elin"]: location_type = "elin" if is_dict_element_present(have_location_type, "elin"): if want_location_type.get("elin") != have_location_type.get("elin"): commands.append( self._compute_command( set_cmd + location_type, value=str(want_location_type["elin"]), ), ) else: commands.append( self._compute_command( set_cmd + location_type, value=str(want_location_type["elin"]), ), ) return commands def _update_location(self, name, want_item, have_item): commands = [] del_cmd = name + " location" want_location_type = want_item.get("location") or {} have_location_type = have_item.get("location") or {} if want_location_type["coordinate_based"]: want_dict = want_location_type.get("coordinate_based") or {} if is_dict_element_present(have_location_type, "coordinate_based"): have_dict = have_location_type.get("coordinate_based") or {} location_type = "coordinate-based" for key, value in have_dict.items(): only_in_have = key_value_in_dict(key, value, want_dict) if not only_in_have: commands.append( self._compute_command(del_cmd + location_type, key, str(value), True), ) else: commands.append(self._compute_command(del_cmd, remove=True)) else: if is_dict_element_present(have_location_type, "elin"): if want_location_type.get("elin") != have_location_type.get("elin"): commands.append(self._compute_command(del_cmd, remove=True)) else: commands.append(self._compute_command(del_cmd, remove=True)) return commands def _compute_command(self, key, attrib=None, value=None, remove=False): if remove: cmd = "delete service lldp interface " else: cmd = "set service lldp interface " cmd += key if attrib: cmd += " " + attrib if value: cmd += " '" + value + "'" return cmd diff --git a/plugins/module_utils/network/vyos/config/logging_global/logging_global.py b/plugins/module_utils/network/vyos/config/logging_global/logging_global.py index 35a2624a..8bc9d1f9 100644 --- a/plugins/module_utils/network/vyos/config/logging_global/logging_global.py +++ b/plugins/module_utils/network/vyos/config/logging_global/logging_global.py @@ -1,222 +1,221 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos_logging_global config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( dict_merge, get_from_dict, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.logging_global import ( Logging_globalTemplate, ) class Logging_global(ResourceModule): """ The vyos_logging_global config class """ def __init__(self, module): super(Logging_global, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="logging_global", tmplt=Logging_globalTemplate(), ) self.parsers = [ "console.facilities", "global_params.archive.file_num", "global_params.archive.size", "global_params.marker_interval", "global_params.preserve_fqdn", "global_params.facilities", "files.archive.size", "files.archive.file_num", "files", "hosts.port", "hosts.facility.protocol", # 1.3 and below "hosts.protocol", "hosts", "users", ] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ if self.want: wantd = self.list_to_dict(self.want, "want") else: wantd = dict() if self.have: haved = self.list_to_dict(self.have, "have") else: haved = dict() if self.state in ["overridden", "replaced"]: if wantd != haved: wantx, havex = self.call_op(wantd, haved, "overridden") for k, have in havex.items(): if k not in wantx: self._compare(want={}, have=have) if not self.state == "deleted": wantd, haved = self.call_op(wantd, haved) if self.state == "merged": wantd = dict_merge(haved, wantd) for k, want in wantd.items(): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Logging_global network resource. """ self.compare(parsers=self.parsers, want=want, have=have) self.handleStates(want=want, have=have) def operation_rep(self, params): op_val = dict() for k, val in params.items(): if k in ["console", "global_params"]: mod_val = deepcopy(val) op_val.update(self.flatten_facility({k: mod_val})) elif k in ["files", "hosts", "users"]: for m, n in val.items(): mod_n = deepcopy(n) if mod_n.get("archive"): del mod_n["archive"] if mod_n.get("facilities"): del mod_n["facilities"] if mod_n.get("port"): del mod_n["port"] tm = self.flatten_facility({k: {m: mod_n}}) op_val.update(tm) return op_val def call_op(self, _want, _have, mode=None): if mode == "overridden": w = self.operation_rep(_want) h = self.operation_rep(_have) else: w = self.flatten_facility(_want) h = self.flatten_facility(_have) return w, h def handleStates(self, want=None, have=None): stateparsers = [ "syslog.state", "console.state", "global_params.state", "global_params.archive.state", "files.archive.state", ] for par in stateparsers: op = get_from_dict(want, par) if op == "enabled": self.addcmd(want, par) elif op == "disabled": self.addcmd(want, par, True) break def flatten_facility(self, param): temp_param = dict() for element, val in param.items(): if element in ["console", "global_params", "syslog"]: if element != "syslog" and val.get("facilities"): for k, v in val.get("facilities").items(): temp_param[k + element] = {element: {"facilities": v}} del val["facilities"] if val: temp_param[element] = {element: val} if element in ["files", "hosts", "users"]: for k, v in val.items(): if v.get("facilities"): for pk, dat in v.get("facilities").items(): temp_param[pk + k] = { element: { "facilities": dat, self.pkey.get(element): v.get(self.pkey.get(element)), }, } del v["facilities"] if len(list(v.keys())) > 1: temp_param[k] = {element: v} else: temp_param[k] = {element: v} return temp_param def list_to_dict(self, param, op=None): updated_param = dict() if self.state == "deleted": if op == "have" and param: self.handleStates({"syslog": {"state": "disabled"}}) updated_param == {} else: self.pkey = { "files": "path", "hosts": "hostname", "users": "username", } for element, val in param.items(): if element == "facilities": # only with recursion call _tem_par = {} for par in val: if par.get("facility") and par.get("severity"): _tem_par.update({par.get("facility") + par.get("severity"): par}) elif par.get("facility") and par.get("protocol"): _tem_par.update({par.get("facility") + par.get("protocol"): par}) else: _tem_par.update({par.get("facility"): par}) return _tem_par elif element in ["console", "global_params", "syslog"]: if element != "syslog" and val.get("facilities"): val["facilities"] = self.list_to_dict(val) updated_param[element] = val elif element in ["hosts", "users", "files"]: for v in val: if v.get("facilities"): v["facilities"] = self.list_to_dict(v) if updated_param.get(element): updated_param[element].update({v.get(self.pkey.get(element)): v}) else: updated_param[element] = {v.get(self.pkey.get(element)): v} return updated_param diff --git a/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py b/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py index 06ec1d6b..78b7a545 100644 --- a/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py +++ b/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py @@ -1,204 +1,203 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos_ntp config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( dict_merge, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ntp_global import ( NtpTemplate, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Ntp_global(ResourceModule): """ The vyos_ntp config class """ def __init__(self, module): super(Ntp_global, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="ntp_global", tmplt=NtpTemplate(), ) self.parsers = [ "allow_clients", "listen_addresses", "server", "options", "allow_clients_delete", "listen_addresses_delete", ] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): path = "service" ac = "allow-client" else: path = "system" ac = "allow-clients" self._tmplt.set_ntp_path(path) self._tmplt.set_ntp_ac(ac) wantd = self._ntp_list_to_dict(self.want) haved = self._ntp_list_to_dict(self.have) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": haved = {k: v for k, v in haved.items() if k in wantd or not wantd} wantd = {} commandlist = self._commandlist(haved) servernames = self._servernames(haved) # removing the servername and commandlist from the list after deleting it from haved # iterate through the top-level items to delete for k, have in haved.items(): if k not in wantd: for hk, hval in have.items(): if hk == "allow_clients" and hk in commandlist: self.commands.append( self._tmplt.render({"": hk}, "allow_clients_delete", True), ) commandlist.remove(hk) elif hk == "listen_addresses" and hk in commandlist: self.commands.append( self._tmplt.render({"": hk}, "listen_addresses_delete", True), ) commandlist.remove(hk) elif hk == "server" and have["server"] in servernames: self._compareoverride(want={}, have=have) servernames.remove(have["server"]) # if everything is deleted add the delete command for {path} ntp # this should be equiv: servernames == [] and commandlist == ["server"]: if wantd == {} and haved != {}: self.commands.append( self._tmplt.render({}, "service_delete", True), ) # remove existing config for overridden and replaced # Getting the list of the server names from haved # to avoid the duplication of overridding/replacing the servers if self.state in ["overridden", "replaced"]: commandlist = self._commandlist(haved) servernames = self._servernames(haved) for k, have in haved.items(): if k not in wantd: if "server" not in have: self._compareoverride(want={}, have=have) # removing the servername from the list after deleting it from haved elif have["server"] in servernames: self._compareoverride(want={}, have=have) servernames.remove(have["server"]) for k, want in wantd.items(): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Ntp network resource. """ if "options" in want: self.compare(parsers="options", want=want, have=have) else: self.compare(parsers=self.parsers, want=want, have=have) def _compareoverride(self, want, have): # do not delete configuration with options level for i, val in have.items(): if i == "options": pass else: self.compare(parsers=i, want={}, have=have) def _ntp_list_to_dict(self, entry): servers_dict = {} for k, data in entry.items(): if k == "servers": for value in data: if "options" in value: result = self._serveroptions_list_to_dict(value) for res, resvalue in result.items(): servers_dict.update({res: resvalue}) else: servers_dict.update({value["server"]: value}) else: for value in data: servers_dict.update({"ip_" + value: {k: value}}) return servers_dict def _serveroptions_list_to_dict(self, entry): serveroptions_dict = {} for Opk, Op in entry.items(): if Opk == "options": for val in Op: dict = {} dict.update({"server": entry["server"]}) dict.update({Opk: val}) serveroptions_dict.update({entry["server"] + "_" + val: dict}) return serveroptions_dict def _commandlist(self, haved): commandlist = [] for k, have in haved.items(): for ck, cval in have.items(): if ck != "options" and ck not in commandlist: commandlist.append(ck) return commandlist def _servernames(self, haved): servernames = [] for k, have in haved.items(): for sk, sval in have.items(): if sk != "options" and sval not in servernames: servernames.append(sval) return servernames diff --git a/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py b/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py index 837a2ddf..bc93ac24 100644 --- a/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py +++ b/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py @@ -1,194 +1,193 @@ # # -*- coding: utf-8 -*- # Copyright 2020 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos_ospf_interfaces config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( dict_merge, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ospf_interfaces import ( Ospf_interfacesTemplate, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ospf_interfaces_14 import ( Ospf_interfacesTemplate14, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Ospf_interfaces(ResourceModule): """ The vyos_ospf_interfaces config class """ def __init__(self, module): super(Ospf_interfaces, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="ospf_interfaces", tmplt=Ospf_interfacesTemplate(), ) self.parsers = [ "authentication_password", "authentication_md5", "bandwidth", "cost", "hello_interval", "dead_interval", "mtu_ignore", "network", "priority", "retransmit_interval", "transmit_delay", "ifmtu", "instance", "passive", ] def _validate_template(self): version = get_os_version(self._module) if LooseVersion(version) >= LooseVersion("1.4"): self._tmplt = Ospf_interfacesTemplate14() else: self._tmplt = Ospf_interfacesTemplate() def parse(self): """override parse to check template""" self._validate_template() return super().parse() def get_parser(self, name): """get_parsers""" self._validate_template() return super().get_parser(name) def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ self._validate_template() if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = {} haved = {} for entry in self.want: wantd.update({entry["name"]: entry}) for entry in self.have: haved.update({entry["name"]: entry}) # turn all lists of dicts into dicts prior to merge for entry in wantd, haved: self._ospf_int_list_to_dict(entry) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": h_del = {} for k, v in haved.items(): if k in wantd or not wantd: h_del.update({k: v}) haved = h_del have_int = [] for k, have in haved.items(): if k in wantd: have_int.append(k) self._remove_ospf_int(have) wantd = {} if self.state == "overridden": have_int = [] for k, have in haved.items(): if k not in wantd: have_int.append(k) self._remove_ospf_int(have) # remove superfluous config for overridden and deleted if self.state in ["overridden", "deleted"]: # removing the interfaces from haved that are already negated for interface in have_int: haved.pop(interface) for k, have in haved.items(): if k not in wantd: self._compare(want={}, have=have) for k, want in wantd.items(): self._compare(want=want, have=haved.pop(k, {})) def _remove_ospf_int(self, entry): int_name = entry.get("name", {}) int_addr = entry.get("address_family", {}) for k, addr in int_addr.items(): rem_entry = {"name": int_name, "address_family": {"afi": k}} self.addcmd(rem_entry, "ip_ospf", True) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Ospf_interfaces network resource. """ self._compare_addr_family(want=want, have=have) def _compare_addr_family(self, want, have): wdict = want.get("address_family", {}) hdict = have.get("address_family", {}) wname = want.get("name") hname = have.get("name") for name, entry in wdict.items(): for key, param in entry.items(): w_addr = {"afi": name, key: param} h_addr = {} if hdict.get(name): h_addr = {"afi": name, key: hdict[name].pop(key, {})} w = {"name": wname, "address_family": w_addr} h = {"name": hname, "address_family": h_addr} self.compare(parsers=self.parsers, want=w, have=h) for name, entry in hdict.items(): for key, param in entry.items(): h_addr = {"afi": name, key: param} w_addr = {} w = {"name": wname, "address_family": w_addr} h = {"name": hname, "address_family": h_addr} self.compare(parsers=self.parsers, want=w, have=h) def _ospf_int_list_to_dict(self, entry): for name, family in entry.items(): if "address_family" in family: addr_dict = {} for entry in family.get("address_family", []): addr_dict.update({entry["afi"]: entry}) family["address_family"] = addr_dict self._ospf_int_list_to_dict(family["address_family"]) diff --git a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py index ed973179..20821980 100644 --- a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py @@ -1,843 +1,843 @@ # # -*- 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 +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 ( remove_empties, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( _bool_to_str, _in_target, _is_w_same, list_diff_want_only, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Ospfv2(ConfigBase): """ The vyos_ospfv2 class """ gather_subset = ["!all", "!min"] gather_network_resources = ["ospfv2"] def __init__(self, module): super(Ospfv2, self).__init__(module) def get_ospfv2_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ - (facts, _warnings) = Facts(self._module).get_facts( + facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data, ) ospfv2_facts = facts["ansible_network_resources"].get("ospfv2", {}) return ospfv2_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() if self.state in self.ACTION_STATES: existing_ospfv2_facts = self.get_ospfv2_facts() else: existing_ospfv2_facts = {} if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_ospfv2_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_ospfv2_facts = self.get_ospfv2_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed", ) result["parsed"] = self.get_ospfv2_facts(data=running_config) else: changed_ospfv2_facts = {} if self.state in self.ACTION_STATES: result["before"] = existing_ospfv2_facts if result["changed"]: result["after"] = changed_ospfv2_facts elif self.state == "gathered": result["gathered"] = changed_ospfv2_facts result["warnings"] = warnings return result def set_config(self, existing_ospfv2_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_ospfv2_facts resp = self.set_state(remove_empties(want), remove_empties(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(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 = [] 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, 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 ospf") return commands def _render_ospf_param(self, want, have, opr=True): """ This function forms the set/delete commands for ospf leaf attributes and triggers the process for other child attributes. for firewall_global attributes. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated commands list. """ commands = [] w = deepcopy(remove_empties(want)) leaf = ("default_metric", "log_adjacency_changes") if w: for key, val in w.items(): if opr and key in leaf and not _is_w_same(w, have, key): commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) elif not opr and key in leaf and not _in_target(have, key): commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) else: commands.extend(self._render_child_param(w, have, key, opr)) return commands def _render_child_param(self, w, h, key, opr=True): """ This function invoke the function to extend commands based on the key. :param w: the desired configuration. :param h: the current configuration. :param key: attribute name. :param opr: operation. :return: list of commands. """ commands = [] if key in ("neighbor", "redistribute"): commands.extend(self._render_list_dict_param(key, w, h, opr=opr)) elif key in ("default_information", "max_metric"): commands.extend(self._render_nested_dict_param(key, w, h, opr=opr)) elif key in ("mpls_te", "auto_cost", "parameters", "auto_cost"): commands.extend(self._render_dict_param(key, w, h, opr=opr)) elif key in ( "route_map", "passive_interface", "passive_interface_exclude", ): commands.extend(self._render_list_param(key, w, h, opr=opr)) elif key == "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", ), } leaf = leaf_dict[attr] for item, value in want[attr].items(): if opr and item in leaf and not _is_w_same(want[attr], h, item): if item == "enabled": item = "enable" if item in ( "opaque_lsa", "enable", "rfc1583_compatibility", ): commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) else: commands.append( self._form_attr_cmd(key=attr, attr=item, val=value, opr=opr), ) elif not opr and item in leaf and not _in_target(h, item): if item == "enabled": commands.append(self._form_attr_cmd(key=attr, attr="enable", opr=opr)) else: commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) return commands def _render_list_param(self, attr, want, have, cmd=None, opr=True): """ This function forms the commands for passed target list attributes'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param opr: True/False. :return: generated list of commands. """ commands = [] h = [] if want: w = want.get(attr) or [] if have: h = have.get(attr) or [] if not cmd: cmd = self._compute_command(opr=opr) if w: if opr: members = list_diff_want_only(w, h) for member in members: command = cmd + attr.replace("_", "-") + " " if attr == "network": command += member["address"] elif ( attr == "passive_interface" and member != "default" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") ): command = ( command.replace("passive-interface", "interface") + member + " passive" ) elif attr == "passive_interface_exclude" and LooseVersion( get_os_version(self._module), ) >= LooseVersion("1.4"): command = ( command.replace("passive-interface-exclude", "interface") + member + " passive disable" ) else: command += member commands.append(command) elif not opr: if h: for member in w: if attr == "network": if not self.search_obj_in_have(h, member, "address"): commands.append( cmd + attr.replace("_", "-") + " " + member["address"], ) elif member not in h: if ( attr == "passive_interface" and member != "default" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") ): commands.append(cmd + "interface" + " " + member + " passive") elif attr == "passive_interface_exclude" and LooseVersion( get_os_version(self._module), ) >= LooseVersion("1.4"): command = ( command.replace("passive-interface-exclude", "interface") + member + " passive disable" ) else: commands.append(cmd + attr.replace("_", "-") + " " + member) else: 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 w_item.items(): 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", "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 w_item.items(): if not cmd: cmd = self._compute_command(opr=opr) h_item = self.search_obj_in_have(h, w_item, name[attr]) if opr and key in leaf and not _is_w_same(w_item, h_item, key): if key in ( "route_type", "neighbor_id", "address", "key_id", ): commands.append(cmd + attr + " " + str(val)) elif key == "cost": commands.append( cmd + attr + " " + w_item[name[attr]] + " " + key + " " + str(val), ) elif key == "not_advertise": commands.append( cmd + attr + " " + w_item[name[attr]] + " " + key.replace("_", "-"), ) elif key == "md5_key": commands.append( cmd + attr + " " + "key-id" + " " + str(w_item[name[attr]]) + " " + key.replace("_", "-") + " " + w_item[key], ) else: commands.append( cmd + attr + " " + w_item[name[attr]] + " " + key.replace("_", "-") + " " + str(val), ) elif not opr and key in leaf and not _in_target(h_item, key): if key in ( "route_type", "neighbor_id", "address", "key_id", ): commands.append(cmd + attr + " " + str(val)) else: commands.append(cmd + attr + " " + w_item[name[attr]] + " " + key) return commands def _render_nested_dict_param(self, attr, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for attributes with in desired nested dicts. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param opr: True/False. :return: generated commands list. """ commands = [] attr_dict = { "default_information": "originate", "max_metric": "router_lsa", } leaf_dict = { "default_information": ( "always", "metric", "metric_type", "route_map", ), "max_metric": ("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 w[key].items(): if opr and item in leaf and not _is_w_same(w[key], h_attrib, item): if item in ("administrative", "always") and val: commands.append( cmd + attr.replace("_", "-") + " " + key.replace("_", "-") + " " + item.replace("_", "-"), ) elif item not in ("administrative", "always"): commands.append( cmd + attr.replace("_", "-") + " " + key.replace("_", "-") + " " + item.replace("_", "-") + " " + str(val), ) elif not opr and item in leaf and not _in_target(h_attrib, item): commands.append(cmd + attr + " " + item) return commands def _render_areas(self, attr, want, have, opr=True): """ 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", "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_id"]), opr=opr, ) + " " ) h_area = self.search_obj_in_have(h_lst, w_area, "area_id") if not opr and not h_area: commands.append( self._form_attr_cmd(key="area", attr=w_area["area_id"], opr=opr), ) else: for key, val in w_area.items(): if opr and key in l_set and not _is_w_same(w_area, h_area, key): if key == "area_id": commands.append( self._form_attr_cmd( attr="area", val=_bool_to_str(val), opr=opr, ), ) else: commands.append( cmd + key + " " + _bool_to_str(val).replace("_", "-"), ) elif not opr and key in l_set: if key == "area_id" and not _in_target(h_area, key): commands.append(cmd) continue if 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("_", "-")) elif w_type: key = "normal" if opr and key in w_type.keys() and not _is_w_same(w_type, h_type, key): if not w_type[key] and h_type and h_type[key]: commands.append( cmd.replace("set", "delete") + attr.replace("_", "-") + " " + key, ) 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 w_type[key].items(): 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 " else: cmd = "set protocols ospf " if key: cmd += key.replace("_", "-") + " " if attr: 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("_", "-") diff --git a/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py index 30d30571..a84899f5 100644 --- a/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py +++ b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py @@ -1,450 +1,450 @@ # # -*- 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 +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 ( remove_empties, search_obj_in_list, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( _bool_to_str, _in_target, _is_w_same, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version 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 w.items(): 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 want[attr].items(): 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", "interface": "name", } leaf_dict = { "redistribute": ("route_map", "route_type"), "range": ("address", "advertise", "not_advertise"), "interface": ("name"), } 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 w_item.items(): 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 in ["route_type", "name"] or ( key == "address" and "advertise" not in w_item and "not-advertise" not in w_item ): if not val: cmd = cmd.replace("set", "delete") if ( LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") and attr == "interface" ): words = cmd.split() cmd14_list = [] for word in words: cmd14_list.append(word) if word == "ospfv3": cmd14_list.append(attr + " " + str(val)) commands.append(" ".join(cmd14_list)) else: 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", "name"): if ( LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") and attr == "interface" ): words = cmd.split() cmd14_list = [] for word in words: cmd14_list.append(word) if word == "ospfv3": cmd14_list.append(attr + " " + str(val)) commands.append(" ".join(cmd14_list)) else: 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 w_area.items(): 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 if 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), ) elif key == "interface": 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/config/prefix_lists/prefix_lists.py b/plugins/module_utils/network/vyos/config/prefix_lists/prefix_lists.py index 9b703399..05164c31 100644 --- a/plugins/module_utils/network/vyos/config/prefix_lists/prefix_lists.py +++ b/plugins/module_utils/network/vyos/config/prefix_lists/prefix_lists.py @@ -1,174 +1,173 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos_prefix_lists config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( dict_merge, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.prefix_lists import ( Prefix_listsTemplate, ) class Prefix_lists(ResourceModule): """ The vyos_prefix_lists config class """ def __init__(self, module): super(Prefix_lists, self).__init__( empty_fact_val=[], facts_module=Facts(module), module=module, resource="prefix_lists", tmplt=Prefix_listsTemplate(), ) self.plist_parsers = [ "name", "description", ] self.entries_parsers = [ "sequence", "action", "rule_description", "ge", "le", "prefix", ] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = {entry["afi"]: entry for entry in self.want} haved = {entry["afi"]: entry for entry in self.have} self._prefix_list_list_to_dict(wantd) self._prefix_list_list_to_dict(haved) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": haved = {k: v for k, v in haved.items() if k in wantd or not wantd} for key, hvalue in haved.items(): wvalue = wantd.pop(key, {}) if wvalue: wplists = wvalue.get("prefix_lists", {}) hplists = hvalue.get("prefix_lists", {}) hvalue["prefix_lists"] = { k: v for k, v in hplists.items() if k in wplists or not wplists } # remove superfluous config for overridden and deleted if self.state in ["overridden", "deleted"]: for k, have in haved.items(): if k not in wantd: self._compare(want={}, have=have) for k, want in wantd.items(): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Prefix_lists network resource. """ wplists = want.get("prefix_lists", {}) hplists = have.get("prefix_lists", {}) self._compare_plists(want=wplists, have=hplists) if self.state in ["overridden", "deleted"]: # remove remaining prefix lists for h in hplists.values(): self.commands.append( "delete policy prefix-{0} {1}".format( "list" if h["afi"] == "ipv4" else "list6", h["name"], ), ) def _compare_plists(self, want, have): for wk, wentry in want.items(): hentry = have.pop(wk, {}) # parser list for name and descriptions self.compare( parsers=self.plist_parsers, want=wentry, have=hentry, ) wplrules = wentry.get("entries", {}) hplrules = hentry.get("entries", {}) self._compare_rules(want=wplrules, have=hplrules) def _compare_rules(self, want, have): for wr, wrule in want.items(): hrule = have.pop(wr, {}) # parser list for entries self.compare( parsers=self.entries_parsers, want=wrule, have=hrule, ) # remove remaining entries for hr in have.values(): self.commands.append( "delete policy prefix-{0} {1} rule {2}".format( "list" if hr["afi"] == "ipv4" else "list6", hr["name"], hr["sequence"], ), ) def _prefix_list_list_to_dict(self, entry): for afi, value in entry.items(): if "prefix_lists" in value: for pl in value["prefix_lists"]: pl.update({"afi": afi}) if "entries" in pl: for entry in pl["entries"]: entry.update({"afi": afi, "name": pl["name"]}) pl["entries"] = {x["sequence"]: x for x in pl["entries"]} value["prefix_lists"] = {entry["name"]: entry for entry in value["prefix_lists"]} diff --git a/plugins/module_utils/network/vyos/config/route_maps/route_maps.py b/plugins/module_utils/network/vyos/config/route_maps/route_maps.py index dca99d1c..948341d3 100644 --- a/plugins/module_utils/network/vyos/config/route_maps/route_maps.py +++ b/plugins/module_utils/network/vyos/config/route_maps/route_maps.py @@ -1,183 +1,182 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos_route_maps config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( dict_merge, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps import ( Route_mapsTemplate, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps_14 import ( Route_mapsTemplate14, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Route_maps(ResourceModule): """ The vyos_route_maps config class """ def __init__(self, module): super(Route_maps, self).__init__( empty_fact_val=[], facts_module=Facts(module), module=module, resource="route_maps", tmplt=Route_mapsTemplate(), ) self.parsers = [ "call", "description", "action", "continue_sequence", "set_aggregator_ip", "set_aggregator_as", "set_as_path_exclude", "set_as_path_prepend", "set_atomic_aggregate", "set_bgp_extcommunity_rt", "set_extcommunity_rt", "set_extcommunity_soo", "set_extcommunity_bandwidth", "set_extcommunity_bandwidth_non_transitive", "set_ip_next_hop", "set_ipv6_next_hop", "set_large_community", "set_local_preference", "set_metric", "set_metric_type", "set_origin", "set_originator_id", "set_src", "set_tag", "set_weight", "set_table", "set_comm_list", "set_comm_list_delete", "set_community", "match_as_path", "match_community_community_list", "match_community_exact_match", "match_extcommunity", "match_interface", "match_large_community_large_community_list", "match_metric", "match_origin", "match_peer", "match_ip_address", "match_ip_next_hop", "match_ip_route_source", "on_match_goto", "on_match_next", "match_ipv6_address", "match_ipv6_nexthop", "match_protocol", "match_rpki", ] def _validate_template(self): version = get_os_version(self._module) if LooseVersion(version) >= LooseVersion("1.4"): self._tmplt = Route_mapsTemplate14() else: self._tmplt = Route_mapsTemplate() def parse(self): """override parse to check template""" self._validate_template() return super().parse() def get_parser(self, name): """get_parsers""" self._validate_template() return super().get_parser(name) def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ self._validate_template() if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = self._route_maps_list_to_dict(self.want) haved = self._route_maps_list_to_dict(self.have) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": haved = {k: v for k, v in haved.items() if k in wantd or not wantd} wantd = {} # remove superfluous config for overridden and deleted if self.state in ["overridden", "deleted"]: for k, have in haved.items(): if k not in wantd: self.commands.append(self._tmplt.render({"route_map": k}, "route_map", True)) for wk, want in wantd.items(): self._compare(want=want, have=haved.pop(wk, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Route_maps network resource. """ w_entries = want.get("entries", {}) h_entries = have.get("entries", {}) self._compare_entries(want=w_entries, have=h_entries) def _compare_entries(self, want, have): for wk, wentry in want.items(): hentry = have.pop(wk, {}) self.compare(parsers=self.parsers, want=wentry, have=hentry) def _route_maps_list_to_dict(self, entry): entry = {x["route_map"]: x for x in entry} for rmap, data in entry.items(): if "entries" in data: for x in data["entries"]: x.update({"route_map": rmap}) data["entries"] = { (rmap, entry.get("sequence")): entry for entry in data["entries"] } return entry diff --git a/plugins/module_utils/network/vyos/config/snmp_server/snmp_server.py b/plugins/module_utils/network/vyos/config/snmp_server/snmp_server.py index 4bc5ddc4..cdc1d6e1 100644 --- a/plugins/module_utils/network/vyos/config/snmp_server/snmp_server.py +++ b/plugins/module_utils/network/vyos/config/snmp_server/snmp_server.py @@ -1,305 +1,304 @@ # # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos_snmp_server config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( dict_merge, get_from_dict, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.snmp_server import ( Snmp_serverTemplate, ) class Snmp_server(ResourceModule): """ The vyos_snmp_server config class """ def __init__(self, module): super(Snmp_server, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="snmp_server", tmplt=Snmp_serverTemplate(), ) self.parsers = [ "contact", "description", "location", "smux_peer", "trap_source", "trap_target", ] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = self._snmp_server_list_to_dict(self.want) haved = self._snmp_server_list_to_dict(self.have) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": if haved: self.commands.append("delete service snmp") if self.state != "deleted": self._compare(want=wantd, have=haved) if self.state not in ["merged", "deleted"]: self._move_negate_commands() def _move_negate_commands(self): command_set = [] for cmd in self.commands: if re.search("delete service snmp", cmd): command_set.insert(0, cmd) else: command_set.append(cmd) self.commands = command_set def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Snmp_server network resource. """ self._compare_lists(want, have) self._compare_snmp_v3(want, have) for key, value in want.items(): self.compare( parsers=self.parsers, want={key: value}, have={key: have.pop(key, "")}, ) for key, entry in have.items(): if entry: self.compare(parsers=self.parsers, want={}, have={key: entry}) def _compare_lists(self, want, have): parsers = [ "communities", "listen_addresses", ] for attrib in parsers: wdict = get_from_dict(want, attrib) or {} hdict = get_from_dict(have, attrib) or {} for key, entry in wdict.items(): # self.addcmd(entry, attrib, False) if attrib == "communities": for k, v in entry.items(): if k in ["clients", "networks"]: v.sort() h = {} if k != "name": if hdict.get(key): h = { "communities": { k: hdict[key].pop(k, ""), "name": key, }, } self.compare( parsers="communities", want={"communities": {k: v, "name": key}}, have=h, ) else: self.compare( parsers="listen_addresses", want={"listen_addresses": entry}, have={"listen_addresses": hdict.pop(key, {})}, ) have.pop(attrib, {}) # remove remaining items in have for replaced for key, entry in hdict.items(): if attrib == "communities": for k, v in entry.items(): if k != "name": self.compare( parsers="communities", have={"communities": {k: v, "name": key}}, want={}, ) else: self.compare(parsers=parsers, want={}, have={attrib: entry}) def _compare_snmp_v3(self, want, have): parsers = [ "snmp_v3.groups", "snmp_v3.trap_targets.port", "snmp_v3.trap_targets.protocol", "snmp_v3.trap_targets.type", "snmp_v3.trap_targets.user", "snmp_v3.users.group", "snmp_v3.users.mode", "snmp_v3.views", "snmp_v3.engine_id", ] attribute_dict = { "groups": "group", "trap_targets": "address", "users": "user", "views": "view", } wdict = get_from_dict(want, "snmp_v3") or {} hdict = get_from_dict(have, "snmp_v3") or {} for attrib in attribute_dict.keys(): wattrib = get_from_dict(wdict, attrib) or {} hattrib = get_from_dict(hdict, attrib) or {} for key, entry in wattrib.items(): self._compare_snmp_v3_auth_privacy(entry, hattrib.get(key, {}), attrib) for k, v in entry.items(): if k != attribute_dict[attrib]: h = {} if hattrib.get(key): h = { "snmp_v3": { attrib: { k: hattrib[key].pop(k, ""), attribute_dict[attrib]: hattrib[key][ attribute_dict[attrib] ], }, }, } self.compare( parsers=parsers, want={ "snmp_v3": { attrib: { k: v, attribute_dict[attrib]: entry[attribute_dict[attrib]], }, }, }, have=h, ) # remove remaining items in have for replaced for key, entry in hattrib.items(): self._compare_snmp_v3_auth_privacy({}, entry, attrib) self.compare(parsers=parsers, want={}, have={"snmp_v3": {attrib: entry}}) hdict.pop(attrib, {}) for key, entry in wdict.items(): # self.addcmd(entry, attrib, False) self.compare( parsers="snmp_v3.engine_id", want={"snmp_v3": {key: entry}}, have={"snmp_v3": {key: hdict.pop(key, {})}}, ) # remove remaining items in have for replaced for key, entry in hdict.items(): self.compare(parsers=parsers, want={}, have={"snmp_v3": {key: entry}}) def _compare_snmp_v3_auth_privacy(self, wattrib, hattrib, attrib): parsers = [ "snmp_v3.trap_targets.authentication", "snmp_v3.trap_targets.privacy", "snmp_v3.users.authentication", "snmp_v3.users.privacy", ] if attrib in ["trap_targets", "users"]: if attrib == "users": primary_key = "user" else: primary_key = "address" for key, entry in wattrib.items(): if key != primary_key and key in ["authentication", "privacy"]: self.compare( parsers=parsers, want={ "snmp_v3": { attrib: { key: entry, primary_key: wattrib[primary_key], }, }, }, have={ "snmp_v3": { attrib: { key: hattrib.pop(key, {}), primary_key: wattrib[primary_key], }, }, }, ) for key, entry in hattrib.items(): if key != primary_key and key in ["authentication", "privacy"]: self.compare( parsers=parsers, want={}, have={ "snmp_v3": { attrib: { key: entry, primary_key: hattrib[primary_key], }, }, }, ) def _snmp_server_list_to_dict(self, entry): param_dict = { "communities": "name", "listen_addresses": "address", } v3_param_dict = { "groups": "group", "users": "user", "views": "view", "trap_targets": "address", } for k, v in param_dict.items(): if k in entry: a_dict = {} for el in entry[k]: a_dict.update({el[v]: el}) entry[k] = a_dict for k, v in v3_param_dict.items(): if entry.get("snmp_v3") and k in entry.get("snmp_v3"): a_dict = {} for el in entry["snmp_v3"][k]: a_dict.update({el[v]: el}) entry["snmp_v3"][k] = a_dict return entry diff --git a/plugins/module_utils/network/vyos/config/static_routes/static_routes.py b/plugins/module_utils/network/vyos/config/static_routes/static_routes.py index da8f8e3d..2a09c0f0 100644 --- a/plugins/module_utils/network/vyos/config/static_routes/static_routes.py +++ b/plugins/module_utils/network/vyos/config/static_routes/static_routes.py @@ -1,604 +1,603 @@ # # -*- 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_static_routes 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 ( dict_diff, remove_empties, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( dict_delete, get_lst_diff_for_dicts, get_lst_same_for_dicts, get_route_type, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Static_routes(ConfigBase): """ The vyos_static_routes class """ gather_subset = ["!all", "!min"] gather_network_resources = ["static_routes"] def __init__(self, module): super(Static_routes, self).__init__(module) def get_static_routes_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, ) static_routes_facts = facts["ansible_network_resources"].get("static_routes") if not static_routes_facts: return [] return static_routes_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_static_routes_facts = self.get_static_routes_facts() else: existing_static_routes_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_static_routes_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_static_routes_facts = self.get_static_routes_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_static_routes_facts(data=running_config) else: changed_static_routes_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_static_routes_facts if result["changed"]: result["after"] = changed_static_routes_facts elif self.state == "gathered": result["gathered"] = changed_static_routes_facts result["warnings"] = warnings return result def set_config(self, existing_static_routes_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_static_routes_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """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 want: 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(want=want, have=have)) elif self.state == "deleted": commands.extend(self._state_deleted(want=want, have=have)) elif want: routes = self._get_routes(want) for r in routes: h_item = self.search_route_in_have(have, r["dest"]) if self.state in ("merged", "rendered"): commands.extend(self._state_merged(want=r, have=h_item)) elif self.state == "replaced": commands.extend(self._state_replaced(want=r, have=h_item)) return commands def search_route_in_have(self, have, want_dest): """ This function returns the route if its found in have config. :param have: :param dest: :return: the matched route """ routes = self._get_routes(have) for r in routes: if r["dest"] == want_dest: return r 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 = [] if have: for key, value in want.items(): if value: if key == "next_hops": commands.extend(self._update_next_hop(want, have)) elif key == "blackhole_config": commands.extend(self._update_blackhole(key, want, have)) commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] routes = self._get_routes(have) for r in routes: route_in_want = self.search_route_in_have(want, r["dest"]) if not route_in_want: commands.append(self._compute_command(r["dest"], remove=True)) routes = self._get_routes(want) for r in routes: route_in_have = self.search_route_in_have(have, r["dest"]) commands.extend(self._state_replaced(r, route_in_have)) return commands def _state_merged(self, want, have, opr=True): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] if have: commands.extend(self._render_updates(want, have)) else: commands.extend(self._render_set_commands(want)) 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: routes = self._get_routes(want) if not routes: for w in want: af = w["address_families"] for item in af: if self.afi_in_have(have, item): commands.append(self._compute_command(afi=item["afi"], remove=True)) else: routes = self._get_routes(have) if self._is_ip_route_exist(routes): commands.append(self._compute_command(afi="ipv4", remove=True)) if self._is_ip_route_exist(routes, "route6"): commands.append(self._compute_command(afi="ipv6", remove=True)) return commands def _render_set_commands(self, want): """ This function returns the list of commands to add attributes which are present in want :param want: :return: list of commands. """ commands = [] have = {} for key, value in want.items(): if value: if key == "dest": commands.append(self._compute_command(dest=want["dest"])) elif key == "blackhole_config": commands.extend(self._add_blackhole(key, want, have)) elif key == "next_hops": commands.extend(self._add_next_hop(want, have)) return commands def _add_blackhole(self, key, want, have): """ This function gets the diff for blackhole config specific attributes and form the commands for attributes which are present in want but not in have. :param key: :param want: :param have: :return: list of commands """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(remove_empties(have)) want_blackhole = want_copy.get(key) or {} have_blackhole = have_copy.get(key) or {} updates = dict_delete(want_blackhole, have_blackhole) if updates: for attrib, value in updates.items(): if value: if attrib == "distance": commands.append( self._compute_command( dest=want["dest"], key="blackhole", attrib=attrib, remove=False, value=str(value), ), ) elif attrib == "type": commands.append(self._compute_command(dest=want["dest"], key="blackhole")) return commands def _add_next_hop(self, want, have, opr=True): """ This function gets the diff for next hop specific attributes and form the commands to add attributes which are present in want but not in have. :param want: :param have: :return: list of commands. """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(remove_empties(have)) if not opr: diff_next_hops = get_lst_same_for_dicts(want_copy, have_copy, "next_hops") else: diff_next_hops = get_lst_diff_for_dicts(want_copy, have_copy, "next_hops") if diff_next_hops: for hop in diff_next_hops: for element in hop: if element == "forward_router_address": commands.append( self._compute_command( dest=want["dest"], key="next-hop", value=hop[element], opr=opr, ), ) elif element == "enabled" and not hop[element]: commands.append( self._compute_command( dest=want["dest"], key="next-hop", attrib=hop["forward_router_address"], value="disable", opr=opr, ), ) elif element == "admin_distance": commands.append( self._compute_command( dest=want["dest"], key="next-hop", attrib=hop["forward_router_address"] + " " + "distance", value=str(hop[element]), opr=opr, ), ) elif element == "interface" and LooseVersion( get_os_version(self._module), ) < LooseVersion("1.4"): commands.append( self._compute_command( dest=want["dest"], key="next-hop-interface", value=hop[element], opr=opr, ).replace("route", "interface-route"), ) elif element == "interface" and LooseVersion( get_os_version(self._module), ) >= LooseVersion("1.4"): commands.append( self._compute_command( dest=want["dest"], key="interface", value=hop[element], opr=opr, ), ) return commands def _update_blackhole(self, key, want, have): """ This function gets the difference for blackhole dict and form the commands to delete the attributes which are present in have but not in want. :param want: :param have: :return: list of commands :param key: :param want: :param have: :return: list of commands """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(remove_empties(have)) want_blackhole = want_copy.get(key) or {} have_blackhole = have_copy.get(key) or {} updates = dict_delete(have_blackhole, want_blackhole) if updates: for attrib, value in updates.items(): if value: if attrib == "distance": commands.append( self._compute_command( dest=want["dest"], key="blackhole", attrib=attrib, remove=True, value=str(value), ), ) elif attrib == "type" and "distance" not in want_blackhole.keys(): commands.append( self._compute_command(dest=want["dest"], key="blackhole", remove=True), ) return commands def _update_next_hop(self, want, have, opr=True): """ This function gets the difference for next_hops list and form the commands to delete the attributes which are present in have but not in want. :param want: :param have: :return: list of commands """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(remove_empties(have)) diff_next_hops = get_lst_diff_for_dicts(have_copy, want_copy, "next_hops") if diff_next_hops: for hop in diff_next_hops: for element in hop: if element == "forward_router_address": commands.append( self._compute_command( dest=want["dest"], key="next-hop", value=hop[element], remove=True, ), ) elif element == "enabled": commands.append( self._compute_command( dest=want["dest"], key="next-hop", attrib=hop["forward_router_address"], value="disable", remove=True, ), ) elif element == "admin_distance": commands.append( self._compute_command( dest=want["dest"], key="next-hop", attrib=hop["forward_router_address"] + " " + "distance", value=str(hop[element]), remove=True, ), ) elif element == "interface" and LooseVersion( get_os_version(self._module), ) < LooseVersion("1.4"): commands.append( self._compute_command( dest=want["dest"], key="next-hop-interface", value=hop[element], remove=True, ).replace("route", "interface-route"), ) elif element == "interface" and LooseVersion( get_os_version(self._module), ) >= LooseVersion("1.4"): commands.append( self._compute_command( dest=want["dest"], key="interface", value=hop[element], remove=True, ), ) return commands def _render_updates(self, want, have, opr=True): """ This function takes the diff between want and have and invokes the appropriate functions to create the commands to update the attributes. :param want: :param have: :return: list of commands """ commands = [] want_nh = want.get("next_hops") or [] # delete static route operation per destination if not opr and not want_nh: commands.append(self._compute_command(dest=want["dest"], remove=True)) else: temp_have_next_hops = have.pop("next_hops", None) temp_want_next_hops = want.pop("next_hops", None) updates = dict_diff(have, want) if temp_have_next_hops: have["next_hops"] = temp_have_next_hops if temp_want_next_hops: want["next_hops"] = temp_want_next_hops commands.extend(self._add_next_hop(want, have, opr=opr)) if opr and updates: for key, value in updates.items(): if value: if key == "blackhole_config": commands.extend(self._add_blackhole(key, want, have)) return commands def _compute_command( self, dest=None, key=None, attrib=None, value=None, remove=False, afi=None, opr=True, ): """ This functions construct the required command based on the passed arguments. :param dest: :param key: :param attrib: :param value: :param remove: :return: constructed command """ if remove or not opr: cmd = "delete protocols static " + self.get_route_type(dest, afi) else: cmd = "set protocols static " + self.get_route_type(dest, afi) if dest: cmd += " " + dest if key: cmd += " " + key if attrib: cmd += " " + attrib if value: cmd += " '" + value + "'" return cmd def afi_in_have(self, have, w_item): """ This functions checks for the afi list in have :param have: :param w_item: :return: """ if have: for h in have: af = h.get("address_families") or [] for item in af: if w_item["afi"] == item["afi"]: return True return False def get_route_type(self, dest=None, afi=None): """ This function returns the route type based on destination ip address or afi :param address: :return: """ if dest: return get_route_type(dest) elif afi == "ipv4": return "route" elif afi == "ipv6": return "route6" def _is_ip_route_exist(self, routes, type="route"): """ This functions checks for the type of route. :param routes: :param type: :return: True/False """ for r in routes: if type == self.get_route_type(r["dest"]): return True return False def _get_routes(self, lst): """ This function returns the list of routes :param lst: list of address families :return: list of routes """ r_list = [] for item in lst: af = item["address_families"] for element in af: routes = element.get("routes") or [] for r in routes: r_list.append(r) return r_list diff --git a/plugins/module_utils/network/vyos/config/vrf/vrf.py b/plugins/module_utils/network/vyos/config/vrf/vrf.py index aa4329d9..a55073b0 100644 --- a/plugins/module_utils/network/vyos/config/vrf/vrf.py +++ b/plugins/module_utils/network/vyos/config/vrf/vrf.py @@ -1,308 +1,307 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos_vrf config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_global.bgp_global import ( Bgp_global, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv2.ospfv2 import ( Ospfv2, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv3.ospfv3 import ( Ospfv3, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.static_routes.static_routes import ( Static_routes, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.vrf import ( VrfTemplate, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import combine from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Vrf(ResourceModule): """ The vyos_vrf config class """ def __init__(self, module): super(Vrf, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="vrf", tmplt=VrfTemplate(), ) self.parsers = [ "bind_to_all", ] def _validate_template(self): version = get_os_version(self._module) if LooseVersion(version) >= LooseVersion("1.4"): self._tmplt = VrfTemplate() else: self._module.fail_json(msg="VRF is not supported in this version of VyOS") def parse(self): """override parse to check template""" self._validate_template() return super().parse() def get_parser(self, name): """get_parsers""" self._validate_template() return super().get_parser(name) def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = {} haved = {} wantd = deepcopy(self.want) haved = deepcopy(self.have) # if state is merged, merge want onto have and then compare if self.state in ["merged", "replaced"]: wantd = combine(haved, wantd, recursive=True) # if state is deleted, delete and empty out wantd if self.state == "deleted": w = deepcopy(wantd) if w == {} and haved != {}: self.commands = ["delete vrf"] return for k, want in w.items(): if not (k in haved and haved[k]): del wantd[k] else: if isinstance(want, list): for entry in want: wname = entry.get("name") haved["instances"] = [ i for i in haved.get("instances", []) if i.get("name") != wname ] self.commands.append("delete vrf name {}".format(wname)) else: self.commands.append("delete vrf {}".format(k.replace("_", "-"))) del wantd[k] if self.state == "overridden": w = deepcopy(wantd) h = deepcopy(haved) for k, want in w.items(): if k in haved and haved[k] != want: if isinstance(want, list): for entry in want: wname = entry.get("name") hdict = next( (inst for inst in haved["instances"] if inst["name"] == wname), None, ) wantc = self._canonicalise(entry) havec = self._canonicalise(hdict or {}) if wantc != havec: haved["instances"] = [ i for i in haved.get("instances", []) if i.get("name") != wname ] self.commands.append("delete vrf name {}".format(wname)) self.commands.append("commit") for k, want in wantd.items(): if isinstance(want, list): self._compare_instances(want=want, have=haved.pop(k, {})) self.compare( parsers=self.parsers, want={k: want}, have={k: haved.pop(k, {})}, ) def _compare_instances(self, want, have): """Compare the instances of the VRF""" parsers = [ "table_id", "vni", "description", "disable_vrf", ] for entry in want: h = {} wname = entry.get("name") h = { k: v for vrf in have if vrf.get("name") == wname for k, v in vrf.items() if k != "address_family" } self.compare(parsers=parsers, want=entry, have=h) if "address_family" in entry: wafi = {"name": wname, "address_family": entry.get("address_family", [])} hdict = next((d for d in have if d.get("name") == wname), None) hafi = { "name": (hdict or {"name": wname})["name"], "address_family": hdict.get("address_family", []) if hdict else [], } self._compare_addr_family(wafi, hafi) if "protocols" in entry: for protocol_name in entry["protocols"]: w_p_dict = entry["protocols"][protocol_name] h_p_dict = next( ( v.get("protocols", {}).get(protocol_name, {}) for v in have if v.get("name") == wname ), {}, ) if protocol_name == "bgp": bgp_module = Bgp_global(self._module) bgp_module._validate_template() bgp_module.want = w_p_dict bgp_module.have = h_p_dict bgp_module.generate_commands() protocol_commands = bgp_module.commands elif protocol_name == "ospf": ospfv2_module = Ospfv2(self._module) ospfv2_module._module.params["config"] = w_p_dict ospfv2_module.state = self.state protocol_commands = ospfv2_module.set_config(h_p_dict) elif protocol_name == "ospfv3": ospfv3_module = Ospfv3(self._module) ospfv3_module._module.params["config"] = w_p_dict ospfv3_module.state = self.state protocol_commands = ospfv3_module.set_config(h_p_dict) elif protocol_name == "static": static_routes_module = Static_routes(self._module) static_routes_module._module.params["config"] = w_p_dict static_routes_module.state = self.state protocol_commands = static_routes_module.set_config(h_p_dict) else: self._module.fail_json( msg="The protocol {} is not supported".format(protocol_name), ) self.commands.extend( [ cmd.replace("protocols", "vrf name " + wname + " protocols", 1) for cmd in protocol_commands ], ) def _compare_addr_family(self, want, have): """Compare the address families of the VRF""" afi_parsers = [ "disable_forwarding", "disable_nht", ] wafi = self.afi_to_list(want) hafi = self.afi_to_list(have) lookup = {(d["name"], d["afi"]): d for d in hafi} pairs = [(d1, lookup.get((d1["name"], d1["afi"]), {})) for d1 in wafi] for wafd, hafd in pairs: if "route_maps" in wafd: self._compare_route_maps(wafd, hafd) self.compare(parsers=afi_parsers, want=wafd, have=hafd) def afi_to_list(self, data): """Convert address family dict to list""" return [ {"name": data["name"], **{**af, "afi": "ip" if af["afi"] == "ipv4" else af["afi"]}} for af in data["address_family"] ] def _compare_route_maps(self, wafd, hafd): want_rms = wafd.get("route_maps", []) have_rms = hafd.get("route_maps", []) for want in want_rms: match = next( ( h for h in have_rms if h["rm_name"] == want["rm_name"] and h["protocol"] == want["protocol"] ), {}, ) base = {"name": wafd["name"], "afi": wafd["afi"]} self.compare( parsers="route_maps", want={**base, "route_maps": want}, have={**base, "route_maps": match}, ) def _canonicalise(self, obj): if isinstance(obj, dict): return {k: self._canonicalise(v) for k, v in obj.items()} if isinstance(obj, list): if not obj: return obj if not isinstance(obj[0], dict): return sorted(obj) canon = [self._canonicalise(i) for i in obj] def sort_key(d): for k in ("name", "address", "afi", "area_id", "neighbor_id", "dest"): if k in d: return d[k] return tuple(sorted(d.items())) return sorted(canon, key=sort_key) return obj diff --git a/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py index 31839c5d..dab92612 100644 --- a/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py +++ b/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py @@ -1,99 +1,98 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos bgp_address_family fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_address_family.bgp_address_family import ( Bgp_address_familyArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family import ( Bgp_address_familyTemplate, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family_14 import ( Bgp_address_familyTemplate14, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Bgp_address_familyFacts(object): """The vyos bgp_address_family facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Bgp_address_familyArgs.argument_spec def get_device_data(self, connection): return connection.get('show configuration commands | match "set protocols bgp"') def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Bgp_address_family network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] config_lines = [] if not data: data = self.get_device_data(connection) for resource in data.splitlines(): if "address-family" in resource or "system-as" in resource: config_lines.append(re.sub("'", "", resource)) # parse native config using the Bgp_address_family template based on version if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): bgp_address_family_parser = Bgp_address_familyTemplate14(lines=config_lines) else: bgp_address_family_parser = Bgp_address_familyTemplate(lines=config_lines) objs = bgp_address_family_parser.parse() if objs: if "address_family" in objs: objs["address_family"] = list(objs["address_family"].values()) for af in objs["address_family"]: if "networks" in af: af["networks"] = sorted(af["networks"], key=lambda k: k["prefix"]) if "aggregate_address" in af: af["aggregate_address"] = sorted( af["aggregate_address"], key=lambda k: k["prefix"], ) if "neighbors" in objs: objs["neighbors"] = list(objs["neighbors"].values()) objs["neighbors"] = sorted(objs["neighbors"], key=lambda k: k["neighbor_address"]) for neigh in objs["neighbors"]: if "address_family" in neigh: neigh["address_family"] = list(neigh["address_family"].values()) ansible_facts["ansible_network_resources"].pop("bgp_address_family", None) params = utils.remove_empties(utils.validate_config(self.argument_spec, {"config": objs})) facts["bgp_address_family"] = params.get("config", []) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py b/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py index 2883cc2d..cf2e4475 100644 --- a/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py +++ b/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py @@ -1,92 +1,91 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos bgp_global fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_global.bgp_global import ( Bgp_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_global import ( Bgp_globalTemplate, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_global_14 import ( Bgp_globalTemplate14, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Bgp_globalFacts(object): """The vyos bgp_global facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Bgp_globalArgs.argument_spec def get_device_data(self, connection): return connection.get('show configuration commands | match "set protocols bgp"') def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Bgp_global network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = {} config_lines = [] if not data: data = self.get_device_data(connection) for resource in data.splitlines(): if "address-family" not in resource: config_lines.append(re.sub("'", "", resource)) if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): bgp_global_parser = Bgp_globalTemplate14(lines=config_lines, module=self._module) else: bgp_global_parser = Bgp_globalTemplate(lines=config_lines, module=self._module) objs = bgp_global_parser.parse() if "neighbor" in objs: objs["neighbor"] = list(objs["neighbor"].values()) objs["neighbor"] = sorted(objs["neighbor"], key=lambda k: k["address"]) if "network" in objs: objs["network"] = sorted(objs["network"], key=lambda k: k["address"]) if "aggregate_address" in objs: objs["aggregate_address"] = sorted(objs["aggregate_address"], key=lambda k: k["prefix"]) ansible_facts["ansible_network_resources"].pop("bgp_global", None) params = utils.remove_empties( bgp_global_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), ) facts["bgp_global"] = params.get("config", []) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index 24c444dd..9bc39935 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -1,133 +1,132 @@ # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The facts class for vyos this file validates each subset of facts and selectively calls the appropriate facts gathering function """ -from __future__ import absolute_import, division, print_function +from __future__ import absolute_import, division, print_function __metaclass__ = type from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts import ( FactsBase, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_address_family.bgp_address_family import ( Bgp_address_familyFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_global.bgp_global import ( Bgp_globalFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_global.firewall_global import ( Firewall_globalFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_interfaces.firewall_interfaces import ( Firewall_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules import ( Firewall_rulesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.hostname.hostname import ( HostnameFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.interfaces.interfaces import ( InterfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.l3_interfaces.l3_interfaces import ( L3_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lag_interfaces.lag_interfaces import ( Lag_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import ( Config, Default, Neighbors, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_global.lldp_global import ( Lldp_globalFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_interfaces.lldp_interfaces import ( Lldp_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.logging_global.logging_global import ( Logging_globalFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ntp_global.ntp_global import ( Ntp_globalFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospf_interfaces.ospf_interfaces import ( Ospf_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv2.ospfv2 import ( Ospfv2Facts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv3.ospfv3 import ( Ospfv3Facts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.prefix_lists.prefix_lists import ( Prefix_listsFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.route_maps.route_maps import ( Route_mapsFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.snmp_server.snmp_server import ( Snmp_serverFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes import ( Static_routesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.vrf.vrf import VrfFacts - FACT_LEGACY_SUBSETS = dict(default=Default, neighbors=Neighbors, config=Config) FACT_RESOURCE_SUBSETS = dict( interfaces=InterfacesFacts, l3_interfaces=L3_interfacesFacts, lag_interfaces=Lag_interfacesFacts, lldp_global=Lldp_globalFacts, lldp_interfaces=Lldp_interfacesFacts, static_routes=Static_routesFacts, firewall_rules=Firewall_rulesFacts, firewall_global=Firewall_globalFacts, firewall_interfaces=Firewall_interfacesFacts, ospfv3=Ospfv3Facts, ospfv2=Ospfv2Facts, ospf_interfaces=Ospf_interfacesFacts, bgp_global=Bgp_globalFacts, bgp_address_family=Bgp_address_familyFacts, route_maps=Route_mapsFacts, prefix_lists=Prefix_listsFacts, logging_global=Logging_globalFacts, ntp_global=Ntp_globalFacts, snmp_server=Snmp_serverFacts, hostname=HostnameFacts, vrf=VrfFacts, ) class Facts(FactsBase): """The fact class for vyos""" VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys()) VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys()) def __init__(self, module): super(Facts, self).__init__(module) def get_facts(self, legacy_facts_type=None, resource_facts_type=None, data=None): """Collect the facts for vyos :param legacy_facts_type: List of legacy facts types :param resource_facts_type: List of resource fact types :param data: previously collected conf :rtype: dict :return: the facts gathered """ if self.VALID_RESOURCE_SUBSETS: self.get_network_resources_facts(FACT_RESOURCE_SUBSETS, resource_facts_type, data) if self.VALID_LEGACY_GATHER_SUBSETS: self.get_network_legacy_facts(FACT_LEGACY_SUBSETS, legacy_facts_type) return self.ansible_facts, self._warnings diff --git a/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py index 3a48889e..c74e63d3 100644 --- a/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py +++ b/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py @@ -1,402 +1,402 @@ # # -*- 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 firewall_global 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 +from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy from re import M, findall, search from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_global.firewall_global import ( Firewall_globalArgs, ) class Firewall_globalFacts(object): """The vyos firewall_global fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Firewall_globalArgs.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 firewall_global :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: # typically data is populated from the current device configuration # data = connection.get('show running-config | section ^interface') # using mock data instead data = self.get_device_data(connection) objs = {} firewalls = findall(r"^set firewall .*$", data, M) if firewalls: objs = self.render_config(firewalls) facts = {} params = utils.validate_config(self.argument_spec, {"config": objs}) facts["firewall_global"] = 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 and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ conf = "\n".join( filter( lambda x: ("firewall ipv6-name" and "firewall name" not in x), conf, ), ) a_lst = [ "config_trap", "validation", "log_martians", "syn_cookies", "twa_hazards_protection", ] firewall = self.parse_attr(conf, a_lst) f_sub = { "ping": self.parse_ping(conf), "group": self.parse_group(conf), "route_redirects": self.route_redirects(conf), "state_policy": self.parse_state_policy(conf), } firewall.update(f_sub) return firewall def route_redirects(self, conf): """ This function forms the regex to fetch the afi and invoke functions to fetch route redirects and source routes :param conf: configuration data. :return: generated rule list configuration. """ rr_lst = [] v6_attr = findall( r"^set firewall (?:global-options )?(?:ipv6-src-route|ipv6-receive-redirects) (\S+)", conf, M, ) if v6_attr: obj = self.parse_rr_attrib(conf, "ipv6") if obj: rr_lst.append(obj) v4_attr = findall( r"^set firewall (?:global-options )?(?:ip-src-route|receive-redirects|send-redirects) (\S+)", conf, M, ) if v4_attr: obj = self.parse_rr_attrib(conf, "ipv4") if obj: rr_lst.append(obj) return rr_lst def parse_rr_attrib(self, conf, attrib=None): """ This function fetches the 'ip_src_route' invoke function to parse icmp redirects. :param conf: configuration to be parsed. :param attrib: 'ipv4/ipv6'. :return: generated config dictionary. """ cfg_dict = self.parse_attr(conf, ["ip_src_route"], type=attrib) cfg_dict["icmp_redirects"] = self.parse_icmp_redirects(conf, attrib) cfg_dict["afi"] = attrib return cfg_dict def parse_icmp_redirects(self, conf, attrib=None): """ This function triggers the parsing of 'icmp_redirects' attributes. :param conf: configuration to be parsed. :param attrib: 'ipv4/ipv6'. :return: generated config dictionary. """ a_lst = ["send", "receive"] cfg_dict = self.parse_attr(conf, a_lst, type=attrib) return cfg_dict def parse_ping(self, conf): """ This function triggers the parsing of 'ping' attributes. :param conf: configuration to be parsed. :return: generated config dictionary. """ a_lst = ["all", "broadcast"] cfg_dict = self.parse_attr(conf, a_lst) return cfg_dict def parse_state_policy(self, conf): """ This function fetched the connecton type and invoke function to parse other state-policy attributes. :param conf: configuration data. :return: generated rule list configuration. """ sp_lst = [] policies = findall(r"^set firewall (?:global-options )?state-policy (\S+)", conf, M) policies = list(set(policies)) # remove redundancies if policies: rules_lst = [] for sp in set(policies): sp_regex = r"^set firewall (?:global-options )?state-policy %s .+$" % sp cfg = "\n".join(findall(sp_regex, conf, M)) obj = self.parse_policies(cfg, sp) obj["connection_type"] = sp if obj: rules_lst.append(obj) sp_lst = sorted(rules_lst, key=lambda i: i["connection_type"]) return sp_lst def parse_policies(self, conf, attrib=None): """ This function triggers the parsing of policy attributes action and log. :param conf: configuration :param attrib: connection type. :return: generated rule configuration dictionary. """ a_lst = ["action", "log", "log_level"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_group(self, conf): """ This function triggers the parsing of 'group' attributes. :param conf: configuration. :return: generated config dictionary. """ cfg_dict = {} cfg_dict["port_group"] = self.parse_group_lst(conf, "port-group", False) cfg_dict["address_group"] = self.parse_group_lst( conf, "address-group", ) + self.parse_group_lst(conf, "ipv6-address-group") cfg_dict["network_group"] = self.parse_group_lst( conf, "network-group", ) + self.parse_group_lst(conf, "ipv6-network-group") return cfg_dict def parse_group_lst(self, conf, type, include_afi=True): """ This function fetches the name of group and invoke function to parse group attributes'. :param conf: configuration data. :param type: type of group. :param include_afi: if the afi should be included in the parsed object :return: generated group list configuration. """ g_lst = [] groups = findall(r"^set firewall group " + type + " (\\S+)", conf, M) if groups: rules_lst = [] for gr in set(groups): gr_regex = r"^set firewall group " + type + " %s .+$" % gr cfg = "\n".join(findall(gr_regex, conf, M)) if "ipv6" in type: # fmt: off obj = self.parse_groups(cfg, type[len("ipv6-"):], gr) # fmt: on if include_afi: obj["afi"] = "ipv6" else: obj = self.parse_groups(cfg, type, gr) if include_afi: obj["afi"] = "ipv4" obj["name"] = gr.strip("'") if obj: rules_lst.append(obj) g_lst = sorted(rules_lst, key=lambda i: i["name"]) return g_lst def parse_groups(self, conf, type, name): """ This function fetches the description and invoke the parsing of group members. :param conf: configuration. :param type: type of group. :param name: name of group. :return: generated configuration dictionary. """ a_lst = ["name", "description"] group = self.parse_attr(conf, a_lst) key = self.get_key(type) r_sub = {key[0]: self.parse_address_port_lst(conf, name, key[1])} group.update(r_sub) return group def parse_address_port_lst(self, conf, name, key): """ This function forms the regex to fetch the group members attributes. :param conf: configuration data. :param name: name of group. :param key: key value. :return: generated member list configuration. """ l_lst = [] attribs = findall(r"^.*" + name + " " + key + " (\\S+)", conf, M) if attribs: for attr in attribs: if key == "port": l_lst.append({"port": attr.strip("'")}) else: l_lst.append({"address": attr.strip("'")}) return l_lst def parse_attr(self, conf, attr_list, match=None, type=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, type) if match: regex = match + " " + regex if conf: if self.is_bool(attrib): # fancy regex to make sure we don't get a substring out = search(r"^.*" + regex + r"( 'disable')?(?=\s|$)", conf, M) if out: if out.group(1): config[attrib] = False else: config[attrib] = True else: out = search(r"^.*" + regex + r" (.+)", conf, M) if out: val = out.group(1).strip("'") if self.is_num(attrib): val = int(val) config[attrib] = val return config def get_key(self, type): """ This function map the group type to member type :param type: :return: """ key = () if type == "port-group": key = ("members", "port") elif type == "address-group": key = ("members", "address") elif type == "network-group": key = ("members", "network") return key def map_regex(self, attrib, type=None): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ regex = attrib.replace("_", "-") if attrib == "all": regex = "all-ping" elif attrib == "disabled": regex = "disable" elif attrib == "broadcast": regex = "broadcast-ping" elif attrib == "send": if type == "ipv6": regex = "ipv6-send-redirects" else: regex = "send-redirects" elif attrib == "ip_src_route": if type == "ipv6": regex = "ipv6-src-route" elif attrib == "receive": if type == "ipv6": regex = "ipv6-receive-redirects" else: regex = "receive-redirects" return regex def is_num(self, attrib): """ This function looks for the attribute in predefined integer type set. :param attrib: attribute. :return: True/false. """ num_set = ("time", "code", "type", "count", "burst", "number") return True if attrib in num_set else False def get_src_route(self, attrib): """ This function looks for the attribute in predefined integer type set. :param attrib: attribute. :return: True/false. """ return "ipv6_src_route" if attrib == "ipv6" else "ip_src_route" def is_bool(self, attrib): """ This function looks for the attribute in predefined bool type set. :param attrib: attribute. :return: True/False """ bool_set = ( "all", "log", "send", "receive", "broadcast", "config_trap", "log_martians", "syn_cookies", "ip_src_route", "twa_hazards_protection", ) return True if attrib in bool_set else False diff --git a/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py b/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py index bac31920..34235af7 100644 --- a/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py +++ b/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py @@ -1,203 +1,203 @@ # # -*- 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 firewall_interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ -from __future__ import absolute_import, division, print_function +from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy from re import M, findall, search from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_interfaces.firewall_interfaces import ( Firewall_interfacesArgs, ) class Firewall_interfacesFacts(object): """The vyos firewall_interfaces fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Firewall_interfacesArgs.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 firewall_interfaces :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: # typically data is populated from the current device configuration # data = connection.get('show running-config | section ^interface') # using mock data instead data = self.get_device_data(connection) objs = [] # Search all set from configuration with set interface, including ethernet and bonding interfaces_raw = findall(r"^set interfaces \S+ (\S+) firewall (?:\'*)", data, M) interfaces_vif = findall(r"^set interfaces \S+ (\S+) vif (\d+)* firewall (?:\'*)", data, M) interfaces = interfaces_raw + interfaces_vif if interfaces: objs = self.get_names(data, interfaces) ansible_facts["ansible_network_resources"].pop("firewall_interfaces", None) facts = {} if objs: facts["firewall_interfaces"] = [] params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["firewall_interfaces"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def get_names(self, data, interfaces): """ This function performs following: - Form regex to fetch 'interface name' from interfaces firewall data. - Form the name list. :param data: configuration. :param rules: list of interfaces. :return: generated firewall interfaces configuration. """ names = [] for r in set(interfaces): myvif = None if isinstance(r, tuple): myinterface, myvif = r else: myinterface = r # Parse interfaces that contains string or tuple when the interface is in a vlan if myvif is not None: int_regex = r" %s vif \d+ firewall .+$" % myinterface cfg = findall(int_regex, data, M) fi = self.render_config(cfg) fi["name"] = myinterface + "." + myvif else: int_regex = r" %s firewall .+$" % myinterface cfg = findall(int_regex, data, M) fi = self.render_config(cfg) fi["name"] = myinterface names.append(fi) if names: names = sorted(names, key=lambda i: i["name"]) return names def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ conf = "\n".join(filter(lambda x: "firewall" in x, conf)) config = {"access_rules": self.parse_access_rules(conf)} return config def parse_access_rules(self, conf): """ This function forms the regex to fetch the 'access-rules' for specific interface. :param conf: configuration data. :return: generated access-rules list configuration. """ ar_lst = [] v4_ar = findall(r"^.*(in|out|local) name .*$", conf, M) v6_ar = findall(r"^.*(in|out|local) ipv6-name .*$", conf, M) if v4_ar: v4_conf = "\n".join(findall(r"(^.*?%s.*?$)" % " name", conf, M)) config = self.parse_int_rules(v4_conf, "ipv4") if config: ar_lst.append(config) if v6_ar: v6_conf = "\n".join(findall(r"(^.*?%s.*?$)" % " ipv6-name", conf, M)) config = self.parse_int_rules(v6_conf, "ipv6") if config: ar_lst.append(config) if ar_lst: ar_lst = sorted(ar_lst, key=lambda i: i["afi"]) else: empty_rules = findall(r"^.*(in|out|local).*", conf, M) if empty_rules: ar_lst.append({"afi": "ipv4", "rules": []}) ar_lst.append({"afi": "ipv6", "rules": []}) return ar_lst def parse_int_rules(self, conf, afi): """ This function forms the regex to fetch the 'access-rules' for specific interface based on ip-type. :param conf: configuration data. :param rules: rules configured per interface. :param afi: ip address type. :return: generated rule configuration dictionary. """ r_lst = [] config = {} rules = ["in", "out", "local"] for r in set(rules): fr = {} r_regex = r" %s .+$" % r cfg = "\n".join(findall(r_regex, conf, M)) if cfg: fr = self.parse_rules(cfg, afi) else: out = search(r"^.*firewall " + "'" + r + "'" + "(.*)", conf, M) if out: fr = {"direction": r} if fr: r_lst.append(fr) if r_lst: r_lst = sorted(r_lst, key=lambda i: i["direction"]) config = {"afi": afi, "rules": r_lst} return config def parse_rules(self, conf, afi): """ This function triggers the parsing of 'rule' attributes. a_lst is a list having rule attributes which doesn't have further sub attributes. :param conf: configuration. :param afi: ip address type. :return: generated rule configuration dictionary. """ cfg = {} out = findall(r"[^\s]+", conf, M) if out: cfg["direction"] = out[0].strip("'") if afi == "ipv6": out = findall(r"[^\s]+ ipv6-name (?:\'*)(\S+)(?:\'*)", conf, M) if out: cfg["name"] = str(out[0]).strip("'") else: out = findall(r"[^\s]+ name (?:\'*)(\S+)(?:\'*)", conf, M) if out: cfg["name"] = out[-1].strip("'") return cfg diff --git a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py index f8f02e7a..31cc1fa5 100644 --- a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py +++ b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py @@ -1,544 +1,544 @@ # # -*- 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 firewall_rules 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 +from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy from re import M, findall, search from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import ( Firewall_rulesArgs, ) class Firewall_rulesFacts(object): """The vyos firewall_rules fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Firewall_rulesArgs.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 firewall_rules :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: # typically data is populated from the current device configuration # data = connection.get('show running-config | section ^interface') # using mock data instead data = self.get_device_data(connection) # split the config into instances of the resource objs = [] # check 1.4+ first new_rules = True v6_rules = findall( r"^set firewall ipv6 (name|forward|input|output) (?:\'*)(\S+)(?:\'*)", data, M, ) if not v6_rules: v6_rules = findall(r"^set firewall ipv6-name (?:\'*)(\S+)(?:\'*)", data, M) if v6_rules: new_rules = False v4_rules = findall( r"^set firewall ipv4 (name|forward|input|output) (?:\'*)(\S+)(?:\'*)", data, M, ) if not v4_rules: v4_rules = findall(r"^set firewall name (?:\'*)(\S+)(?:\'*)", data, M) if v4_rules: new_rules = False if v6_rules: if new_rules: config = self.get_rules_post_1_4(data, v6_rules, type="ipv6") else: config = self.get_rules(data, v6_rules, type="ipv6") if config: config = utils.remove_empties(config) objs.append(config) if v4_rules: if new_rules: config = self.get_rules_post_1_4(data, v4_rules, type="ipv4") else: config = self.get_rules(data, v4_rules, type="ipv4") if config: config = utils.remove_empties(config) objs.append(config) ansible_facts["ansible_network_resources"].pop("firewall_rules", None) facts = {} if objs: facts["firewall_rules"] = [] params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["firewall_rules"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def get_rules(self, data, rules, type): """ This function performs following: - Form regex to fetch 'rule-sets' specific config from data. - Form the rule-set list based on ip address. :param data: configuration. :param rules: list of rule-sets. :param type: ip address type. :return: generated rule-sets configuration. """ r_v4 = [] r_v6 = [] for r in set(rules): name_key = "ipv6-name" if type == "ipv6" else "name" rule_regex = r" %s %s .+$" % (name_key, r.strip("'")) cfg = findall(rule_regex, data, M) fr = self.render_config(cfg, r.strip("'")) fr["name"] = r.strip("'") if type == "ipv6": r_v6.append(fr) else: r_v4.append(fr) if r_v4: config = {"afi": "ipv4", "rule_sets": r_v4} if r_v6: config = {"afi": "ipv6", "rule_sets": r_v6} return config def get_rules_post_1_4(self, data, rules, type): """ This function performs following: - Form regex to fetch 'rule-sets' specific config from data. - Form the rule-set list based on ip address. Specifically for v1.4+ version. :param data: configuration. :param rules: list of rule-sets. :param type: ip address type. :return: generated rule-sets configuration. """ r_v4 = [] r_v6 = [] for kind, name in set(rules): rule_regex = r" %s %s %s .+$" % (type, kind, name.strip("'")) cfg = findall(rule_regex, data, M) fr = self.render_config(cfg, name.strip("'")) if kind == "name": fr["name"] = name.strip("'") elif kind in ("forward", "input", "output"): fr["filter"] = kind else: raise ValueError("Unknown rule kind: %s %s" % kind, name) if type == "ipv6": r_v6.append(fr) else: r_v4.append(fr) if r_v4: config = {"afi": "ipv4", "rule_sets": r_v4} if r_v6: config = {"afi": "ipv6", "rule_sets": r_v6} return config def render_config(self, conf, match): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ conf = "\n".join(filter(lambda x: x, conf)) a_lst = [ "description", "default_action", "default_jump_target", "enable_default_log", "default_log", ] config = self.parse_attr(conf, a_lst, match) if not config: config = {} if "default_log" in config: config["enable_default_log"] = config.pop("default_log") config["rules"] = self.parse_rules_lst(conf) return config def parse_rules_lst(self, conf): """ This function forms the regex to fetch the 'rules' with in 'rule-sets' :param conf: configuration data. :return: generated rule list configuration. """ r_lst = [] rules = findall(r"rule (?:\'*)(\d+)(?:\'*)", conf, M) if rules: rules_lst = [] for r in set(rules): r_regex = r" %s .+$" % r cfg = "\n".join(findall(r_regex, conf, M)) obj = self.parse_rules(cfg) obj["number"] = int(r) if obj: rules_lst.append(obj) r_lst = sorted(rules_lst, key=lambda i: i["number"]) return r_lst def parse_rules(self, conf): """ This function triggers the parsing of 'rule' attributes. a_lst is a list having rule attributes which doesn't have further sub attributes. :param conf: configuration :return: generated rule configuration dictionary. """ a_lst = [ "ipsec", "log", "action", "protocol", "fragment", "disable", "description", "icmp", "jump_target", "offload_target", "queue", "queue_options", ] rule = self.parse_attr(conf, a_lst) r_sub = { "tcp": self.parse_tcp(conf), "icmp": self.parse_icmp(conf, "icmp"), "time": self.parse_time(conf, "time"), "limit": self.parse_limit(conf, "limit"), "state": self.parse_state(conf, "state"), "recent": self.parse_recent(conf, "recent"), "source": self.parse_src_or_dest(conf, "source"), "destination": self.parse_src_or_dest(conf, "destination"), "inbound_interface": self.parse_interface(conf, "inbound-interface"), "outbound_interface": self.parse_interface(conf, "outbound-interface"), "packet_length": self.parse_packet_length(conf, "packet-length"), "packet_length_exclude": self.parse_packet_length(conf, "packet-length-exclude"), } rule.update(r_sub) return rule def parse_interface(self, conf, attrib): """ This function triggers the parsing of 'interface' attributes. :param conf: configuration. :param attrib: 'interface'. :return: generated config dictionary. """ a_lst = ["name", "group"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_packet_length(self, conf, attrib=None): """ This function triggers the parsing of 'packet-length' attributes. :param conf: configuration. :param attrib: 'packet-length'. :return: generated config dictionary. """ lengths = [] rule_regex = r"%s (.+)$" % attrib found_lengths = findall(rule_regex, conf, M) if found_lengths: lengths = [] for pplen in set(found_lengths): obj = {"length": pplen.strip("'")} lengths.append(obj) return lengths def parse_src_or_dest(self, conf, attrib=None): """ This function triggers the parsing of 'source or destination' attributes. :param conf: configuration. :param attrib:'source/destination'. :return:generated source/destination configuration dictionary. """ a_lst = ["port", "address", "mac_address"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) cfg_dict["group"] = self.parse_group(conf, attrib + " group") return cfg_dict def parse_recent(self, conf, attrib=None): """ This function triggers the parsing of 'recent' attributes :param conf: configuration. :param attrib: 'recent'. :return: generated config dictionary. """ a_lst = ["time", "count"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_tcp(self, conf): """ This function triggers the parsing of 'tcp' attributes. :param conf: configuration. :param attrib: 'tcp'. :return: generated config dictionary. """ f_lst = [] flags = findall(r"tcp flags (not )?(?:\'*)([\w!,]+)(?:\'*)", conf, M) # for pre 1.4, this is a string including possible commas # and ! as an inverter. For 1.4+ this is a single flag per # command and 'not' as the inverter if flags: flag_lst = [] for n, f in set(flags): f = f.strip("'").lower() if "," in f: # pre 1.4 version with multiple flags fs = f.split(",") for f in fs: if "!" in f: obj = {"flag": f.strip("'!"), "invert": True} else: obj = {"flag": f.strip("'")} flag_lst.append(obj) elif "!" in f: obj = {"flag": f.strip("'!"), "invert": True} flag_lst.append(obj) else: obj = {"flag": f.strip("'")} if n: obj["invert"] = True flag_lst.append(obj) f_lst = sorted(flag_lst, key=lambda i: i["flag"]) return {"flags": f_lst} def parse_time(self, conf, attrib=None): """ This function triggers the parsing of 'time' attributes. :param conf: configuration. :param attrib: 'time'. :return: generated config dictionary. """ a_lst = [ "stopdate", "stoptime", "weekdays", "monthdays", "startdate", "starttime", ] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_state(self, conf, attrib=None): """ This function triggers the parsing of 'state' attributes. :param conf: configuration :param attrib: 'state'. :return: generated config dictionary. """ a_lst = ["new", "invalid", "related", "established"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_group(self, conf, attrib=None): """ This function triggers the parsing of 'group' attributes. :param conf: configuration. :param attrib: 'group'. :return: generated config dictionary. """ a_lst = ["port_group", "address_group", "network_group"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_icmp_attr(self, conf, match): """ This function peforms the following: - parse ICMP arguemnts for firewall rules - consider that older versions may need numbers or letters in type, newer ones are more specific :param conf: configuration. :param match: parent node/attribute name. :return: generated config dictionary. """ config = {} if not conf: return config for attrib in ("code", "type", "type-name"): regex = self.map_regex(attrib) if match: regex = match + " " + regex out = search(r"^.*" + regex + " (.+)", conf, M) if out: val = out.group(1).strip("'") if attrib == "type-name": config["type_name"] = val if attrib == "code": config["code"] = int(val) if attrib == "type": # <1.3 could be # (type), #/# (type/code) or 'type' (type_name) # recent this is only for strings if "/" in val: # type/code (type_no, code) = val.split("/") config["type"] = int(type_no) config["code"] = int(code) elif val.isnumeric(): config["type"] = int(val) else: config["type_name"] = val return config def parse_icmp(self, conf, attrib=None): """ This function triggers the parsing of 'icmp' attributes. :param conf: configuration to be parsed. :param attrib: 'icmp'. :return: generated config dictionary. """ cfg_dict = self.parse_icmp_attr(conf, "icmp") if len(cfg_dict) == 0: cfg_dict = self.parse_icmp_attr(conf, "icmpv6") return cfg_dict def parse_limit(self, conf, attrib=None): """ This function triggers the parsing of 'limit' attributes. :param conf: configuration to be parsed. :param attrib: 'limit' :return: generated config dictionary. """ cfg_dict = self.parse_attr(conf, ["burst"], match=attrib) cfg_dict["rate"] = self.parse_rate(conf, "rate") 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 + " " + regex if conf: if self.is_bool(attrib): out = conf.find(attrib.replace("_", "-")) dis = conf.find(attrib.replace("_", "-") + " 'disable'") if out >= 1: if dis >= 1: config[attrib] = False else: config[attrib] = True else: out = search(r"^.*" + regex + " (.+)", conf, M) if not out: if attrib == "disable": out = search(r"^.*\d+" + " (disable$)", conf, M) if attrib == "log": out = search(r"^.*\d+" + " (log$)", conf, M) if out: val = out.group(1).strip("'") if self.is_num(attrib): val = int(val) if attrib == "disable": val = True if attrib == "log": val = "enable" 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 """ regex = attrib.replace("_", "-") if attrib == "disabled": regex = "disable" return regex def is_bool(self, attrib): """ This function looks for the attribute in predefined bool type set. :param attrib: attribute. :return: True/False """ bool_set = ( "new", "invalid", "related", "disabled", "established", "enable_default_log", "default_log", ) 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 = ("time", "code", "type", "count", "burst", "number") return True if attrib in num_set else False def parse_rate(self, conf, match): """ This function triggers the parsing of 'rate' attributes. :param conf: configuration. :param attrib: 'rate' :return: generated config dictionary. """ config = {} out = search(r"^.*" + match + " (.+)", conf, M) if out: val = out.group(1).strip("'") if "/" in val: # number/unit - (number, unit) = val.split("/") + number, unit = val.split("/") config["number"] = number config["unit"] = unit return config diff --git a/plugins/module_utils/network/vyos/facts/hostname/hostname.py b/plugins/module_utils/network/vyos/facts/hostname/hostname.py index b4f7c529..18a66357 100644 --- a/plugins/module_utils/network/vyos/facts/hostname/hostname.py +++ b/plugins/module_utils/network/vyos/facts/hostname/hostname.py @@ -1,72 +1,71 @@ # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos hostname fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.hostname.hostname import ( HostnameArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.hostname import ( HostnameTemplate, ) class HostnameFacts(object): """The vyos hostname facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = HostnameArgs.argument_spec def get_config(self, connection): return connection.get("show configuration commands | grep host-name") def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Snmp_server network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] config_lines = [] if not data: data = self.get_config(connection) for resource in data.splitlines(): config_lines.append(re.sub("'", "", resource)) # parse native config using the Hostname template hostname_parser = HostnameTemplate(lines=config_lines, module=self._module) objs = hostname_parser.parse() ansible_facts["ansible_network_resources"].pop("hostname", None) params = utils.remove_empties( hostname_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), ) facts["hostname"] = params.get("config", {}) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py b/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py index cd8008c6..51f3c5f6 100644 --- a/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py +++ b/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py @@ -1,146 +1,145 @@ # # -*- 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 interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function - __metaclass__ = type from copy import deepcopy from re import M, findall, search from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.interfaces.interfaces import ( InterfacesArgs, ) class InterfacesFacts(object): """The vyos interfaces fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = InterfacesArgs.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): data = connection.get_config(flags=["| grep interfaces"]) return data def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for interfaces :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) objs = [] interface_names = findall( r"^set interfaces (?:ethernet|bonding|bridge|dummy|tunnel|vti|loopback|vxlan|openvpn|wireguard) (?:\'*)(\S+)(?:\'*)", data, M, ) if interface_names: for interface in set(interface_names): intf_regex = r" %s (.+$)" % interface.strip("'") cfg = findall(intf_regex, data, M) obj = self.render_config(cfg) obj["name"] = interface.strip("'") if obj: objs.append(obj) facts = {} if objs: facts["interfaces"] = [] params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["interfaces"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ vif_conf = "\n".join(filter(lambda x: ("vif" in x), conf)) eth_conf = "\n".join(filter(lambda x: ("vif" not in x), conf)) config = self.parse_attribs(["description", "speed", "mtu", "duplex"], eth_conf) config["vifs"] = self.parse_vifs(vif_conf) return utils.remove_empties(config) def parse_vifs(self, conf): vif_names = findall(r"vif (?:\'*)(\d+)(?:\'*)", conf, M) vifs_list = None if vif_names: vifs_list = [] for vif in set(vif_names): vif_regex = r"%s (.+$)" % vif cfg = "\n".join(findall(vif_regex, conf, M)) obj = self.parse_attribs(["description", "mtu"], cfg) obj["vlan_id"] = int(vif) if obj: vifs_list.append(obj) vifs_list = sorted(vifs_list, key=lambda i: i["vlan_id"]) return vifs_list def parse_attribs(self, attribs, conf): """ Parse the attributes of a network interface. :param attribs: List of attribute names. :param conf: Configuration string. :rtype: dict :returns: Parsed configuration dictionary. """ config = {} for item in attribs: value = utils.parse_conf_arg(conf, item) if value and item == "mtu": config[item] = int(value.strip("'")) elif value: config[item] = value.strip("'") else: config[item] = None # match only on disable next to the interface name # there are other sub-commands that can be disabled match = search(r"^ *disable", conf, M) if match: config["enabled"] = False else: config["enabled"] = True return utils.remove_empties(config) diff --git a/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py index d62ea9b8..9e79e24d 100644 --- a/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py +++ b/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py @@ -1,145 +1,144 @@ # # -*- 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 l3_interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function - __metaclass__ = type import re from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.l3_interfaces.l3_interfaces import ( L3_interfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( get_ip_address_version, ) class L3_interfacesFacts(object): """The vyos l3_interfaces fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = L3_interfacesArgs.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 populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for l3_interfaces :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = connection.get_config() # operate on a collection of resource x objs = [] interface_names = re.findall( r"set interfaces (?:ethernet|bonding|bridge|dummy|tunnel|vti|loopback|vxlan) (?:\'*)(\S+)(?:\'*)", data, re.M, ) if interface_names: for interface in set(interface_names): intf_regex = r" %s .+$" % interface cfg = re.findall(intf_regex, data, re.M) obj = self.render_config(cfg) obj["name"] = interface.strip("'") if obj: objs.append(obj) ansible_facts["ansible_network_resources"].pop("l3_interfaces", None) facts = {} if objs: facts["l3_interfaces"] = [] params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["l3_interfaces"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ vif_conf = "\n".join(filter(lambda x: ("vif" in x), conf)) eth_conf = "\n".join(filter(lambda x: ("vif" not in x), conf)) config = self.parse_attribs(eth_conf) config["vifs"] = self.parse_vifs(vif_conf) return utils.remove_empties(config) def parse_vifs(self, conf): vif_names = re.findall(r"vif (\d+)", conf, re.M) vifs_list = None if vif_names: vifs_list = [] for vif in set(vif_names): vif_regex = r" %s .+$" % vif cfg = "\n".join(re.findall(vif_regex, conf, re.M)) obj = self.parse_attribs(cfg) obj["vlan_id"] = vif if obj: vifs_list.append(obj) return vifs_list def parse_attribs(self, conf): config = {} ipaddrs = re.findall(r"address (\S+)", conf, re.M) config["ipv4"] = [] config["ipv6"] = [] for item in ipaddrs: item = item.strip("'") if item == "dhcp": config["ipv4"].append({"address": item}) elif item == "dhcpv6": config["ipv6"].append({"address": item}) elif item == "no-default-link-local": config["ipv6"].append({"address": item}) elif item == "autoconf": config["ipv6"].append({"address": item}) else: ip_version = get_ip_address_version(item.split("/")[0]) if ip_version == 4: config["ipv4"].append({"address": item}) else: config["ipv6"].append({"address": item}) for key, value in config.items(): if value == []: config[key] = None return utils.remove_empties(config) diff --git a/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py index 8e1c8624..94c73416 100644 --- a/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py +++ b/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py @@ -1,145 +1,145 @@ # # -*- 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 lag_interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ -from __future__ import absolute_import, division, print_function +from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy from re import M, findall, search from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lag_interfaces.lag_interfaces import ( Lag_interfacesArgs, ) class Lag_interfacesFacts(object): """The vyos lag_interfaces fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Lag_interfacesArgs.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_config(self, connection): """Get the configuration from the device :param connection: the device connection :rtype: string :returns: The configuration """ return connection.get_config() def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for lag_interfaces :param module: the module instance :param connection: the device connection :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = self.get_config(connection) objs = [] lag_names = findall(r"^set interfaces bonding (\S+)", data, M) if lag_names: for lag in set(lag_names): lag_regex = r" %s .+$" % lag cfg = findall(lag_regex, data, M) obj = self.render_config(cfg) members = [] member = {} group_regex = r"%s member interface .*eth.*" % lag g_cfg = findall(group_regex, data, M) for item in g_cfg: output = search("member interface '(\\S+)'", item, M) if output: member["member"] = output.group(1).strip("'") members.append(deepcopy(member)) obj["name"] = lag.strip("'") if members: obj["members"] = members if obj: objs.append(obj) facts = {} if objs: facts["lag_interfaces"] = [] params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["lag_interfaces"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ arp_monitor_conf = "\n".join(filter(lambda x: ("arp-monitor" in x), conf)) hash_policy_conf = "\n".join(filter(lambda x: ("hash-policy" in x), conf)) lag_conf = "\n".join(filter(lambda x: ("bond" in x), conf)) config = self.parse_attribs(["mode", "primary"], lag_conf) config["arp_monitor"] = self.parse_arp_monitor(arp_monitor_conf) config["hash_policy"] = self.parse_hash_policy(hash_policy_conf) return utils.remove_empties(config) def parse_attribs(self, attribs, conf): config = {} for item in attribs: value = utils.parse_conf_arg(conf, item) if value: config[item] = value.strip("'") else: config[item] = None return utils.remove_empties(config) def parse_arp_monitor(self, conf): arp_monitor = None if conf: arp_monitor = {} target_list = [] interval = search(r"^.*arp-monitor interval (.+)", conf, M) targets = findall(r"^.*arp-monitor target '(.+)'", conf, M) if targets: for target in targets: target_list.append(target) arp_monitor["target"] = target_list if interval: value = interval.group(1).strip("'") arp_monitor["interval"] = int(value) return arp_monitor def parse_hash_policy(self, conf): hash_policy = None if conf: hash_policy = search(r"^.*hash-policy (.+)", conf, M) hash_policy = hash_policy.group(1).strip("'") return hash_policy diff --git a/plugins/module_utils/network/vyos/facts/legacy/base.py b/plugins/module_utils/network/vyos/facts/legacy/base.py index 30978e0e..59666e19 100644 --- a/plugins/module_utils/network/vyos/facts/legacy/base.py +++ b/plugins/module_utils/network/vyos/facts/legacy/base.py @@ -1,160 +1,159 @@ # -*- 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 interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function - __metaclass__ = type import platform import re from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( get_capabilities, run_commands, ) class LegacyFactsBase(object): COMMANDS = frozenset() def __init__(self, module): self.module = module self.facts = dict() self.warnings = list() self.responses = None def populate(self): self.responses = run_commands(self.module, list(self.COMMANDS)) class Default(LegacyFactsBase): COMMANDS = [ "show version", ] def populate(self): super(Default, self).populate() data = self.responses[0] self.facts["serialnum"] = self.parse_serialnum(data) self.facts.update(self.platform_facts()) def parse_serialnum(self, data): match = re.search(r"(?:HW|Hardware) S/N:\s+(\S+)", data) if match: return match.group(1) def platform_facts(self): platform_facts = {} resp = get_capabilities(self.module) device_info = resp["device_info"] platform_facts["system"] = device_info["network_os"] for item in ("model", "image", "version", "platform", "hostname"): val = device_info.get("network_os_%s" % item) if val: platform_facts[item] = val platform_facts["api"] = resp["network_api"] platform_facts["python_version"] = platform.python_version() return platform_facts class Config(LegacyFactsBase): COMMANDS = [ "show configuration commands", "show system commit", ] def populate(self): super(Config, self).populate() self.facts["config"] = self.responses commits = self.responses[1] entries = list() entry = None for line in commits.split("\n"): match = re.match(r"(\d+)\s+(.+)by(.+)via(.+)", line) if match: if entry: entries.append(entry) entry = dict( revision=match.group(1), datetime=match.group(2), by=str(match.group(3)).strip(), via=str(match.group(4)).strip(), comment=None, ) else: entry["comment"] = line.strip() self.facts["commits"] = entries class Neighbors(LegacyFactsBase): COMMANDS = [ "show lldp neighbors", "show lldp neighbors detail", ] def populate(self): super(Neighbors, self).populate() all_neighbors = self.responses[0] if "LLDP not configured" not in all_neighbors: neighbors = self.parse(self.responses[1]) self.facts["neighbors"] = self.parse_neighbors(neighbors) def parse(self, data): parsed = list() values = None for line in data.split("\n"): if not line: continue if line[0] == " ": values += "\n%s" % line elif line.startswith("Interface"): if values: parsed.append(values) values = line if values: parsed.append(values) return parsed def parse_neighbors(self, data): facts = dict() for item in data: interface = self.parse_interface(item) host = self.parse_host(item) port = self.parse_port(item) if interface not in facts: facts[interface] = list() facts[interface].append(dict(host=host, port=port)) return facts def parse_interface(self, data): match = re.search(r"^Interface:\s+(\S+),", data) return match.group(1) def parse_host(self, data): match = re.search(r"SysName:\s+(.+)$", data, re.M) if match: return match.group(1) def parse_port(self, data): match = re.search(r"PortDescr:\s+(.+)$", data, re.M) if match: return match.group(1) diff --git a/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py b/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py index 7a6e9b8e..40ff2ad3 100644 --- a/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py +++ b/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py @@ -1,126 +1,126 @@ # # -*- 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 lldp_global 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 +from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy from re import M, findall from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_global.lldp_global import ( Lldp_globalArgs, ) class Lldp_globalFacts(object): """The vyos lldp_global fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Lldp_globalArgs.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_config(self, connection): return connection.get("show configuration commands | grep lldp") def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for lldp_global :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_config(connection) objs = {} lldp_output = findall(r"^set service lldp (\S+)", data, M) if lldp_output: for item in set(lldp_output): lldp_regex = r" %s .+$" % item cfg = findall(lldp_regex, data, M) obj = self.render_config(cfg) if obj: objs.update(obj) lldp_service = findall(r"^set service (lldp)?('lldp')", data, M) if lldp_service or lldp_output: lldp_obj = {} lldp_obj["enable"] = True objs.update(lldp_obj) facts = {} params = utils.validate_config(self.argument_spec, {"config": objs}) facts["lldp_global"] = 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 and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ protocol_conf = "\n".join(filter(lambda x: ("legacy-protocols" in x), conf)) att_conf = "\n".join(filter(lambda x: ("snmp" in x), conf)) addr_conf = "\n".join(filter(lambda x: ("management-address" in x), conf)) config = self.parse_attribs(["snmp"], att_conf) config["legacy_protocols"] = self.parse_protocols(protocol_conf) config["addresses"] = self.parse_addresses(addr_conf) return utils.remove_empties(config) def parse_protocols(self, conf): protocol_support = None if conf: protocols = findall(r"^.*legacy-protocols (.+)", conf, M) if protocols: protocol_support = [] for protocol in protocols: protocol_support.append(protocol.strip("'")) return protocol_support def parse_addresses(self, conf): management_addresses = None if conf: addresses = findall(r"^.*management-address (.+)", conf, M) if addresses: management_addresses = [] for protocol in addresses: management_addresses.append(protocol.strip("'")) return management_addresses def parse_attribs(self, attribs, conf): config = {} for item in attribs: value = utils.parse_conf_arg(conf, item) if value: config[item] = value.strip("'") else: config[item] = None return utils.remove_empties(config) diff --git a/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py b/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py index e029b47a..72bdf154 100644 --- a/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py +++ b/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py @@ -1,127 +1,126 @@ # # -*- 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 lldp_interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function - __metaclass__ = type from copy import deepcopy from re import M, findall, search from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_interfaces.lldp_interfaces import ( Lldp_interfacesArgs, ) class Lldp_interfacesFacts(object): """The vyos lldp_interfaces fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Lldp_interfacesArgs.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 populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for lldp_interfaces :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = connection.get_config() objs = [] lldp_names = findall(r"^set service lldp interface (\S+)", data, M) if lldp_names: for lldp in set(lldp_names): lldp_regex = r" %s .+$" % lldp cfg = findall(lldp_regex, data, M) obj = self.render_config(cfg) obj["name"] = lldp.strip("'") if obj: objs.append(obj) facts = {} if objs: facts["lldp_interfaces"] = objs ansible_facts["ansible_network_resources"].update(facts) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = {} location = {} elin_conf = "\n".join(filter(lambda x: ("elin" in x), conf)) coordinate_conf = "\n".join(filter(lambda x: ("coordinate-based" in x), conf)) disable = "\n".join(filter(lambda x: ("disable" in x), conf)) coordinate_based_conf = self.parse_attribs( ["altitude", "datum", "longitude", "latitude"], coordinate_conf, ) elin_based_conf = self.parse_lldp_elin_based(elin_conf) if disable: config["enable"] = False if coordinate_conf: location["coordinate_based"] = coordinate_based_conf config["location"] = location elif elin_conf: location["elin"] = elin_based_conf config["location"] = location return utils.remove_empties(config) def parse_attribs(self, attribs, conf): config = {} for item in attribs: value = utils.parse_conf_arg(conf, item) if value: value = value.strip("'") if item == "altitude": value = int(value) config[item] = value else: config[item] = None return utils.remove_empties(config) def parse_lldp_elin_based(self, conf): elin_based = None if conf: e_num = search(r"^.* elin (.+)", conf, M) elin_based = e_num.group(1).strip("'") return elin_based diff --git a/plugins/module_utils/network/vyos/facts/logging_global/logging_global.py b/plugins/module_utils/network/vyos/facts/logging_global/logging_global.py index da80d967..1e89aad6 100644 --- a/plugins/module_utils/network/vyos/facts/logging_global/logging_global.py +++ b/plugins/module_utils/network/vyos/facts/logging_global/logging_global.py @@ -1,100 +1,99 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos logging_global 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 ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.logging_global.logging_global import ( Logging_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.logging_global import ( Logging_globalTemplate, ) class Logging_globalFacts(object): """The vyos logging_global facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Logging_globalArgs.argument_spec def get_logging_data(self, connection): return connection.get("show configuration commands | grep syslog") def process_facts(self, objFinal): if objFinal: for ke, vl in objFinal.items(): if ke == "files": _files = [] for k, v in vl.items(): _files.append(v) objFinal[ke] = _files objFinal[ke] = sorted(objFinal[ke], key=lambda item: item["path"]) elif ke == "hosts": _hosts = [] for k, v in vl.items(): _hosts.append(v) objFinal[ke] = _hosts objFinal[ke] = sorted(objFinal[ke], key=lambda item: item["hostname"]) elif ke == "users": _users = [] for k, v in vl.items(): _users.append(v) objFinal[ke] = _users objFinal[ke] = sorted(objFinal[ke], key=lambda item: item["username"]) elif ke == "console" or ke == "global_params": if objFinal[ke].get("facilities"): objFinal[ke]["facilities"] = sorted( objFinal[ke]["facilities"], key=lambda item: item["facility"], ) return objFinal def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Logging_global network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] if not data: data = self.get_logging_data(connection) # parse native config using the Logging_global template logging_global_parser = Logging_globalTemplate(lines=data.splitlines(), module=self._module) objs = logging_global_parser.parse() ansible_facts["ansible_network_resources"].pop("logging_global", None) objs = self.process_facts(objs) params = utils.remove_empties( logging_global_parser.validate_config( self.argument_spec, {"config": objs}, redact=True, ), ) facts["logging_global"] = params.get("config", {}) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/ntp_global/ntp_global.py b/plugins/module_utils/network/vyos/facts/ntp_global/ntp_global.py index 880bc79f..a6d6892f 100644 --- a/plugins/module_utils/network/vyos/facts/ntp_global/ntp_global.py +++ b/plugins/module_utils/network/vyos/facts/ntp_global/ntp_global.py @@ -1,92 +1,91 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos ntp fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ntp_global.ntp_global import ( Ntp_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ntp_global import ( NtpTemplate, ) class Ntp_globalFacts(object): """The vyos ntp facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Ntp_globalArgs.argument_spec def get_config(self, connection): return connection.get("show configuration commands | grep ntp") def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Ntp network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] config_lines = [] if not data: data = self.get_config(connection) for resource in data.splitlines(): config_lines.append(re.sub("'", "", resource)) # parse native config using the Ntp template ntp_parser = NtpTemplate(lines=config_lines, module=self._module) objs = ntp_parser.parse() if objs: if "allow_clients" in objs: objs["allow_clients"] = sorted(list(objs["allow_clients"])) if "listen_addresses" in objs: objs["listen_addresses"] = sorted(list(objs["listen_addresses"])) """ if "options" in objs["servers"].values(): val = objs["servers"].values() val["options"] = sorted(val["options"]) """ if "servers" in objs: objs["servers"] = list(objs["servers"].values()) objs["servers"] = sorted(objs["servers"], key=lambda k: k["server"]) for i in objs["servers"]: if "options" in i: i["options"] = sorted(list(i["options"])) ansible_facts["ansible_network_resources"].pop("ntp_global", None) params = utils.remove_empties( ntp_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), ) if params.get("config"): facts["ntp_global"] = params["config"] ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py b/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py index 2160fc71..1a9a687d 100644 --- a/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py +++ b/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py @@ -1,135 +1,134 @@ # -*- coding: utf-8 -*- # Copyright 2020 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos ospf_interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospf_interfaces.ospf_interfaces import ( Ospf_interfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ospf_interfaces import ( Ospf_interfacesTemplate, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ospf_interfaces_14 import ( Ospf_interfacesTemplate14, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Ospf_interfacesFacts(object): """The vyos ospf_interfaces facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Ospf_interfacesArgs.argument_spec def get_device_data(self, connection): if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): # use set protocols ospf in order to get both ospf and ospfv3 return connection.get("show configuration commands | match 'set protocols ospf'") return connection.get('show configuration commands | match "set interfaces"') def get_config_set_1_4(self, data): """To classify the configurations beased on interface""" config_dict = {} for config_line in data.splitlines(): ospf_int = re.search(r"set protocols (?:ospf|ospfv3) interface (\S+).*", config_line) if ospf_int: config_dict[ospf_int.group(1)] = ( config_dict.get(ospf_int.group(1), "") + config_line + "\n" ) return list(config_dict.values()) def get_config_set_1_2(self, data): """To classify the configurations beased on interface""" interface_list = [] config_set = [] int_string = "" for config_line in data.splitlines(): ospf_int_raw = re.findall(r"^set interfaces \S+ (\S+)", config_line, re.M) ospf_int_vif = re.findall(r"^set interfaces \S+ (\S+) vif (\d+)", config_line, re.M) ospf_int = ospf_int_raw + ospf_int_vif if ospf_int: if ospf_int not in interface_list: if int_string: config_set.append(int_string) interface_list.append(ospf_int) int_string = "" int_string = int_string + config_line + "\n" if int_string: config_set.append(int_string) return config_set def get_config_set(self, data, connection): """To classify the configurations beased on interface""" if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): return self.get_config_set_1_4(data) return self.get_config_set_1_2(data) def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Ospf_interfaces network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): ospf_interface_class = Ospf_interfacesTemplate14 else: ospf_interface_class = Ospf_interfacesTemplate ospf_interfaces_parser = ospf_interface_class(lines=[], module=self._module) if not data: data = self.get_device_data(connection) # parse native config using the Ospf_interfaces template ospf_interfaces_facts = [] resources = self.get_config_set(data, connection) for resource in resources: ospf_interfaces_parser = ospf_interface_class( lines=resource.split("\n"), module=self._module, ) objs = ospf_interfaces_parser.parse() for key, sortv in [("address_family", "afi")]: if key in objs and objs[key]: objs[key] = list(objs[key].values()) ospf_interfaces_facts.append(objs) ansible_facts["ansible_network_resources"].pop("ospf_interfaces", None) facts = {"ospf_interfaces": []} params = utils.remove_empties( ospf_interfaces_parser.validate_config( self.argument_spec, {"config": ospf_interfaces_facts}, redact=True, ), ) if params.get("config"): for cfg in params["config"]: facts["ospf_interfaces"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py index d07bf13a..056f949d 100644 --- a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py @@ -1,509 +1,509 @@ # # -*- 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 +from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy from re import M, findall, search from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import ( Ospfv2Args, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Ospfv2Facts(object): """The vyos ospfv2 fact class""" def __init__( self, module, subspec="config", options="options", ): self._module = module self.argument_spec = Ospfv2Args.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def get_device_data(self, connection): return connection.get_config() def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for ospfv2 :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = self.get_device_data(connection) # typically data is populated from the current device configuration # data = connection.get('show running-config | section ^interface') # using mock data instead objs = {} ospfv2 = findall(r"^set protocols ospf (.+)", data, M) if ospfv2: objs = self.render_config(ospfv2) facts = {} params = utils.validate_config(self.argument_spec, {"config": objs}) facts["ospfv2"] = utils.remove_empties(params["config"]) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure :param conf: The configuration :returns: The generated config """ conf = "\n".join(filter(lambda x: x, conf)) a_lst = ["default_metric", "log_adjacency_changes"] config = self.parse_attr(conf, a_lst) if not config: config = {} config["timers"] = self.parse_timers(conf) config["auto_cost"] = self.parse_auto_cost(conf) config["distance"] = self.parse_distance(conf) config["max_metric"] = self.parse_max_metric(conf) config["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_passive(conf, "passive-interface") config["redistribute"] = self.parse_attrib_list(conf, "redistribute", "route_type") config["passive_interface_exclude"] = self.parse_passive( conf, "passive-interface-exclude", ) return config def parse_timers(self, conf): """ This function triggers the parsing of 'timers' attributes :param conf: configuration :return: generated config dictionary """ cfg_dict = {} cfg_dict["refresh"] = self.parse_refresh(conf, "refresh") cfg_dict["throttle"] = self.parse_throttle(conf, "spf") return cfg_dict def parse_throttle(self, conf, attrib=None): """ This function triggers the parsing of 'throttle' attributes :param conf: configuration :param attrib: 'spf' :return: generated config dictionary """ cfg_dict = {} cfg_dict[attrib] = self.parse_attrib(conf, attrib, match=attrib) return cfg_dict def parse_refresh(self, conf, attrib=None): """ This function triggers the parsing of 'refresh' attributes :param conf: configuration :param attrib: 'refresh' :return: generated config dictionary """ cfg_dict = self.parse_attr(conf, ["timers"], match=attrib) return cfg_dict def parse_leaf_list(self, conf, attrib): """ This function forms the regex to fetch the listed attributes from the configuration data :param conf: configuration data :param attrib: attribute name :return: generated rule list configuration """ lst = [] items = findall(r"^" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M) if items: for i in set(items): lst.append(i.strip("'")) lst.sort() return lst def parse_passive(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 = [] if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): if attrib == "passive-interface-exclude": items = findall("^interface (?:'*)(\\S+)(?:'*) passive disable$", conf, M) else: items = findall("^interface (?:'*)(\\S+)(?:'*) passive$", conf, M) items += findall(r"^" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M) if items: for i in set(items): lst.append(i.strip("'")) lst.sort() return lst def parse_distance(self, conf, attrib=None): """ This function triggers the parsing of 'distance' attributes :param conf: configuration :param attrib: attribute name :return: generated config dictionary """ cfg_dict = self.parse_attr(conf, ["global"], match=attrib) cfg_dict["ospf"] = self.parse_ospf(conf, "ospf") return cfg_dict def parse_ospf(self, conf, attrib=None): """ This function triggers the parsing of 'distance ospf' attributes :param conf: configuration :param attrib: 'ospf' :return: generated config dictionary """ cfg_dict = self.parse_attrib(conf, "ospf", match=attrib) return cfg_dict def parse_max_metric(self, conf): """ This function triggers the parsing of 'max_metric' attributes :param conf: configuration :return: generated config dictionary """ cfg_dict = {} cfg_dict["router_lsa"] = self.parse_attrib(conf, "router_lsa", match="router-lsa") return cfg_dict def parse_auto_cost(self, conf, attrib=None): """ This function triggers the parsing of 'auto_cost' attributes :param conf: configuration :param attrib: attribute name :return: generated config dictionary """ cfg_dict = self.parse_attr(conf, ["reference_bandwidth"], match=attrib) return cfg_dict def parse_def_info(self, conf): """ This function triggers the parsing of 'default_information' attributes :param conf: configuration :return: generated config dictionary """ cfg_dict = {} cfg_dict["originate"] = self.parse_attrib(conf, "originate", "originate") return cfg_dict def parse_area(self, conf, area_id): """ This function triggers the parsing of 'area' attributes. :param conf: configuration data :param area_id: area identity :return: generated rule configuration dictionary. """ rule = self.parse_attrib(conf, "area_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"), } 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", 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("'")} app_lst.append(obj) a_lst = sorted(app_lst, key=lambda i: i["address"]) return a_lst def parse_vlink(self, conf): """ This function triggers the parsing of '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")} 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_attrib_list(conf, "key-id", "key_id") 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.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 = { "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 if conf: if self.is_bool(attrib): 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") != -1 if out >= 1: if dis >= 1: config[attrib] = False else: config[attrib] = True elif match and en: config[attrib] = True else: out = search(r"^.*" + regex + " (.+)", conf, M) if out: val = out.group(1).strip("'") if self.is_num(attrib): val = int(val) config[attrib] = val return config def map_regex(self, attrib): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ return ( "disable" if attrib == "disabled" else ( "enable" if attrib == "enabled" else ("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 = ( "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", "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/facts/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py index ae67a4b6..5fd9e627 100644 --- a/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py +++ b/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py @@ -1,217 +1,217 @@ # # -*- 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 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 +from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy from re import M, findall, search from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils 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.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Ospfv3Facts(object): """The vyos ospfv3 fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Ospfv3Args.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 ospfv3 :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 = {} ospfv3 = findall(r"^set protocols ospfv3 (.+)", data, M) if ospfv3: objs = self.render_config(ospfv3) facts = {} params = utils.validate_config(self.argument_spec, {"config": objs}) facts["ospfv3"] = 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)) config = {} config["parameters"] = self.parse_attrib(conf, "parameters", "parameters") config["areas"] = self.parse_attrib_list(conf, "area", "area_id") config["redistribute"] = self.parse_attrib_list(conf, "redistribute", "route_type") return config 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 LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): cfg14 = findall(r"(interface .+) area '%s'$" % item, conf, M) cfg += "\n " + item + " " + " ".join(cfg14) if attrib == "area": obj = self.parse_area(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_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_id", match=area_id) r_sub = {"range": self.parse_attrib_list(conf, "range", "address")} rule.update(r_sub) r_int = {"interface": self.parse_attrib_list(conf, "interface", "name")} rule.update(r_int) return rule 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 = { "area_id": ["export_list", "import_list"], "redistribute": ["route_map"], "range": ["advertise", "not_advertise"], "interface": ["name"], "parameters": ["router_id"], } cfg_dict = self.parse_attr(conf, param_lst[param], match) return cfg_dict def parse_attr(self, conf, attr_list, match=None): """ This function peforms the following: - Form the regex to fetch the required attribute config. - Type cast the output in desired format. :param conf: configuration. :param attr_list: list of attributes. :param match: parent node/attribute name. :return: generated config dictionary. """ config = {} for attrib in attr_list: regex = self.map_regex(attrib) if match: regex = match.replace("_", "-") + " " + regex if conf: if self.is_bool(attrib): out = conf.find(attrib.replace("_", "-")) dis = conf.find(attrib.replace("_", "-") + " 'disable'") if match: en = conf.find(match + " 'enable'") if out >= 1: if dis >= 1: config[attrib] = False else: config[attrib] = True elif match and en >= 1: config[attrib] = True else: out = search(r"^.*" + regex + " (.+)", conf, M) if out: val = out.group(1).strip("'") if self.is_num(attrib): val = int(val) config[attrib] = val return config def map_regex(self, attrib): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ return ( "disable" if attrib == "disabled" else "enable" if attrib == "enabled" else attrib.replace("_", "-") ) def is_bool(self, attrib): """ This function looks for the attribute in predefined bool type set. :param attrib: attribute. :return: True/False """ bool_set = ("enabled", "advertise", "not_advertise") 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" return True if attrib in num_set else False diff --git a/plugins/module_utils/network/vyos/facts/prefix_lists/prefix_lists.py b/plugins/module_utils/network/vyos/facts/prefix_lists/prefix_lists.py index 17f63fb2..4bdcbf8c 100644 --- a/plugins/module_utils/network/vyos/facts/prefix_lists/prefix_lists.py +++ b/plugins/module_utils/network/vyos/facts/prefix_lists/prefix_lists.py @@ -1,89 +1,88 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos prefix_lists 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 ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.prefix_lists.prefix_lists import ( Prefix_listsArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.prefix_lists import ( Prefix_listsTemplate, ) class Prefix_listsFacts(object): """The vyos prefix_lists facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Prefix_listsArgs.argument_spec def get_config(self, connection): return connection.get("show configuration commands | grep prefix-list") def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Prefix_lists network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] if not data: data = self.get_config(connection) # parse native config using the Prefix_lists template prefix_lists_parser = Prefix_listsTemplate(lines=data.splitlines(), module=self._module) objs = prefix_lists_parser.parse() objs = sorted( list(objs.values()), key=lambda k: k["afi"], ) if objs: for item in objs: item["prefix_lists"] = sorted( list(item["prefix_lists"].values()), key=lambda k: k["name"], ) for pl in item["prefix_lists"]: if "entries" in pl: pl["entries"] = sorted( list(pl["entries"].values()), key=lambda k: k["sequence"], ) ansible_facts["ansible_network_resources"].pop("prefix_lists", None) params = utils.remove_empties( prefix_lists_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), ) if params.get("config"): facts["prefix_lists"] = params["config"] else: facts["prefix_lists"] = [] ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py b/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py index cfae26e3..d4084fd7 100644 --- a/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py +++ b/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py @@ -1,96 +1,95 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos route_maps 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 copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.route_maps.route_maps import ( Route_mapsArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps import ( Route_mapsTemplate, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps_14 import ( Route_mapsTemplate14, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version class Route_mapsFacts(object): """The vyos route_maps facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Route_mapsArgs.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_config(self, connection): return connection.get("show configuration commands | grep route-map") def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Route_maps network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): route_maps_class = Route_mapsTemplate14 else: route_maps_class = Route_mapsTemplate if not data: data = self.get_config(connection) # parse native config using the Route_maps template route_maps_parser = route_maps_class(lines=data.splitlines()) if route_maps_parser.parse().get("route_maps"): objs = list(route_maps_parser.parse().get("route_maps").values()) for item in objs: if item.get("entries"): item["entries"] = list(item["entries"].values()) ansible_facts["ansible_network_resources"].pop("route_maps", None) # import epdb;epdb.serve() params = utils.remove_empties(utils.validate_config(self.argument_spec, {"config": objs})) if params.get("config"): facts["route_maps"] = params["config"] ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/snmp_server/snmp_server.py b/plugins/module_utils/network/vyos/facts/snmp_server/snmp_server.py index d961a0ff..d3ff02e5 100644 --- a/plugins/module_utils/network/vyos/facts/snmp_server/snmp_server.py +++ b/plugins/module_utils/network/vyos/facts/snmp_server/snmp_server.py @@ -1,113 +1,112 @@ # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos snmp_server fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.snmp_server.snmp_server import ( Snmp_serverArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.snmp_server import ( Snmp_serverTemplate, ) class Snmp_serverFacts(object): """The vyos snmp_server facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Snmp_serverArgs.argument_spec def get_config(self, connection): return connection.get("show configuration commands | grep snmp") def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Snmp_server network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] config_lines = [] if not data: data = self.get_config(connection) for resource in data.splitlines(): config_lines.append(re.sub("'", "", resource)) # parse native config using the Snmp_server template snmp_server_parser = Snmp_serverTemplate(lines=config_lines, module=self._module) objs = snmp_server_parser.parse() if objs: if "communities" in objs: for k in objs["communities"].values(): for param, val in k.items(): if param in ["clients", "networks"]: if None in val: val.remove(None) val.sort() objs["communities"] = sorted( list(objs["communities"].values()), key=lambda k, sk="name": k[sk], ) if "listen_addresses" in objs: objs["listen_addresses"] = sorted( list(objs["listen_addresses"].values()), key=lambda k, sk="address": k[sk], ) if "snmp_v3" in objs: if "groups" in objs["snmp_v3"]: objs["snmp_v3"]["groups"] = sorted( list(objs["snmp_v3"]["groups"].values()), key=lambda k, sk="group": k[sk], ) if "trap_targets" in objs["snmp_v3"]: objs["snmp_v3"]["trap_targets"] = sorted( list(objs["snmp_v3"]["trap_targets"].values()), key=lambda k, sk="address": k[sk], ) if "users" in objs["snmp_v3"]: objs["snmp_v3"]["users"] = sorted( list(objs["snmp_v3"]["users"].values()), key=lambda k, sk="user": k[sk], ) if "views" in objs["snmp_v3"]: objs["snmp_v3"]["views"] = sorted( list(objs["snmp_v3"]["views"].values()), key=lambda k, sk="view": k[sk], ) else: objs = {} ansible_facts["ansible_network_resources"].pop("snmp_server", None) params = utils.remove_empties( snmp_server_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), ) facts["snmp_server"] = params.get("config", {}) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py b/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py index 710d91fe..1bce772c 100644 --- a/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py +++ b/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py @@ -1,178 +1,177 @@ # # -*- 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 static_routes 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 copy import deepcopy from re import M, findall, search from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.static_routes.static_routes import ( Static_routesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( get_route_type, ) class Static_routesFacts(object): """The vyos static_routes fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Static_routesArgs.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 static_routes :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 = [] r_v4 = [] r_v6 = [] af = [] static_routes = findall(r"set protocols static route(6)? (\S+)", data, M) if static_routes: for route in set(static_routes): route_regex = r" %s .+$" % route[1] cfg = findall(route_regex, data, M) sr = self.render_config(cfg) sr["dest"] = route[1].strip("'") afi = self.get_afi(sr["dest"]) if afi == "ipv4": r_v4.append(sr) else: r_v6.append(sr) if r_v4: afi_v4 = {"afi": "ipv4", "routes": r_v4} af.append(afi_v4) if r_v6: afi_v6 = {"afi": "ipv6", "routes": r_v6} af.append(afi_v6) config = {"address_families": af} if config: objs.append(config) ansible_facts["ansible_network_resources"].pop("static_routes", None) facts = {} if objs: facts["static_routes"] = [] params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["static_routes"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ next_hops_conf = "\n".join(filter(lambda x: ("next-hop" in x or "interface" in x), conf)) blackhole_conf = "\n".join(filter(lambda x: ("blackhole" in x), conf)) routes_dict = { "blackhole_config": self.parse_blackhole(blackhole_conf), "next_hops": self.parse_next_hop(next_hops_conf), } return routes_dict def parse_blackhole(self, conf): blackhole = None if conf: distance = search(r"^.*blackhole distance (.\S+)", conf, M) bh = conf.find("blackhole") if distance is not None: blackhole = {} value = distance.group(1).strip("'") blackhole["distance"] = int(value) elif bh: blackhole = {} blackhole["type"] = "blackhole" return blackhole def get_afi(self, address): route_type = get_route_type(address) if route_type == "route": return "ipv4" elif route_type == "route6": return "ipv6" def parse_next_hop(self, conf): nh_list = None nh_info = {} if conf: nh_list = [] hop_list = [ match[0] if match[0] else match[1] for match in findall(r"^.*next-hop(.+)|(\s+interface.+)$", conf, M) ] if hop_list: for hop in hop_list: distance = search(r"^.*distance (.\S+)", hop, M) interface = search(r"^.*interface (.\S+)", hop, M) dis = hop.find("disable") hop_info = hop.split(" ") if interface: nh_info["interface"] = interface.group(1).strip("'") else: nh_info = {"forward_router_address": hop_info[1].strip("'")} if distance: value = distance.group(1).strip("'") nh_info["admin_distance"] = int(value) elif dis >= 1: nh_info["enabled"] = False for element in nh_list: if element.get("forward_router_address") == nh_info.get( "forward_router_address", ): if "admin_distance" in nh_info.keys(): element["admin_distance"] = nh_info["admin_distance"] if "enabled" in nh_info.keys(): element["enabled"] = nh_info["enabled"] nh_info = None if nh_info is not None: nh_list.append(nh_info) nh_info = {} return nh_list diff --git a/plugins/module_utils/network/vyos/facts/vrf/vrf.py b/plugins/module_utils/network/vyos/facts/vrf/vrf.py index 6c7b8918..74f46b45 100644 --- a/plugins/module_utils/network/vyos/facts/vrf/vrf.py +++ b/plugins/module_utils/network/vyos/facts/vrf/vrf.py @@ -1,227 +1,226 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos vrf fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.vrf.vrf import VrfArgs from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_global.bgp_global import ( Bgp_globalFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv2.ospfv2 import ( Ospfv2Facts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv3.ospfv3 import ( Ospfv3Facts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes import ( Static_routesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.vrf import ( VrfTemplate, ) class VrfFacts(object): """The vyos vrf facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = VrfArgs.argument_spec def get_config(self, connection): return connection.get("show configuration commands | match 'set vrf'") def get_config_set(self, data, connection): """To classify the configurations beased on vrf""" config_dict = {} for config_line in data.splitlines(): vrf_inst = re.search(r"set vrf name (\S+).*", config_line) vrf_bta = re.search(r"set vrf bind-to-all", config_line) if vrf_bta: config_dict["bind_to_all"] = config_dict.get("bind_to_all", "") + config_line + "\n" if vrf_inst: config_dict[vrf_inst.group(1)] = ( config_dict.get(vrf_inst.group(1), "") + config_line + "\n" ) return list(config_dict.values()) def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Vrf network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] if not data: data = self.get_config(connection) vrf_facts = {} instances = [] vrf_parser = VrfTemplate(lines=[], module=self._module) resources = self.get_config_set(data, connection) for resource in resources: vrf_parser = VrfTemplate( lines=resource.split("\n"), module=self._module, ) objs = vrf_parser.parse() if objs and "protocols" in resource: protocol_lines = [] for line in resource.strip().split("\n"): if "protocols" in line: idx = line.index("protocols") protocol_lines.append("set " + line[idx:]) objs["protocols"] = self._parse_protocols("\n".join(protocol_lines)) if objs: if "bind_to_all" in objs: vrf_facts.update(objs) if "name" in objs: instances.append(self._normalise_instance(objs)) if instances: vrf_facts.update({"instances": instances}) ansible_facts["ansible_network_resources"].pop("vrf_facts", None) facts = {"vrf": []} params = utils.remove_empties( vrf_parser.validate_config( self.argument_spec, {"config": vrf_facts}, redact=True, ), ) if not resources: params["config"].pop("bind_to_all", None) if params.get("config"): facts["vrf"] = params["config"] ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def _normalise_instance(self, instance): n_inst = instance.copy() af_map = {} for af in instance.get("address_family", []): afi = af.get("afi") if not afi: continue if afi not in af_map: af_map[afi] = {"afi": afi} for k, v in af.items(): if k == "afi": continue elif k == "route_maps": if "route_maps" not in af_map[afi]: af_map[afi]["route_maps"] = [] af_map[afi]["route_maps"].extend(v) else: af_map[afi][k] = v for afi_data in af_map.values(): if "route_maps" in afi_data: seen = [] deduped = [] for item in afi_data["route_maps"]: if item not in seen: seen.append(item) deduped.append(item) afi_data["route_maps"] = deduped n_inst["address_family"] = list(af_map.values()) return n_inst def _parse_protocols(self, protocols): """Parse protocols and return a dictionary""" protocol_chunks = {} parsed_protocols = {} for line in protocols.split("\n"): parts = line.split() if len(parts) > 2 and parts[0] == "set" and parts[1] == "protocols": protocol = parts[2] protocol_chunks.setdefault(protocol, []).append(line) protocol_strings = {proto: "\n".join(lines) for proto, lines in protocol_chunks.items()} for protocol_name, protocol_string in protocol_strings.items(): protocol_dict = {} if protocol_name == "bgp": bgp_module = Bgp_globalFacts(self._module) protocol_dict = bgp_module.populate_facts( connection=self._module._connection, ansible_facts={"ansible_network_resources": {}}, data=protocol_string, ) parsed_protocols[protocol_name] = list( protocol_dict.get("ansible_network_resources").values(), )[0] elif protocol_name == "ospf": ospf_module = Ospfv2Facts(self._module) protocol_dict = ospf_module.populate_facts( connection=self._module._connection, ansible_facts={"ansible_network_resources": {}}, data=protocol_string, ) parsed_protocols[protocol_name] = list( protocol_dict.get("ansible_network_resources").values(), )[0] elif protocol_name == "ospfv3": ospfv3_module = Ospfv3Facts(self._module) protocol_dict = ospfv3_module.populate_facts( connection=self._module._connection, ansible_facts={"ansible_network_resources": {}}, data=protocol_string, ) parsed_protocols[protocol_name] = list( protocol_dict.get("ansible_network_resources").values(), )[0] elif protocol_name == "static": static_routes_module = Static_routesFacts(self._module) protocol_dict = static_routes_module.populate_facts( connection=self._module._connection, ansible_facts={"ansible_network_resources": {}}, data=protocol_string, ) parsed_protocols[protocol_name] = list( protocol_dict.get("ansible_network_resources").values(), )[0] else: self._module.fail_json(msg="The protocol is not supported" + protocol_name) return parsed_protocols diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py index fccfc9c2..fe7889aa 100644 --- a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py +++ b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py @@ -1,1435 +1,1434 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The Bgp_address_family parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) def _tmplt_bgp_af_aggregate_address(config_data): afi = config_data["address_family"]["afi"] + "-unicast" command = "protocols bgp {as_number} address-family ".format(**config_data) config_data = config_data["address_family"] if config_data["aggregate_address"].get("as_set"): command += afi + " aggregate-address {prefix} as-set".format( **config_data["aggregate_address"], ) if config_data["aggregate_address"].get("summary_only"): command += afi + " aggregate-address {prefix} summary-only".format( **config_data["aggregate_address"], ) return command def _tmplt_bgp_af_redistribute(config_data): afi = config_data["address_family"]["afi"] + "-unicast" command = "protocols bgp {as_number} address-family ".format(**config_data) config_data = config_data["address_family"]["redistribute"] command += afi + " redistribute {protocol}".format(**config_data) if config_data.get("metric"): command += " metric {metric}".format(**config_data) elif config_data.get("route_map"): command += " route-map {route_map}".format(**config_data) elif config_data.get("table"): command += " table {table}".format(**config_data) return command def _tmplt_bgp_af_redistribute_delete(config_data): afi = config_data["address_family"]["afi"] + "-unicast" command = "protocols bgp {as_number} address-family ".format(**config_data) config_data = config_data["address_family"]["redistribute"] command += afi + " redistribute {protocol}".format(**config_data) if config_data.get("metric"): command += " metric" elif config_data.get("route_map"): command += " route-map" elif config_data.get("table"): command += " table" return command def _tmplt_bgp_af_neighbor_distribute_list(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp {as_number} neighbor ".format(**config_data) cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for list_el in config_data["distribute_list"]: command.append( cmd + afi + " distribute-list " + list_el["action"] + " " + str(list_el["acl"]), ) return command def _tmplt_bgp_af_neighbor_route_map(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp {as_number} neighbor ".format(**config_data) cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for list_el in config_data["route_map"]: command.append( cmd + afi + " route-map " + list_el["action"] + " " + str(list_el["route_map"]), ) return command def _tmplt_bgp_af_neighbor_prefix_list(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp {as_number} neighbor ".format(**config_data) cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for list_el in config_data["prefix_list"]: command.append( cmd + afi + " prefix-list " + list_el["action"] + " " + str(list_el["prefix_list"]), ) return command def _tmplt_bgp_af_neighbor_filter_list(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp {as_number} neighbor ".format(**config_data) cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for list_el in config_data["filter_list"]: command.append( cmd + afi + " filter-list " + list_el["action"] + " " + str(list_el["path_list"]), ) return command def _tmplt_bgp_af_neighbor_attribute(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp {as_number} neighbor ".format(**config_data) cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for k in config_data["attribute_unchanged"].keys(): if config_data["attribute_unchanged"][k]: k = re.sub("_", "-", k) c = cmd + afi + " attribute-unchanged " + k command.append(c) return command def _tmplt_bgp_af_neighbor_delete(config_data): afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" command = "protocols bgp {as_number} ".format(**config_data) command += ( "neighbor {neighbor_address} address-family ".format(**config_data["neighbors"]) + afi ) config_data = config_data["neighbors"]["address_family"] if config_data.get("allowas_in"): command += " allowas-in" elif config_data.get("as_override"): command += " as-override" elif config_data.get("attribute_unchanged"): command += " attribute-unchanged" elif config_data.get("capability"): command += " capability" elif config_data.get("default_originate"): command += " default-originate" elif config_data.get("maximum_prefix"): command += " maximum-prefix" elif config_data.get("nexthop_local"): command += " nexthop-local" elif config_data.get("nexthop_self"): command += " nexthop-self" elif config_data.get("peer_group"): command += " peer-group" elif config_data.get("remote_private_as"): command += " remote-private-as" elif config_data.get("route_reflector_client"): command += " route-reflector-client" elif config_data.get("route_server_client"): command += " route-server-client" elif config_data.get("soft_reconfiguration"): command += " soft-reconfiguration" elif config_data.get("unsuppress_map"): command += " unsuppress-map" elif config_data.get("weight"): command += " weight" elif config_data.get("filter_list"): command += " filter-list" elif config_data.get("prefix_list"): command += " prefix-list" elif config_data.get("distribute_list"): command += " distribute-list" elif config_data.get("route_map"): command += " route-map" return command def _tmplt_bgp_af_neighbor(config_data): afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" command = "protocols bgp {as_number} ".format(**config_data) command += ( "neighbor {neighbor_address} address-family ".format(**config_data["neighbors"]) + afi ) config_data = config_data["neighbors"]["address_family"] if config_data.get("allowas_in"): command += " allowas-in number {allowas_in}".format(**config_data) elif config_data.get("as_override"): command += " as-override" elif config_data.get("capability"): command += " capability " if config_data["capability"].get("dynamic"): command += "dynamic" elif config_data["capability"].get("orf"): command += " prefix-list {orf}".format(**config_data["capability"]) elif config_data.get("default_originate"): command += " default-originate route-map {default_originate}".format(**config_data) elif config_data.get("maximum_prefix"): command += " maximum-prefix {maximum_prefix}".format(**config_data) elif config_data.get("nexthop_local"): command += " nexthop-local" elif config_data.get("nexthop_self"): command += " nexthop-self" elif config_data.get("peer_group"): command += " peer-group {peer_group}".format(**config_data) elif config_data.get("remote_private_as"): command += " remote-private-as" elif config_data.get("route_reflector_client"): command += " route-reflector-client" elif config_data.get("route_server_client"): command += " route-server-client" elif config_data.get("soft_reconfiguration"): command += " soft-reconfiguration inbound" elif config_data.get("unsuppress_map"): command += " unsuppress-map {unsuppress_map}".format(**config_data) elif config_data.get("weight"): command += " weight {weight}".format(**config_data) return command def _tmplt_bgp_af_network(config_data): afi = config_data["address_family"]["afi"] + "-unicast" command = "protocols bgp {as_number} address-family ".format(**config_data) config_data = config_data["address_family"]["networks"] command += afi + " network {prefix}".format(**config_data) if config_data.get("backdoor"): command += " backdoor" elif config_data.get("route_map"): command += " route-map {route_map}".format(**config_data) return command def _tmplt_bgp_af_network_delete(config_data): afi = config_data["address_family"]["afi"] + "-unicast" command = "protocols bgp {as_number} address-family ".format(**config_data) config_data = config_data["address_family"]["networks"] command += afi + " network {prefix}".format(**config_data) if config_data.get("backdoor"): command += " backdoor" elif config_data.get("route_map"): command += " route_map" return command class Bgp_address_familyTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Bgp_address_familyTemplate, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module, ) # fmt: off PARSERS = [ { "name": "address_family", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast", "compval": "as_number", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", }, }, }, }, { "name": "aggregate_address", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+aggregate-address \s+(?P
\S+) \s*(?Pas-set)? \s*(?Psummary-only)? $""", re.VERBOSE, ), "setval": _tmplt_bgp_af_aggregate_address, "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast aggregate-address" + " {{ address_family.aggregate_address.prefix }}", "compval": "address_family.aggregate_address", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "aggregate_address": [ { "prefix": "{{ address }}", "as_set": "{{ True if as_set is defined }}", "summary_only": "{{ True if summary_only is defined }}", }, ], }, }, }, }, { "name": "network", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+network \s+(?P
\S+) $""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network {{ address_family.networks.prefix }}", "compval": "address_family.networks.prefix", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "networks": [ { "prefix": "{{ address }}", }, ], }, }, }, }, { "name": "network.backdoor", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+network \s+(?P
\S+) \s+backdoor *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, "remval": _tmplt_bgp_af_network_delete, "compval": "address_family.networks.backdoor", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "networks": [ { "prefix": "{{ address }}", "backdoor": "{{ True }}", }, ], }, }, }, }, { "name": "network.path_limit", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+network \s+(?P
\S+) \s+path-limit \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, "remval": _tmplt_bgp_af_network_delete, "compval": "address_family.networks.path_limit", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "networks": [ { "prefix": "{{ address }}", "path_limit": "{{ limit|int }}", }, ], }, }, }, }, { "name": "network.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+network \s+(?P
\S+) \s+route-map \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, "remval": _tmplt_bgp_af_network_delete, "compval": "address_family.networks.route_map", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "networks": [ { "prefix": "{{ address }}", "route_map": "{{ map }}", }, ], }, }, }, }, { "name": "redistribute", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+redistribute \s+(?P\S+) $""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast redistribute {{ address_family.redistribute.protocol }}", "compval": "address_family.redistribute.protocol", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "redistribute": [ { "protocol": "{{ proto }}", }, ], }, }, }, }, { "name": "redistribute.metric", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+redistribute \s+(?P\S+) \s+metric \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, "remval": _tmplt_bgp_af_redistribute_delete, "compval": "address_family.redistribute.metric", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "redistribute": [ { "protocol": "{{ proto }}", "metric": "{{ val|int }}", }, ], }, }, }, }, { "name": "redistribute.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+redistribute \s+(?P\S+) \s+route-map \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, "remval": _tmplt_bgp_af_redistribute_delete, "compval": "address_family.redistribute.route_map", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "redistribute": [ { "protocol": "{{ proto }}", "route_map": "{{ map }}", }, ], }, }, }, }, { "name": "redistribute.table", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+redistribute \s+table \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, "remval": _tmplt_bgp_af_redistribute_delete, "compval": "address_family.redistribute.table", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "redistribute": [ { "table": "{{ tab }}", }, ], }, }, }, }, { "name": "neighbors", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbors.neighbor_address }} address-family", "compval": "neighbors", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", }, }, }, }, { "name": "neighbors.address_family", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbors.neighbor_address }} address-family {{ neighbors.address_family.afi }}-unicast", "compval": "neighbors", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", }, }, }, }, }, }, { "name": "neighbors.allowas_in", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+allowas-in \s+number \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.allowas_in", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "allowas_in": "{{ num }}", }, }, }, }, }, }, { "name": "neighbors.as_override", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+as-override *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.as_override", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "as_override": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.attribute_unchanged.as_path", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+attribute-unchanged \s+(?Pas-path) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_attribute, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.attribute_unchanged.as_path", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "attribute_unchanged": { "as_path": "{{ True }}", }, }, }, }, }, }, }, { "name": "neighbors.attribute_unchanged.med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+attribute-unchanged \s+(?Pmed) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_attribute, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.attribute_unchanged.med", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "attribute_unchanged": { "med": "{{ True }}", }, }, }, }, }, }, }, { "name": "neighbors.attribute_unchanged.next_hop", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+attribute-unchanged \s+(?Pnext-hop) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_attribute, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.attribute_unchanged.next_hop", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "attribute_unchanged": { "next_hop": "{{ True }}", }, }, }, }, }, }, }, { "name": "neighbors.capability_dynamic", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+capability \s+dynamic *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.capability.dynamic", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "capability": { "dynamic": "{{ true }}", }, }, }, }, }, }, }, { "name": "neighbors.capability_orf", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+capability \s+prefix-list \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.capability.orf", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "capability": { "orf": "{{ orf }}", }, }, }, }, }, }, }, { "name": "neighbors.default_originate", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+default-originate \s+route-map \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.default_originate", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "default_originate": "{{ map }}", }, }, }, }, }, }, { "name": "neighbors.distribute_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+distribute-list \s+(?Pexport|import) \s+(?P\d+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_distribute_list, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.distribute_list", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "distribute_list": [ { "action": "{{ action }}", "acl": "{{ list }}", }, ], }, }, }, }, }, }, { "name": "neighbors.prefix_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+prefix-list \s+(?Pexport|import) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_prefix_list, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.prefix_list", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "prefix_list": [ { "action": "{{ action }}", "prefix_list": "{{ list }}", }, ], }, }, }, }, }, }, { "name": "neighbors.filter_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+filter-list \s+(?Pexport|import) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_filter_list, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.filter_list", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "filter_list": [ { "action": "{{ action }}", "path_list": "{{ list }}", }, ], }, }, }, }, }, }, { "name": "neighbors.maximum_prefix", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+maximum-prefix \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.maximum_prefix", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "maximum_prefix": "{{ num }}", }, }, }, }, }, }, { "name": "neighbors.nexthop_local", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+nexthop-local *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.nexthop_local", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "nexthop_local": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.nexthop_self", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+nexthop-self *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.nexthop_self", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "nexthop_self": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.peer_group", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+peer-group \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.peer_group", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "peer_group": "{{ name }}", }, }, }, }, }, }, { "name": "neighbors.remove_private_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+remove-private-as *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.remove_private_as", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "remove_private_as": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+route-map \s+(?Pexport|import) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_route_map, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.route_map", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "route_map": [ { "action": "{{ action }}", "route_map": "{{ map }}", }, ], }, }, }, }, }, }, { "name": "neighbors.route_reflector_client", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+route-reflector-client *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.route_reflector_client", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "route_reflector_client": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.route_server_client", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+route-server-client *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.route_server_client", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "route_server_client": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.soft_reconfiguration", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+soft-reconfiguration \s+inbound *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.soft_reconfiguration", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "soft_reconfiguration": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.unsuppress_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+unsuppress-map \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.unsuppress_map", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "unsuppress_map": "{{ map }}", }, }, }, }, }, }, { "name": "neighbors.weight", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+weight \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.weight", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "weight": "{{ num }}", }, }, }, }, }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family_14.py b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family_14.py index 09cd41a7..9936cf6d 100644 --- a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family_14.py +++ b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family_14.py @@ -1,1418 +1,1417 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The Bgp_address_family parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) def _tmplt_bgp_af_aggregate_address(config_data): afi = config_data["address_family"]["afi"] + "-unicast" command = "protocols bgp address-family ".format(**config_data) config_data = config_data["address_family"] if config_data["aggregate_address"].get("as_set"): command += afi + " aggregate-address {prefix} as-set".format( **config_data["aggregate_address"], ) if config_data["aggregate_address"].get("summary_only"): command += afi + " aggregate-address {prefix} summary-only".format( **config_data["aggregate_address"], ) return command def _tmplt_bgp_af_redistribute(config_data): afi = config_data["address_family"]["afi"] + "-unicast" command = "protocols bgp address-family ".format(**config_data) config_data = config_data["address_family"]["redistribute"] command += afi + " redistribute {protocol}".format(**config_data) if config_data.get("metric"): command += " metric {metric}".format(**config_data) elif config_data.get("route_map"): command += " route-map {route_map}".format(**config_data) elif config_data.get("table"): command += " table {table}".format(**config_data) return command def _tmplt_bgp_af_redistribute_delete(config_data): afi = config_data["address_family"]["afi"] + "-unicast" command = "protocols bgp address-family ".format(**config_data) config_data = config_data["address_family"]["redistribute"] command += afi + " redistribute {protocol}".format(**config_data) if config_data.get("metric"): command += " metric" elif config_data.get("route_map"): command += " route-map" elif config_data.get("table"): command += " table" return command def _tmplt_bgp_af_neighbor_distribute_list(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp neighbor ".format(**config_data) cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for list_el in config_data["distribute_list"]: command.append( cmd + afi + " distribute-list " + list_el["action"] + " " + str(list_el["acl"]), ) return command def _tmplt_bgp_af_neighbor_route_map(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp neighbor ".format(**config_data) cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for list_el in config_data["route_map"]: command.append( cmd + afi + " route-map " + list_el["action"] + " " + str(list_el["route_map"]), ) return command def _tmplt_bgp_af_neighbor_prefix_list(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp neighbor ".format(**config_data) cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for list_el in config_data["prefix_list"]: command.append( cmd + afi + " prefix-list " + list_el["action"] + " " + str(list_el["prefix_list"]), ) return command def _tmplt_bgp_af_neighbor_filter_list(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp neighbor ".format(**config_data) cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for list_el in config_data["filter_list"]: command.append( cmd + afi + " filter-list " + list_el["action"] + " " + str(list_el["path_list"]), ) return command def _tmplt_bgp_af_neighbor_attribute(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp neighbor ".format(**config_data) cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for k in config_data["attribute_unchanged"].keys(): if config_data["attribute_unchanged"][k]: k = re.sub("_", "-", k) c = cmd + afi + " attribute-unchanged " + k command.append(c) return command def _tmplt_bgp_af_neighbor_delete(config_data): afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" command = "protocols bgp ".format(**config_data) command += ( "neighbor {neighbor_address} address-family ".format(**config_data["neighbors"]) + afi ) config_data = config_data["neighbors"]["address_family"] if config_data.get("allowas_in"): command += " allowas-in" elif config_data.get("as_override"): command += " as-override" elif config_data.get("attribute_unchanged"): command += " attribute-unchanged" elif config_data.get("capability"): command += " capability" elif config_data.get("default_originate"): command += " default-originate" elif config_data.get("maximum_prefix"): command += " maximum-prefix" elif config_data.get("nexthop_local"): command += " nexthop-local" elif config_data.get("nexthop_self"): command += " nexthop-self" elif config_data.get("peer_group"): command += " peer-group" elif config_data.get("remote_private_as"): command += " remote-private-as" elif config_data.get("route_reflector_client"): command += " route-reflector-client" elif config_data.get("route_server_client"): command += " route-server-client" elif config_data.get("soft_reconfiguration"): command += " soft-reconfiguration" elif config_data.get("unsuppress_map"): command += " unsuppress-map" elif config_data.get("weight"): command += " weight" elif config_data.get("filter_list"): command += " filter-list" elif config_data.get("prefix_list"): command += " prefix-list" elif config_data.get("distribute_list"): command += " distribute-list" elif config_data.get("route_map"): command += " route-map" return command def _tmplt_bgp_af_neighbor(config_data): afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" command = "protocols bgp ".format(**config_data) command += ( "neighbor {neighbor_address} address-family ".format(**config_data["neighbors"]) + afi ) config_data = config_data["neighbors"]["address_family"] if config_data.get("allowas_in"): command += " allowas-in number {allowas_in}".format(**config_data) elif config_data.get("as_override"): command += " as-override" elif config_data.get("capability"): command += " capability " if config_data["capability"].get("dynamic"): command += "dynamic" elif config_data["capability"].get("orf"): command += " prefix-list {orf}".format(**config_data["capability"]) elif config_data.get("default_originate"): command += " default-originate route-map {default_originate}".format(**config_data) elif config_data.get("maximum_prefix"): command += " maximum-prefix {maximum_prefix}".format(**config_data) elif config_data.get("nexthop_local"): command += " nexthop-local" elif config_data.get("nexthop_self"): command += " nexthop-self" elif config_data.get("peer_group"): command += " peer-group {peer_group}".format(**config_data) elif config_data.get("remote_private_as"): command += " remote-private-as" elif config_data.get("route_reflector_client"): command += " route-reflector-client" elif config_data.get("route_server_client"): command += " route-server-client" elif config_data.get("soft_reconfiguration"): command += " soft-reconfiguration inbound" elif config_data.get("unsuppress_map"): command += " unsuppress-map {unsuppress_map}".format(**config_data) elif config_data.get("weight"): command += " weight {weight}".format(**config_data) return command def _tmplt_bgp_af_network(config_data): afi = config_data["address_family"]["afi"] + "-unicast" command = "protocols bgp address-family ".format(**config_data) config_data = config_data["address_family"]["networks"] command += afi + " network {prefix}".format(**config_data) if config_data.get("backdoor"): command += " backdoor" elif config_data.get("route_map"): command += " route-map {route_map}".format(**config_data) return command def _tmplt_bgp_af_network_delete(config_data): afi = config_data["address_family"]["afi"] + "-unicast" command = "protocols bgp address-family ".format(**config_data) config_data = config_data["address_family"]["networks"] command += afi + " network {prefix}".format(**config_data) if config_data.get("backdoor"): command += " backdoor" elif config_data.get("route_map"): command += " route_map" return command class Bgp_address_familyTemplate14(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Bgp_address_familyTemplate14, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module, ) # fmt: off PARSERS = [ { "name": "system_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+system-as \s+(?P\d+) *$""", re.VERBOSE, ), "setval": "protocols bgp system-as {{ as_number }}", "compval": "as_number", "result": { "as_number": "{{ as_num }}", }, }, { "name": "address_family", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+address-family \s+(?P\S+)-unicast *$""", re.VERBOSE, ), "setval": "protocols bgp address-family {{ address_family.afi }}-unicast", "compval": "as_number", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", }, }, }, }, { "name": "aggregate_address", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+address-family \s+(?P\S+)-unicast \s+aggregate-address \s+(?P
\S+) \s*(?Pas-set)? \s*(?Psummary-only)? $""", re.VERBOSE, ), "setval": _tmplt_bgp_af_aggregate_address, "remval": "protocols bgp address-family {{ address_family.afi }}-unicast aggregate-address" + " {{ address_family.aggregate_address.prefix }}", "compval": "address_family.aggregate_address", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "aggregate_address": [ { "prefix": "{{ address }}", "as_set": "{{ True if as_set is defined }}", "summary_only": "{{ True if summary_only is defined }}", }, ], }, }, }, }, { "name": "network", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+address-family \s+(?P\S+)-unicast \s+network \s+(?P
\S+) $""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, "remval": "protocols bgp address-family {{ address_family.afi }}-unicast network {{ address_family.networks.prefix }}", "compval": "address_family.networks.prefix", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "networks": [ { "prefix": "{{ address }}", }, ], }, }, }, }, { "name": "network.backdoor", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+address-family \s+(?P\S+)-unicast \s+network \s+(?P
\S+) \s+backdoor *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, "remval": _tmplt_bgp_af_network_delete, "compval": "address_family.networks.backdoor", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "networks": [ { "prefix": "{{ address }}", "backdoor": "{{ True }}", }, ], }, }, }, }, { "name": "network.path_limit", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+address-family \s+(?P\S+)-unicast \s+network \s+(?P
\S+) \s+path-limit \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, "remval": _tmplt_bgp_af_network_delete, "compval": "address_family.networks.path_limit", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "networks": [ { "prefix": "{{ address }}", "path_limit": "{{ limit|int }}", }, ], }, }, }, }, { "name": "network.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+address-family \s+(?P\S+)-unicast \s+network \s+(?P
\S+) \s+route-map \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, "remval": _tmplt_bgp_af_network_delete, "compval": "address_family.networks.route_map", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "networks": [ { "prefix": "{{ address }}", "route_map": "{{ map }}", }, ], }, }, }, }, { "name": "redistribute", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+address-family \s+(?P\S+)-unicast \s+redistribute \s+(?P\S+) $""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, "remval": "protocols bgp address-family {{ address_family.afi }}-unicast redistribute {{ address_family.redistribute.protocol }}", "compval": "address_family.redistribute.protocol", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "redistribute": [ { "protocol": "{{ proto }}", }, ], }, }, }, }, { "name": "redistribute.metric", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+address-family \s+(?P\S+)-unicast \s+redistribute \s+(?P\S+) \s+metric\s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, "remval": _tmplt_bgp_af_redistribute_delete, "compval": "address_family.redistribute.metric", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "redistribute": [ { "protocol": "{{ proto }}", "metric": "{{ val }}", }, ], }, }, }, }, { "name": "redistribute.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+address-family \s+(?P\S+)-unicast \s+redistribute \s+(?P\S+) \s+route-map \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, "remval": _tmplt_bgp_af_redistribute_delete, "compval": "address_family.redistribute.route_map", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "redistribute": [ { "protocol": "{{ proto }}", "route_map": "{{ map }}", }, ], }, }, }, }, { "name": "redistribute.table", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+address-family \s+(?P\S+)-unicast \s+redistribute \s+table \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, "remval": _tmplt_bgp_af_redistribute_delete, "compval": "address_family.redistribute.table", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "redistribute": [ { "table": "{{ tab }}", }, ], }, }, }, }, { "name": "neighbors", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbors.neighbor_address }} address-family", "compval": "neighbors", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", }, }, }, }, { "name": "neighbors.address_family", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbors.neighbor_address }} address-family {{ neighbors.address_family.afi }}-unicast", "compval": "neighbors", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", }, }, }, }, }, }, { "name": "neighbors.allowas_in", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+allowas-in \s+number \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.allowas_in", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "allowas_in": "{{ num }}", }, }, }, }, }, }, { "name": "neighbors.as_override", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+as-override *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.as_override", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "as_override": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.attribute_unchanged.as_path", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+attribute-unchanged \s+(?Pas-path) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_attribute, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.attribute_unchanged.as_path", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "attribute_unchanged": { "as_path": "{{ True }}", }, }, }, }, }, }, }, { "name": "neighbors.attribute_unchanged.med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+attribute-unchanged \s+(?Pmed) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_attribute, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.attribute_unchanged.med", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "attribute_unchanged": { "med": "{{ True }}", }, }, }, }, }, }, }, { "name": "neighbors.attribute_unchanged.next_hop", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+attribute-unchanged \s+(?Pnext-hop) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_attribute, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.attribute_unchanged.next_hop", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "attribute_unchanged": { "next_hop": "{{ True }}", }, }, }, }, }, }, }, { "name": "neighbors.capability_dynamic", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+capability \s+dynamic *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.capability.dynamic", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "capability": { "dynamic": "{{ true }}", }, }, }, }, }, }, }, { "name": "neighbors.capability_orf", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+capability \s+prefix-list \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.capability.orf", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "capability": { "orf": "{{ orf }}", }, }, }, }, }, }, }, { "name": "neighbors.default_originate", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+default-originate \s+route-map \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.default_originate", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "default_originate": "{{ map }}", }, }, }, }, }, }, { "name": "neighbors.distribute_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+distribute-list \s+(?Pexport|import) \s+(?P\d+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_distribute_list, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.distribute_list", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "distribute_list": [ { "action": "{{ action }}", "acl": "{{ list }}", }, ], }, }, }, }, }, }, { "name": "neighbors.prefix_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+prefix-list \s+(?Pexport|import) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_prefix_list, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.prefix_list", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "prefix_list": [ { "action": "{{ action }}", "prefix_list": "{{ list }}", }, ], }, }, }, }, }, }, { "name": "neighbors.filter_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+filter-list \s+(?Pexport|import) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_filter_list, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.filter_list", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "filter_list": [ { "action": "{{ action }}", "path_list": "{{ list }}", }, ], }, }, }, }, }, }, { "name": "neighbors.maximum_prefix", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+maximum-prefix \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.maximum_prefix", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "maximum_prefix": "{{ num }}", }, }, }, }, }, }, { "name": "neighbors.nexthop_local", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+nexthop-local *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.nexthop_local", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "nexthop_local": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.nexthop_self", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+nexthop-self *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.nexthop_self", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "nexthop_self": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.peer_group", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+peer-group \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.peer_group", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "peer_group": "{{ name }}", }, }, }, }, }, }, { "name": "neighbors.remove_private_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+remove-private-as *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.remove_private_as", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "remove_private_as": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+route-map \s+(?Pexport|import) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_route_map, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.route_map", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "route_map": [ { "action": "{{ action }}", "route_map": "{{ map }}", }, ], }, }, }, }, }, }, { "name": "neighbors.route_reflector_client", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+route-reflector-client *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.route_reflector_client", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "route_reflector_client": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.route_server_client", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+route-server-client *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.route_server_client", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "route_server_client": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.soft_reconfiguration", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+soft-reconfiguration \s+inbound *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.soft_reconfiguration", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "soft_reconfiguration": "{{ True }}", }, }, }, }, }, }, { "name": "neighbors.unsuppress_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+unsuppress-map \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.unsuppress_map", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "unsuppress_map": "{{ map }}", }, }, }, }, }, }, { "name": "neighbors.weight", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+weight \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.weight", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "weight": "{{ num }}", }, }, }, }, }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_global.py b/plugins/module_utils/network/vyos/rm_templates/bgp_global.py index 03f6265b..730fa5ee 100644 --- a/plugins/module_utils/network/vyos/rm_templates/bgp_global.py +++ b/plugins/module_utils/network/vyos/rm_templates/bgp_global.py @@ -1,1828 +1,1827 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The Bgp_global parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) def _tmplt_bgp_params_confederation(config_data): command = [] for list_el in config_data["bgp_params"]["confederation"]: for k, v in list_el.items(): command.append( "protocols bgp {as_number} parameters confederation ".format(**config_data) + k + " " + str(v), ) return command def _tmplt_bgp_maximum_paths(config_data): command = [] for list_el in config_data["maximum_paths"]: command.append( "protocols bgp {as_number} maximum-paths ".format(**config_data) + list_el["path"] + " " + str(list_el["count"]), ) return command def _tmplt_delete_bgp_maximum_paths(config_data): command = "protocols bgp {as_number} maximum-paths".format(**config_data) return command def _tmplt_bgp_params_default(config_data): command = "protocols bgp {as_number} parameters default".format(**config_data) if config_data["bgp_params"]["default"].get("no_ipv4_unicast"): command += " no-ipv4-unicast" if config_data["bgp_params"]["default"].get("local_pref"): command += " local-pref {local_pref}".format(**config_data["bgp_params"]["default"]) return command def _tmplt_bgp_neighbor_timers(config_data): command = [] for k, v in config_data["neighbor"]["timers"].items(): command.append( "protocols bgp {as_number} neighbor ".format(**config_data) + config_data["neighbor"]["address"] + " timers " + k + " " + str(v), ) return command def _tmplt_bgp_timers(config_data): command = [] for k, v in config_data["timers"].items(): command.append( "protocols bgp {as_number} ".format(**config_data) + "timers " + k + " " + str(v), ) return command def _tmplt_bgp_neighbor_attribute_unchanged_as_path(config_data): command = "protocols bgp {as_number} ".format( **config_data, ) + "neighbor {address} attribute-unchanged as-path".format(**config_data["neighbor"]) return command def _tmplt_bgp_neighbor_attribute_unchanged_med(config_data): command = "protocols bgp {as_number} ".format( **config_data, ) + "neighbor {address} attribute-unchanged med".format(**config_data["neighbor"]) return command def _tmplt_bgp_neighbor_attribute_unchanged_next_hop(config_data): command = "protocols bgp {as_number} ".format( **config_data, ) + "neighbor {address} attribute-unchanged next-hop".format(**config_data["neighbor"]) return command def _tmplt_bgp_neighbor_distribute_list(config_data): command = [] for list_el in config_data["neighbor"]["distribute_list"]: command.append( "protocols bgp {as_number} ".format(**config_data) + "neighbor {address} distribute-list ".format(**config_data["neighbor"]) + list_el["action"] + " " + str(list_el["acl"]), ) return command def _tmplt_bgp_neighbor_route_map(config_data): command = [] for list_el in config_data["neighbor"]["route_map"]: command.append( "protocols bgp {as_number} ".format(**config_data) + "neighbor {address} route-map ".format(**config_data["neighbor"]) + list_el["action"] + " " + str(list_el["route_map"]), ) return command def _tmplt_bgp_neighbor_prefix_list(config_data): command = [] for list_el in config_data["neighbor"]["prefix_list"]: command.append( "protocols bgp {as_number} ".format(**config_data) + "neighbor {address} prefix-list ".format(**config_data["neighbor"]) + list_el["action"] + " " + str(list_el["prefix_list"]), ) return command def _tmplt_bgp_neighbor_filter_list(config_data): command = [] for list_el in config_data["neighbor"]["filter_list"]: command.append( "protocols bgp {as_number} ".format(**config_data) + "neighbor {address} filter-list ".format(**config_data["neighbor"]) + list_el["action"] + " " + str(list_el["path_list"]), ) return command def _tmplt_bgp_params_distance(config_data): command = ( "protocols bgp {as_number} parameters distance global ".format(**config_data) + config_data["bgp_params"]["distance"]["type"] + " " + str(config_data["bgp_params"]["distance"]["value"]) ) return command class Bgp_globalTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Bgp_globalTemplate, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module, ) # fmt: off PARSERS = [ { "name": "router", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }}", "compval": "as_number", "result": { "as_number": "{{ as_num }}", }, }, { "name": "maximum_paths", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+maximum-paths \s+(?Pebgp|ibgp) \s+(?P\d+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_maximum_paths, "remval": _tmplt_delete_bgp_maximum_paths, "compval": "maximum_paths", "result": { "as_number": "{{ as_num }}", "maximum_paths": [ { "path": "{{ path }}", "count": "{{ count }}", }, ], }, }, { "name": "neighbor.advertisement_interval", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+advertisement-interval \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} advertisement-interval {{ neighbor.advertisement_interval }}", "remval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} advertisement-interval", "compval": "neighbor.advertisement_interval", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "advertisement_interval": "{{ interval }}", }, }, }, }, { "name": "neighbor.allowas_in", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+allowas-in \s+number \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} allowas-in number {{ neighbor.allowas_in }}", "compval": "neighbor.allowas_in", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "allowas_in": "{{ count }}", }, }, }, }, { "name": "neighbor.as_override", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+as-override *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} as-override", "compval": "neighbor.as_override", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "as_override": "{{ True }}", }, }, }, }, { "name": "neighbor.attribute_unchanged.as_path", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+attribute-unchanged \s+(?Pas-path) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_attribute_unchanged_as_path, "compval": "neighbor.attribute_unchanged", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "attribute_unchanged": { "{{ 'as_path' }}": "{{ True }}", }, }, }, }, }, { "name": "neighbor.attribute_unchanged.med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+attribute-unchanged \s+(?Pmed) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_attribute_unchanged_med, "compval": "neighbor.attribute_unchanged", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "attribute_unchanged": { "{{ 'med' }}": "{{ True }}", }, }, }, }, }, { "name": "neighbor.attribute_unchanged.next_hop", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+attribute-unchanged \s+(?Pnext-hop) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_attribute_unchanged_next_hop, "compval": "neighbor.attribute_unchanged", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "attribute_unchanged": { "{{ 'next_hop' }}": "{{ True }}", }, }, }, }, }, { "name": "neighbor.capability_dynamic", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+capability \s+(?Pdynamic) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} capability dynamic", "compval": "neighbor.capability.dynamic", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "capability": { "dynamic": "{{ True if dynamic is defined}}", }, }, }, }, }, { "name": "neighbor.capability_orf", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+capability \s+orf \s+prefix-list \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} capability orf prefix-list {{ neighbor.capability.orf }}", "compval": "neighbor.capability.orf", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "capability": { "orf": "{{ orf }}", }, }, }, }, }, { "name": "neighbor.default_originate", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+default-originate \s+route-map \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} default-originate route-map {{ neighbor.default_originate }}", "compval": "neighbor.advertisement_interval", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "default_originate": "{{ map }}", }, }, }, }, { "name": "neighbor.description", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+description \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} description {{ neighbor.description }}", "compval": "neighbor.description", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "description": "{{ desc }}", }, }, }, }, { "name": "neighbor.disable_capability_negotiation", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+disable-capability-negotiation *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} disable-capability-negotiation", "compval": "neighbor.disable_capability_negotiation", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "disable_capability_negotiation": "{{ True }}", }, }, }, }, { "name": "neighbor.disable_connected_check", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+disable-connected-check *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} disable-connected-check", "compval": "neighbor.disable_connected_check", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "disable_connected_check": "{{ True }}", }, }, }, }, { "name": "neighbor.disable_send_community", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+disable-send-community \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} disable-send-community {{ neighbor.disable_send_community }}", "compval": "neighbor.disable_send_community", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "disable_send_community": "{{ comm }}", }, }, }, }, { "name": "neighbor.distribute_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+distribute-list \s+(?Pexport|import) \s+(?P\d+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_distribute_list, "compval": "neighbor.distribute_list", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "distribute_list": [ { "action": "{{ action }}", "acl": "{{ list }}", }, ], }, }, }, }, { "name": "neighbor.ebgp_multihop", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+ebgp-multihop \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} ebgp-multihop {{ neighbor.ebgp_multihop }}", "compval": "neighbor.ebgp_multihop", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "ebgp_multihop": "{{ hop|int }}", }, }, }, }, { "name": "neighbor.filter_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+filter-list \s+(?Pexport|import) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_filter_list, "compval": "neighbor.filter_list", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "filter_list": [ { "action": "{{ action }}", "path_list": "{{ list }}", }, ], }, }, }, }, { "name": "neighbor.local_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+local-as \s+(?P\S+) \s+no-prepend *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} local-as {{ neighbor.local_as }} no-prepend", "compval": "neighbor.local_as", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "local_as": "{{ as }}", }, }, }, }, { "name": "neighbor.maximum_prefix", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+maximum-prefix \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} maximum-prefix {{ neighbor.maximum_prefix }}", "compval": "neighbor.maximum_prefix", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "maximum_prefix": "{{ num }}", }, }, }, }, { "name": "neighbor.nexthop_self", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+nexthop-self *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} nexthop-self", "compval": "neighbor.nexthop_self", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "nexthop_self": "{{ True }}", }, }, }, }, { "name": "neighbor.override_capability", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+override-capability *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} override-capability", "compval": "neighbor.override_capability", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "override_capability": "{{ True }}", }, }, }, }, { "name": "neighbor.passive", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+passive *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} passive", "compval": "neighbor.passive", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "passive": "{{ True }}", }, }, }, }, { "name": "neighbor.password", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+password \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} password {{ neighbor.password }}", "compval": "neighbor.password", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "password": "{{ pwd }}", }, }, }, }, { "name": "neighbor.peer_group_name", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+peer-group \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} peer-group {{ neighbor.peer_group_name }}", "compval": "neighbor.peer_group_name", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "peer_group_name": "{{ name }}", }, }, }, }, { "name": "neighbor.port", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+port \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} port {{ neighbor.port }}", "compval": "neighbor.port", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "port": "{{ num|int }}", }, }, }, }, { "name": "neighbor.prefix_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+prefix-list \s+(?Pexport|import) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_prefix_list, "compval": "neighbor.prefix_list", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "prefix_list": [ { "action": "{{ action }}", "prefix_list": "{{ list }}", }, ], }, }, }, }, { "name": "neighbor.remote_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+remote-as \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} remote-as {{ neighbor.remote_as }}", "compval": "neighbor.remote_as", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "remote_as": "{{ num|int }}", }, }, }, }, { "name": "neighbor.remove_private_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+remote-private-as *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} remote-private-as", "compval": "neighbor.remove_private_as", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "remove_private_as": "{{ True }}", }, }, }, }, { "name": "neighbor.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+route-map \s+(?Pexport|import) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_route_map, "compval": "neighbor.route_map", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "route_map": [ { "action": "{{ action }}", "route_map": "{{ map }}", }, ], }, }, }, }, { "name": "neighbor.route_reflector_client", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+route-reflector-client *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} router-reflector-client", "compval": "neighbor.route_reflector_client", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "route_reflector_client": "{{ True }}", }, }, }, }, { "name": "neighbor.route_server_client", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+route-server-client *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} route-server-client", "compval": "neighbor.route_server_client", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "route_server_client": "{{ True }}", }, }, }, }, { "name": "neighbor.shutdown", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+shutdown *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} shutdown", "compval": "neighbor.shutdown", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "shutdown": "{{ True }}", }, }, }, }, { "name": "neighbor.soft_reconfiguration", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+soft-reconfiguration \s+inbound *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} soft-reconfiguration", "compval": "neighbor.soft_reconfiguration", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "soft_reconfiguration": "{{ True }}", }, }, }, }, { "name": "neighbor.strict_capability_match", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+strict-capability-match *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} strict-capability-match", "compval": "neighbor.strict_capability_match", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "strict_capability_match": "{{ True }}", }, }, }, }, { "name": "neighbor.unsuppress_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+unsuppress-map \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} unsuppress-map {{ neighbor.unsuppress_map }}", "compval": "neighbor.unsuppress_map", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "unsuppress_map": "{{ map }}", }, }, }, }, { "name": "neighbor.update_source", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+update-source \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} update-source {{ neighbor.update_source }}", "compval": "neighbor.update_source", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "update_source": "{{ src }}", }, }, }, }, { "name": "neighbor.weight", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+weight \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} weight {{ neighbor.weight }}", "compval": "neighbor.weight", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "weight": "{{ num }}", }, }, }, }, { "name": "neighbor.ttl_security", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+ttl-security \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} ttl-security {{ neighbor.ttl_security }}", "compval": "neighbor.ttl_security", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "ttl_security": "{{ ttl|int }}", }, }, }, }, { "name": "neighbor.timers", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+timers \s+(?Pconnect|holdtime|keepalive) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_timers, "remval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} timers", "compval": "neighbor.timers", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "timers": { "{{ type }}": "{{ sec }}", }, }, }, }, }, { "name": "timers", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+timers \s+(?P\S+) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_timers, "remval": "protocols bgp {{ as_number }} timers", "compval": "timers", "result": { "as_number": "{{ as_num }}", "timers": { "{{ type }}": "{{ val }}", }, }, }, { "name": "bgp_params.always_compare_med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+always-compare-med *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters always-compare-med", "compval": "bgp_params.always_compare_med", "result": { "as_number": "{{ as_num }}", "bgp_params": { "always_compare_med": "{{ True }}", }, }, }, { "name": "bgp_params.bestpath.as_path", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+bestpath \s+as-path \s+(?Pconfed|ignore) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters bestpath as-path {{ bgp_params.bestpath.as_path }}", "compval": "bgp_params.bestpath.as_path", "result": { "as_number": "{{ as_num }}", "bgp_params": { "bestpath": { "as_path": "{{ path }}", }, }, }, }, { "name": "bgp_params.bestpath.compare_routerid", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+bestpath \s+compare-routerid *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters bestpath compare-routerid", "compval": "bgp_params.bestpath.compare_routerid", "result": { "as_number": "{{ as_num }}", "bgp_params": { "bestpath": { "compare_routerid": "{{ True }}", }, }, }, }, { "name": "bgp_params.bestpath.med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+bestpath \s+med \s+(?Pconfed|missing-as-worst) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters bestpath med {{ bestpath.med }}", "compval": "bgp_params.bestpath.med", "result": { "as_number": "{{ as_num }}", "bgp_params": { "bestpath": { "med": "{{ path }}", }, }, }, }, { "name": "bgp_params.cluster_id", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+cluster-id \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters cluster-id {{ bgp_params.cluster_id }}", "compval": "bgp_params.cluster_id", "result": { "as_number": "{{ as_num }}", "bgp_params": { "cluster_id": "{{ id }}", }, }, }, { "name": "bgp_params.confederation", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+confederation \s+(?Pidentifier|peers) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_confederation, "compval": "bgp_params.always_compare_med", "result": { "as_number": "{{ as_num }}", "bgp_params": { "confederation": [ { "peers": "{{ val if type == 'peers' }}", "identifier": "{{ val if type == 'identifier' }}", }, ], }, }, }, { "name": "bgp_params.dampening_half_life", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+dampening \s+half-life \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening half-life {{ bgp_params.dampening.half_life}}", "compval": "bgp_params.dampening.half_life", "result": { "as_number": "{{ as_num }}", "bgp_params": { "dampening": { "half_life": "{{ val }}", }, }, }, }, { "name": "bgp_params.dampening_max_suppress_time", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+dampening \s+max-suppress-time \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening max-suppress-time {{ bgp_params.dampening.max_suppress_time}}", "compval": "bgp_params.dampening.max_suppress_time", "result": { "as_number": "{{ as_num }}", "bgp_params": { "dampening": { "max_suppress_time": "{{ val }}", }, }, }, }, { "name": "bgp_params.dampening_re_use", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+dampening \s+re-use \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening re-use {{ bgp_params.dampening.re_use}}", "compval": "bgp_params.dampening.re_use", "result": { "as_number": "{{ as_num }}", "bgp_params": { "dampening": { "re_use": "{{ val }}", }, }, }, }, { "name": "bgp_params.dampening_start_suppress_time", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+dampening \s+start-suppress-time \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening start-suppress-time {{ bgp_params.dampening.start_suppress_time}}", "compval": "bgp_params.dampening.start_suppress_time", "result": { "as_number": "{{ as_num }}", "bgp_params": { "dampening": { "start_suppress_time": "{{ val }}", }, }, }, }, { "name": "bgp_params.default", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+default \s*(?Pno-ipv4-unicast)? \s*(?Plocal-pref\s\S+)?\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_default, "remval": "protocols bgp {{ as_number }} parameters default", "compval": "bgp_params.default", "result": { "as_number": "{{ as_num }}", "bgp_params": { "default": { "no_ipv4_unicast": "{{ True if no_ipv4_unicast is defined }}", "local_pref": "{{ local_pref.split(" " )[1] if local_pref is defined }}", }, }, }, }, { "name": "bgp_params.deterministic_med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+deterministic-med *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters deterministic-med", "compval": "bgp_params.deterministic_med", "result": { "as_number": "{{ as_num }}", "bgp_params": { "deterministic_med": "{{ True }}", }, }, }, { "name": "bgp_params.disbale_network_import_check", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+disable-network-import-check *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters disable-network-import-check", "compval": "bgp_params.disable_network_import_check", "result": { "as_number": "{{ as_num }}", "bgp_params": { "disable_network_import_check": "{{ True }}", }, }, }, { "name": "bgp_params.distance.prefix", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+distance\sprefix \s+(?P\S+) \s+distance \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters distance prefix {{ bgp_params.distance.prefix }} distance {{ bgp_params.distance.value }}", "compval": "bgp_params.distance.prefix", "remval": "protocols bgp {{ as_number }} parameters distance prefix", "result": { "as_number": "{{ as_num }}", "bgp_params": { "distance": [ { "prefix": "{{ prefix }}", "value": "{{ val }}", }, ], }, }, }, { "name": "bgp_params.distance.global", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+distance\sglobal \s+(?P\S+) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_distance, "remval": "protocols bgp {{ as_number }} parameters distance global", "compval": "bgp_params.distance", "result": { "as_number": "{{ as_num }}", "bgp_params": { "distance": [ { "type": "{{ type }}", "value": "{{ val }}", }, ], }, }, }, { "name": "bgp_params.enforce_first_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+enforce-first-as *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters enforce-first-as", "compval": "bgp_params.enforce_first_as", "result": { "as_number": "{{ as_num }}", "bgp_params": { "enforce_first_as": "{{ True }}", }, }, }, { "name": "bgp_params.graceful_restart", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+graceful-restart\s+stalepath-time \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters graceful-restart stalepath-time {{ bgp_params.graceful_restart }}", "compval": "bgp_params.graceful_restart", "result": { "as_number": "{{ as_num }}", "bgp_params": { "graceful_restart": "{{ val }}", }, }, }, { "name": "bgp_params.log_neighbor_changes", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+log-neighbor-changes *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters log-neighbor-changes", "compval": "bgp_params.log_neighbor_changes", "result": { "as_number": "{{ as_num }}", "bgp_params": { "log_neighbor_changes": "{{ True }}", }, }, }, { "name": "bgp_params.no_client_to_client_reflection", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+no-client-to-client-reflection *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters no-client-to-client-reflection", "compval": "bgp_params.log_neighbor_changes", "result": { "as_number": "{{ as_num }}", "bgp_params": { "no_client_to_client_reflection": "{{ True }}", }, }, }, { "name": "bgp_params.no_fast_external_failover", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+no-fast-external-failover *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters no-fast-external-failover", "compval": "bgp_params.no_fast_external_failover", "result": { "as_number": "{{ as_num }}", "bgp_params": { "no_fast_external_failover": "{{ True }}", }, }, }, { "name": "bgp_params.routerid", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+router-id \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters router-id {{ bgp_params.router_id }}", "compval": "bgp_params.router_id", "result": { "as_number": "{{ as_num }}", "bgp_params": { "router_id": "{{ id }}", }, }, }, { "name": "bgp_params.scan_time", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+scan-time \s+(?P\d+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters scan-time {{ bgp_params.scan_time }}", "compval": "bgp_params.scan_time", "result": { "as_number": "{{ as_num }}", "bgp_params": { "scan_time": "{{ val }}", }, }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_global_14.py b/plugins/module_utils/network/vyos/rm_templates/bgp_global_14.py index cbe21e3a..b86e233c 100644 --- a/plugins/module_utils/network/vyos/rm_templates/bgp_global_14.py +++ b/plugins/module_utils/network/vyos/rm_templates/bgp_global_14.py @@ -1,1764 +1,1763 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The Bgp_global parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) def _tmplt_bgp_params_confederation(config_data): command = [] for list_el in config_data["bgp_params"]["confederation"]: for k, v in list_el.items(): command.append( "protocols bgp parameters confederation ".format(**config_data) + k + " " + str(v), ) return command def _tmplt_bgp_maximum_paths(config_data): command = [] for list_el in config_data["maximum_paths"]: command.append( "protocols bgp maximum-paths ".format(**config_data) + list_el["path"] + " " + str(list_el["count"]), ) return command def _tmplt_delete_bgp_maximum_paths(config_data): command = "protocols bgp maximum-paths".format(**config_data) return command def _tmplt_bgp_params_default(config_data): command = "protocols bgp parameters default".format(**config_data) if config_data["bgp_params"]["default"].get("no_ipv4_unicast"): command += " no-ipv4-unicast" if config_data["bgp_params"]["default"].get("local_pref"): command += " local-pref {local_pref}".format(**config_data["bgp_params"]["default"]) return command def _tmplt_bgp_neighbor_timers(config_data): command = [] for k, v in config_data["neighbor"]["timers"].items(): command.append( "protocols bgp neighbor ".format(**config_data) + config_data["neighbor"]["address"] + " timers " + k + " " + str(v), ) return command def _tmplt_bgp_timers(config_data): command = [] for k, v in config_data["timers"].items(): command.append( "protocols bgp ".format(**config_data) + "timers " + k + " " + str(v), ) return command def _tmplt_bgp_neighbor_attribute_unchanged_as_path(config_data): command = "protocols bgp ".format( **config_data, ) + "neighbor {address} attribute-unchanged as-path".format(**config_data["neighbor"]) return command def _tmplt_bgp_neighbor_attribute_unchanged_med(config_data): command = "protocols bgp ".format( **config_data, ) + "neighbor {address} attribute-unchanged med".format(**config_data["neighbor"]) return command def _tmplt_bgp_neighbor_attribute_unchanged_next_hop(config_data): command = "protocols bgp ".format( **config_data, ) + "neighbor {address} attribute-unchanged next-hop".format(**config_data["neighbor"]) return command def _tmplt_bgp_neighbor_distribute_list(config_data): command = [] for list_el in config_data["neighbor"]["distribute_list"]: command.append( "protocols bgp ".format(**config_data) + "neighbor {address} distribute-list ".format(**config_data["neighbor"]) + list_el["action"] + " " + str(list_el["acl"]), ) return command def _tmplt_bgp_neighbor_route_map(config_data): command = [] for list_el in config_data["neighbor"]["route_map"]: command.append( "protocols bgp ".format(**config_data) + "neighbor {address} route-map ".format(**config_data["neighbor"]) + list_el["action"] + " " + str(list_el["route_map"]), ) return command def _tmplt_bgp_neighbor_prefix_list(config_data): command = [] for list_el in config_data["neighbor"]["prefix_list"]: command.append( "protocols bgp ".format(**config_data) + "neighbor {address} prefix-list ".format(**config_data["neighbor"]) + list_el["action"] + " " + str(list_el["prefix_list"]), ) return command def _tmplt_bgp_neighbor_filter_list(config_data): command = [] for list_el in config_data["neighbor"]["filter_list"]: command.append( "protocols bgp ".format(**config_data) + "neighbor {address} filter-list ".format(**config_data["neighbor"]) + list_el["action"] + " " + str(list_el["path_list"]), ) return command def _tmplt_bgp_params_distance(config_data): command = ( "protocols bgp parameters distance global ".format(**config_data) + config_data["bgp_params"]["distance"]["type"] + " " + str(config_data["bgp_params"]["distance"]["value"]) ) return command class Bgp_globalTemplate14(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Bgp_globalTemplate14, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module, ) # fmt: off PARSERS = [ { "name": "router", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s*system-as \s+(?P\d+) *$""", re.VERBOSE, ), "setval": "protocols bgp system-as {{ as_number }}", "remval": "protocols bgp", "compval": "as_number", "result": { "as_number": "{{ as_num }}", }, }, { "name": "maximum_paths", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+maximum-paths \s+(?Pebgp|ibgp) \s+(?P\d+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_maximum_paths, "remval": _tmplt_delete_bgp_maximum_paths, "compval": "maximum_paths", "result": { "as_number": "{{ as_num }}", "maximum_paths": [ { "path": "{{ path }}", "count": "{{ count }}", }, ], }, }, { "name": "neighbor.advertisement_interval", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+advertisement-interval \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} advertisement-interval {{ neighbor.advertisement_interval }}", "remval": "protocols bgp neighbor {{ neighbor.address }} advertisement-interval", "compval": "neighbor.advertisement_interval", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "advertisement_interval": "{{ interval }}", }, }, }, }, { "name": "neighbor.allowas_in", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+allowas-in \s+number \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} allowas-in number {{ neighbor.allowas_in }}", "compval": "neighbor.allowas_in", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "allowas_in": "{{ count }}", }, }, }, }, { "name": "neighbor.as_override", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+as-override *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} as-override", "compval": "neighbor.as_override", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "as_override": "{{ True }}", }, }, }, }, { "name": "neighbor.attribute_unchanged.as_path", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+attribute-unchanged \s+(?Pas-path) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_attribute_unchanged_as_path, "compval": "neighbor.attribute_unchanged", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "attribute_unchanged": { "{{ 'as_path' }}": "{{ True }}", }, }, }, }, }, { "name": "neighbor.attribute_unchanged.med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+attribute-unchanged \s+(?Pmed) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_attribute_unchanged_med, "compval": "neighbor.attribute_unchanged", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "attribute_unchanged": { "{{ 'med' }}": "{{ True }}", }, }, }, }, }, { "name": "neighbor.attribute_unchanged.next_hop", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+attribute-unchanged \s+(?Pnext-hop) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_attribute_unchanged_next_hop, "compval": "neighbor.attribute_unchanged", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "attribute_unchanged": { "{{ 'next_hop' }}": "{{ True }}", }, }, }, }, }, { "name": "neighbor.capability_dynamic", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+capability \s+(?Pdynamic) *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} capability dynamic", "compval": "neighbor.capability.dynamic", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "capability": { "dynamic": "{{ True if dynamic is defined}}", }, }, }, }, }, { "name": "neighbor.capability_orf", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+capability \s+orf \s+prefix-list \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} capability orf prefix-list {{ neighbor.capability.orf }}", "compval": "neighbor.capability.orf", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "capability": { "orf": "{{ orf }}", }, }, }, }, }, { "name": "neighbor.default_originate", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+default-originate \s+route-map \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} default-originate route-map {{ neighbor.default_originate }}", "compval": "neighbor.advertisement_interval", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "default_originate": "{{ map }}", }, }, }, }, { "name": "neighbor.description", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+description \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} description {{ neighbor.description }}", "compval": "neighbor.description", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "description": "{{ desc }}", }, }, }, }, { "name": "neighbor.disable_capability_negotiation", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+disable-capability-negotiation *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} disable-capability-negotiation", "compval": "neighbor.disable_capability_negotiation", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "disable_capability_negotiation": "{{ True }}", }, }, }, }, { "name": "neighbor.disable_connected_check", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+disable-connected-check *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} disable-connected-check", "compval": "neighbor.disable_connected_check", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "disable_connected_check": "{{ True }}", }, }, }, }, { "name": "neighbor.disable_send_community", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+disable-send-community \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} disable-send-community {{ neighbor.disable_send_community }}", "compval": "neighbor.disable_send_community", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "disable_send_community": "{{ comm }}", }, }, }, }, { "name": "neighbor.distribute_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+distribute-list \s+(?Pexport|import) \s+(?P\d+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_distribute_list, "compval": "neighbor.distribute_list", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "distribute_list": [ { "action": "{{ action }}", "acl": "{{ list }}", }, ], }, }, }, }, { "name": "neighbor.ebgp_multihop", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+ebgp-multihop \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} ebgp-multihop {{ neighbor.ebgp_multihop }}", "compval": "neighbor.ebgp_multihop", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "ebgp_multihop": "{{ hop|int }}", }, }, }, }, { "name": "neighbor.filter_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+filter-list \s+(?Pexport|import) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_filter_list, "compval": "neighbor.filter_list", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "filter_list": [ { "action": "{{ action }}", "path_list": "{{ list }}", }, ], }, }, }, }, { "name": "neighbor.local_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+local-as \s+(?P\S+) \s+no-prepend *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} local-as {{ neighbor.local_as }} no-prepend", "compval": "neighbor.local_as", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "local_as": "{{ as }}", }, }, }, }, { "name": "neighbor.maximum_prefix", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+maximum-prefix \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} maximum-prefix {{ neighbor.maximum_prefix }}", "compval": "neighbor.maximum_prefix", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "maximum_prefix": "{{ num }}", }, }, }, }, { "name": "neighbor.nexthop_self", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+nexthop-self *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} nexthop-self", "compval": "neighbor.nexthop_self", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "nexthop_self": "{{ True }}", }, }, }, }, { "name": "neighbor.override_capability", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+override-capability *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} override-capability", "compval": "neighbor.override_capability", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "override_capability": "{{ True }}", }, }, }, }, { "name": "neighbor.passive", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+passive *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} passive", "compval": "neighbor.passive", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "passive": "{{ True }}", }, }, }, }, { "name": "neighbor.password", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+password \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} password {{ neighbor.password }}", "compval": "neighbor.password", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "password": "{{ pwd }}", }, }, }, }, { "name": "neighbor.peer_group_name", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+peer-group \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} peer-group {{ neighbor.peer_group_name }}", "compval": "neighbor.peer_group_name", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "peer_group_name": "{{ name }}", }, }, }, }, { "name": "neighbor.port", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+port \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} port {{ neighbor.port }}", "compval": "neighbor.port", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "port": "{{ num|int }}", }, }, }, }, { "name": "neighbor.prefix_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+prefix-list \s+(?Pexport|import) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_prefix_list, "compval": "neighbor.prefix_list", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "prefix_list": [ { "action": "{{ action }}", "prefix_list": "{{ list }}", }, ], }, }, }, }, { "name": "neighbor.remote_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+remote-as \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} remote-as {{ neighbor.remote_as }}", "compval": "neighbor.remote_as", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "remote_as": "{{ num|int }}", }, }, }, }, { "name": "neighbor.remove_private_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+remote-private-as *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} remote-private-as", "compval": "neighbor.remove_private_as", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "remove_private_as": "{{ True }}", }, }, }, }, { "name": "neighbor.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+route-map \s+(?Pexport|import) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_route_map, "compval": "neighbor.route_map", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "route_map": [ { "action": "{{ action }}", "route_map": "{{ map }}", }, ], }, }, }, }, { "name": "neighbor.route_reflector_client", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+route-reflector-client *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} router-reflector-client", "compval": "neighbor.route_reflector_client", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "route_reflector_client": "{{ True }}", }, }, }, }, { "name": "neighbor.route_server_client", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+route-server-client *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} route-server-client", "compval": "neighbor.route_server_client", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "route_server_client": "{{ True }}", }, }, }, }, { "name": "neighbor.shutdown", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+shutdown *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} shutdown", "compval": "neighbor.shutdown", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "shutdown": "{{ True }}", }, }, }, }, { "name": "neighbor.soft_reconfiguration", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+soft-reconfiguration \s+inbound *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} soft-reconfiguration", "compval": "neighbor.soft_reconfiguration", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "soft_reconfiguration": "{{ True }}", }, }, }, }, { "name": "neighbor.strict_capability_match", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+strict-capability-match *$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} strict-capability-match", "compval": "neighbor.strict_capability_match", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "strict_capability_match": "{{ True }}", }, }, }, }, { "name": "neighbor.unsuppress_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+unsuppress-map \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} unsuppress-map {{ neighbor.unsuppress_map }}", "compval": "neighbor.unsuppress_map", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "unsuppress_map": "{{ map }}", }, }, }, }, { "name": "neighbor.update_source", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+update-source \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} update-source {{ neighbor.update_source }}", "compval": "neighbor.update_source", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "update_source": "{{ src }}", }, }, }, }, { "name": "neighbor.weight", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+weight \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} weight {{ neighbor.weight }}", "compval": "neighbor.weight", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "weight": "{{ num }}", }, }, }, }, { "name": "neighbor.ttl_security", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+ttl-security \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} ttl-security {{ neighbor.ttl_security }}", "compval": "neighbor.ttl_security", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "ttl_security": "{{ ttl|int }}", }, }, }, }, { "name": "neighbor.timers", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+neighbor \s+(?P
\S+) \s+timers \s+(?Pconnect|holdtime|keepalive) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_timers, "remval": "protocols bgp neighbor {{ neighbor.address }} timers", "compval": "neighbor.timers", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "timers": { "{{ type }}": "{{ sec }}", }, }, }, }, }, { "name": "timers", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+timers \s+(?P\S+) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_timers, "remval": "protocols bgp timers", "compval": "timers", "result": { "as_number": "{{ as_num }}", "timers": { "{{ type }}": "{{ val }}", }, }, }, { "name": "bgp_params.always_compare_med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+always-compare-med *$""", re.VERBOSE, ), "setval": "protocols bgp parameters always-compare-med", "compval": "bgp_params.always_compare_med", "result": { "as_number": "{{ as_num }}", "bgp_params": { "always_compare_med": "{{ True }}", }, }, }, { "name": "bgp_params.bestpath.as_path", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+bestpath \s+as-path \s+(?Pconfed|ignore) *$""", re.VERBOSE, ), "setval": "protocols bgp parameters bestpath as-path {{ bgp_params.bestpath.as_path }}", "compval": "bgp_params.bestpath.as_path", "result": { "as_number": "{{ as_num }}", "bgp_params": { "bestpath": { "as_path": "{{ path }}", }, }, }, }, { "name": "bgp_params.bestpath.compare_routerid", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+bestpath \s+compare-routerid *$""", re.VERBOSE, ), "setval": "protocols bgp parameters bestpath compare-routerid", "compval": "bgp_params.bestpath.compare_routerid", "result": { "as_number": "{{ as_num }}", "bgp_params": { "bestpath": { "compare_routerid": "{{ True }}", }, }, }, }, { "name": "bgp_params.bestpath.med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+bestpath \s+med \s+(?Pconfed|missing-as-worst) *$""", re.VERBOSE, ), "setval": "protocols bgp parameters bestpath med {{ bestpath.med }}", "compval": "bgp_params.bestpath.med", "result": { "as_number": "{{ as_num }}", "bgp_params": { "bestpath": { "med": "{{ path }}", }, }, }, }, { "name": "bgp_params.cluster_id", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+cluster-id \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters cluster-id {{ bgp_params.cluster_id }}", "compval": "bgp_params.cluster_id", "result": { "as_number": "{{ as_num }}", "bgp_params": { "cluster_id": "{{ id }}", }, }, }, { "name": "bgp_params.confederation", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+confederation \s+(?Pidentifier|peers) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_confederation, "compval": "bgp_params.always_compare_med", "result": { "as_number": "{{ as_num }}", "bgp_params": { "confederation": [ { "peers": "{{ val if type == 'peers' }}", "identifier": "{{ val if type == 'identifier' }}", }, ], }, }, }, { "name": "bgp_params.dampening_half_life", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+dampening \s+half-life \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters dampening half-life {{ bgp_params.dampening.half_life}}", "compval": "bgp_params.dampening.half_life", "result": { "as_number": "{{ as_num }}", "bgp_params": { "dampening": { "half_life": "{{ val }}", }, }, }, }, { "name": "bgp_params.dampening_max_suppress_time", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+dampening \s+max-suppress-time \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters dampening max-suppress-time {{ bgp_params.dampening.max_suppress_time}}", "compval": "bgp_params.dampening.max_suppress_time", "result": { "as_number": "{{ as_num }}", "bgp_params": { "dampening": { "max_suppress_time": "{{ val }}", }, }, }, }, { "name": "bgp_params.dampening_re_use", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+dampening \s+re-use \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters dampening re-use {{ bgp_params.dampening.re_use}}", "compval": "bgp_params.dampening.re_use", "result": { "as_number": "{{ as_num }}", "bgp_params": { "dampening": { "re_use": "{{ val }}", }, }, }, }, { "name": "bgp_params.dampening_start_suppress_time", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+dampening \s+start-suppress-time \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters dampening start-suppress-time {{ bgp_params.dampening.start_suppress_time}}", "compval": "bgp_params.dampening.start_suppress_time", "result": { "as_number": "{{ as_num }}", "bgp_params": { "dampening": { "start_suppress_time": "{{ val }}", }, }, }, }, { "name": "bgp_params.default", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+default \s*(?Pno-ipv4-unicast)? \s*(?Plocal-pref\s\S+)?\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_default, "remval": "protocols bgp parameters default", "compval": "bgp_params.default", "result": { "as_number": "{{ as_num }}", "bgp_params": { "default": { "no_ipv4_unicast": "{{ True if no_ipv4_unicast is defined }}", "local_pref": "{{ local_pref.split(" " )[1] if local_pref is defined }}", }, }, }, }, { "name": "bgp_params.deterministic_med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+deterministic-med *$""", re.VERBOSE, ), "setval": "protocols bgp parameters deterministic-med", "compval": "bgp_params.deterministic_med", "result": { "as_number": "{{ as_num }}", "bgp_params": { "deterministic_med": "{{ True }}", }, }, }, { "name": "bgp_params.disbale_network_import_check", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+disable-network-import-check *$""", re.VERBOSE, ), "setval": "protocols bgp parameters disable-network-import-check", "compval": "bgp_params.disable_network_import_check", "result": { "as_number": "{{ as_num }}", "bgp_params": { "disable_network_import_check": "{{ True }}", }, }, }, { "name": "bgp_params.distance.prefix", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+distance\sprefix \s+(?P\S+) \s+distance \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters distance prefix {{ bgp_params.distance.prefix }} distance {{ bgp_params.distance.value }}", "compval": "bgp_params.distance.prefix", "remval": "protocols bgp parameters distance prefix", "result": { "as_number": "{{ as_num }}", "bgp_params": { "distance": [ { "prefix": "{{ prefix }}", "value": "{{ val }}", }, ], }, }, }, { "name": "bgp_params.distance.global", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+distance\sglobal \s+(?P\S+) \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_distance, "remval": "protocols bgp parameters distance global", "compval": "bgp_params.distance", "result": { "as_number": "{{ as_num }}", "bgp_params": { "distance": [ { "type": "{{ type }}", "value": "{{ val }}", }, ], }, }, }, { "name": "bgp_params.enforce_first_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+enforce-first-as *$""", re.VERBOSE, ), "setval": "protocols bgp parameters enforce-first-as", "compval": "bgp_params.enforce_first_as", "result": { "as_number": "{{ as_num }}", "bgp_params": { "enforce_first_as": "{{ True }}", }, }, }, { "name": "bgp_params.graceful_restart", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+graceful-restart\s+stalepath-time \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters graceful-restart stalepath-time {{ bgp_params.graceful_restart }}", "compval": "bgp_params.graceful_restart", "result": { "as_number": "{{ as_num }}", "bgp_params": { "graceful_restart": "{{ val }}", }, }, }, { "name": "bgp_params.log_neighbor_changes", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+log-neighbor-changes *$""", re.VERBOSE, ), "setval": "protocols bgp parameters log-neighbor-changes", "compval": "bgp_params.log_neighbor_changes", "result": { "as_number": "{{ as_num }}", "bgp_params": { "log_neighbor_changes": "{{ True }}", }, }, }, { "name": "bgp_params.no_client_to_client_reflection", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+no-client-to-client-reflection *$""", re.VERBOSE, ), "setval": "protocols bgp parameters no-client-to-client-reflection", "compval": "bgp_params.log_neighbor_changes", "result": { "as_number": "{{ as_num }}", "bgp_params": { "no_client_to_client_reflection": "{{ True }}", }, }, }, { "name": "bgp_params.no_fast_external_failover", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+no-fast-external-failover *$""", re.VERBOSE, ), "setval": "protocols bgp parameters no-fast-external-failover", "compval": "bgp_params.no_fast_external_failover", "result": { "as_number": "{{ as_num }}", "bgp_params": { "no_fast_external_failover": "{{ True }}", }, }, }, { "name": "bgp_params.routerid", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+router-id \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters router-id {{ bgp_params.router_id }}", "compval": "bgp_params.router_id", "result": { "as_number": "{{ as_num }}", "bgp_params": { "router_id": "{{ id }}", }, }, }, { "name": "bgp_params.scan_time", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+parameters \s+scan-time \s+(?P\d+) *$""", re.VERBOSE, ), "setval": "protocols bgp parameters scan-time {{ bgp_params.scan_time }}", "compval": "bgp_params.scan_time", "result": { "as_number": "{{ as_num }}", "bgp_params": { "scan_time": "{{ val }}", }, }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/hostname.py b/plugins/module_utils/network/vyos/rm_templates/hostname.py index 29ab00f6..b7e56310 100644 --- a/plugins/module_utils/network/vyos/rm_templates/hostname.py +++ b/plugins/module_utils/network/vyos/rm_templates/hostname.py @@ -1,53 +1,52 @@ # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The Hostname parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) class HostnameTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(HostnameTemplate, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module, ) # fmt: off PARSERS = [ # service snmp community <> { "name": "hostname", "getval": re.compile( r""" ^set\ssystem\shost-name \s+(?P\S+) $""", re.VERBOSE, ), "setval": "system host-name {{ hostname }}", "result": { "hostname": "{{ name }}", }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/logging_global.py b/plugins/module_utils/network/vyos/rm_templates/logging_global.py index 516e270b..07ad1f68 100644 --- a/plugins/module_utils/network/vyos/rm_templates/logging_global.py +++ b/plugins/module_utils/network/vyos/rm_templates/logging_global.py @@ -1,455 +1,454 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The Logging_global parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) def tmplt_params(config_data): def templt_common(val, tmplt): if val.get("facility"): tmplt += " facility {facility}".format(facility=val["facility"]) if val.get("severity"): tmplt += " level {level}".format(level=val["severity"]) elif val.get("protocol"): tmplt += " protocol {protocol}".format(protocol=val["protocol"]) return tmplt tmplt = "" if config_data.get("global_params"): val = config_data.get("global_params") if not val.get("archive"): tmplt += "system syslog global" tmplt = templt_common(val.get("facilities"), tmplt) elif config_data.get("console"): val = config_data.get("console") tmplt += "system syslog console" tmplt = templt_common(val.get("facilities"), tmplt) elif config_data.get("users"): val = config_data.get("users") if val.get("username") and not val.get("archive"): tmplt += "system syslog user {username}".format(username=val["username"]) if val.get("facilities"): tmplt = templt_common(val.get("facilities"), tmplt) elif config_data.get("hosts"): val = config_data.get("hosts") if ( val.get("hostname") and not val.get("archive") and not val.get("port") and not val.get("protocol") ): tmplt += "system syslog host {hostname}".format(hostname=val["hostname"]) if val.get("facilities"): tmplt = templt_common(val.get("facilities"), tmplt) elif config_data.get("files"): val = config_data.get("files") if val.get("path") and not val.get("archive"): tmplt += "system syslog file {path}".format(path=val["path"]) if val.get("facilities"): tmplt = templt_common(val.get("facilities"), tmplt) return tmplt class Logging_globalTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Logging_globalTemplate, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module, ) # fmt: off PARSERS = [ { "name": "syslog.state", "getval": re.compile( r""" ^set\ssystem (\s(?Psyslog)) $""", re.VERBOSE, ), "setval": "system syslog", "result": { "syslog": { "state": "{{ 'enabled' if syslog is defined else 'disabled' }}", }, }, }, { "name": "console.facilities", "getval": re.compile( r""" ^set\ssystem\ssyslog\sconsole\sfacility (\s(?Pall|auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local[0-7]))? (\slevel\s(?P'(emerg|alert|crit|err|warning|notice|info|debug|all)'))? $""", re.VERBOSE, ), "setval": tmplt_params, "remval": "system syslog console facility {{ console.facilities.facility }}", "result": { "console": { "facilities": [ { "facility": "{{ facility }}", "severity": "{{ level }}", }, ], }, }, }, { "name": "console.state", "getval": re.compile( r""" ^set\ssystem\ssyslog (\s(?Pconsole)) $""", re.VERBOSE, ), "setval": "system syslog console", "result": { "console": { "state": "{{ 'enabled' if console is defined else 'disabled' }}", }, }, }, { "name": "files.archive.state", "getval": re.compile( r""" ^set\ssystem\ssyslog\sfile (\s(?P\S+))? (\s(?Parchive)) $""", re.VERBOSE, ), "setval": "system syslog file {{ files.path }} archive", "result": { "files": { "{{ path }}": { "path": "{{ path }}", "archive": { "state": "{{ 'enabled' if archive is defined else 'disabled' }}", }, }, }, }, }, { "name": "files.archive.size", "getval": re.compile( r""" ^set\ssystem\ssyslog\sfile (\s(?P\S+))? (\sarchive\ssize\s(?P'(\d+)'))? $""", re.VERBOSE, ), "setval": "system syslog file {{ files.path }} archive size {{ files.archive.size }}", "result": { "files": { "{{ path }}": { "path": "{{ path }}", "archive": { "size": "{{ size }}", }, }, }, }, }, { "name": "files.archive.file_num", "getval": re.compile( r""" ^set\ssystem\ssyslog\sfile (\s(?P\S+))? (\sarchive\sfile\s(?P'(\d+)'))? $""", re.VERBOSE, ), "setval": "system syslog file {{ files.path }} archive file {{ files.archive.file_num }}", "result": { "files": { "{{ path }}": { "path": "{{ path }}", "archive": { "file_num": "{{ file_num }}", }, }, }, }, }, { "name": "files", "getval": re.compile( r""" ^set\ssystem\ssyslog\sfile (\s(?P\S+))? (\sfacility\s(?Pall|auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local[0-7]))? (\slevel\s(?P'(emerg|alert|crit|err|warning|notice|info|debug|all)'))? $""", re.VERBOSE, ), "setval": tmplt_params, "remval": "system syslog file{{ (' ' + files.path) if files.path|d('') is defined else '' }}", "result": { "files": { "{{ path }}": { "path": "{{ path }}", "facilities": [ { "facility": "{{ facility }}", "severity": "{{ level }}", }, ], }, }, }, }, { "name": "global_params.state", "getval": re.compile( r""" ^set\ssystem\ssyslog (\s(?Pglobal)) $""", re.VERBOSE, ), "setval": "system syslog global", "result": { "global_params": { "state": "{{ 'enabled' if global is defined else 'disabled' }}", }, }, }, { "name": "global_params.archive.state", "getval": re.compile( r""" ^set\ssystem\ssyslog\sglobal (\s(?Parchive)) $""", re.VERBOSE, ), "setval": "system syslog global archive", "result": { "global_params": { "archive": { "state": "{{ 'enabled' if archive is defined else 'disabled' }}", }, }, }, }, { "name": "global_params.archive.file_num", "getval": re.compile( r""" ^set\ssystem\ssyslog\sglobal\sarchive\sfile (\s(?P'(\d+)'))? $""", re.VERBOSE, ), "setval": "system syslog global archive file {{ global_params.archive.file_num }}", "result": { "global_params": { "archive": { "file_num": "{{ file_num }}", }, }, }, }, { "name": "global_params.archive.size", "getval": re.compile( r""" ^set\ssystem\ssyslog\sglobal\sarchive\ssize (\s(?P'(\d+)'))? $""", re.VERBOSE, ), "setval": "system syslog global archive size {{ global_params.archive.size }}", "result": { "global_params": { "archive": { "size": "{{ size }}", }, }, }, }, { "name": "global_params.marker_interval", "getval": re.compile( r""" ^set\ssystem\ssyslog\sglobal\smarker\sinterval (\s(?P'(\d+)'))? $""", re.VERBOSE, ), "setval": "system syslog global marker interval {{ global_params.marker_interval }}", "remval": "system syslog global marker", "result": { "global_params": { "marker_interval": "{{ marker_interval }}", }, }, }, { "name": "global_params.preserve_fqdn", "getval": re.compile( r""" ^set\ssystem\ssyslog\sglobal (\s(?Ppreserve-fqdn)) $""", re.VERBOSE, ), "setval": "system syslog global preserve-fqdn", "result": { "global_params": { "preserve_fqdn": "{{ True if preserve_fqdn is defined }}", }, }, }, { "name": "global_params.facilities", "getval": re.compile( r""" ^set\ssystem\ssyslog\sglobal\sfacility (\s(?Pall|auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local[0-7]))? (\slevel\s(?P'(emerg|alert|crit|err|warning|notice|info|debug|all)'))? $""", re.VERBOSE, ), "setval": tmplt_params, "remval": "system syslog global facility {{ global_params.facilities.facility }}", "result": { "global_params": { "facilities": [ { "facility": "{{ facility }}", "severity": "{{ level }}", }, ], }, }, }, { "name": "hosts.port", "getval": re.compile( r""" ^set\ssystem\ssyslog\shost (\s(?P\S+)) (\sport\s(?P'(\d+)')) $""", re.VERBOSE, ), "setval": "system syslog host {{ hosts.hostname }} port {{ hosts.port }}", "result": { "hosts": { "{{ hostname }}": { "hostname": "{{ hostname }}", "port": "{{ port }}", }, }, }, }, { "name": "hosts.protocol", "getval": re.compile( r""" ^set\ssystem\ssyslog\shost (\s(?P\S+)) (\sprotocol\s(?P'(udp|tcp)')) $""", re.VERBOSE, ), "setval": "system syslog host {{ hosts.hostname }} protocol {{ hosts.protocol }}", "result": { "hosts": { "{{ hostname }}": { "hostname": "{{ hostname }}", "protocol": "{{ protocol }}", }, }, }, }, { # Version 1.3 and below "name": "hosts.facility.protocol", "getval": re.compile( r""" ^set\ssystem\ssyslog\shost (\s(?P\S+)) (\sfacility\s(?Pall|auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local[0-7])) (\sprotocol\s(?P'(udp|tcp)')) $""", re.VERBOSE, ), "setval": "system syslog host {{ hosts.hostname }} facility {{ hosts.facility }} protocol {{ hosts.protocol }}", "remval": "system syslog host {{ hosts.hostname }} facility {{ hosts.facility }} protocol {{ hosts.protocol }}", "result": { "hosts": { "{{ hostname }}": { "hostname": "{{ hostname }}", "facilities": [ { "facility": "{{ facility }}", "protocol": "{{ protocol }}", }, ], }, }, }, }, { "name": "hosts", "getval": re.compile( r""" ^set\ssystem\ssyslog\shost (\s(?P\S+)) (\sfacility\s(?Pall|auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local[0-7])) (\slevel\s(?P'(emerg|alert|crit|err|warning|notice|info|debug|all)'))? $""", re.VERBOSE, ), "setval": tmplt_params, "remval": "system syslog host {{ hosts.hostname }}", "result": { "hosts": { "{{ hostname }}": { "hostname": "{{ hostname }}", "facilities": [ { "facility": "{{ facility }}", "severity": "{{ level }}", }, ], }, }, }, }, { "name": "users", "getval": re.compile( r""" ^set\ssystem\ssyslog\suser (\s(?P\S+))? (\sfacility\s(?Pall|auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local[0-7]))? (\slevel\s(?P'(emerg|alert|crit|err|warning|notice|info|debug|all)'))? $""", re.VERBOSE, ), "setval": tmplt_params, "remval": "system syslog user {{ users.username }}", "result": { "users": { "{{ username }}": { "username": "{{ username }}", "facilities": [ { "facility": "{{ facility }}", "severity": "{{ level }}", }, ], }, }, }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/ntp_global.py b/plugins/module_utils/network/vyos/rm_templates/ntp_global.py index e9d8a0cb..b83a3e1c 100644 --- a/plugins/module_utils/network/vyos/rm_templates/ntp_global.py +++ b/plugins/module_utils/network/vyos/rm_templates/ntp_global.py @@ -1,169 +1,168 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The Ntp parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) class NtpTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} self._overrides = { # 1.4+ by default "_path": "service", # 1.4 or greater, "system" for 1.3 or less "_ac": "allow-client", # 1.4 or greater, "allow-clients" for 1.3 or less } super(NtpTemplate, self).__init__(lines=lines, tmplt=self, prefix=prefix, module=module) def set_ntp_path(self, path: str): """set_ntp_path""" self._overrides["_path"] = path def set_ntp_ac(self, ac: str): """set_ntp_ac""" self._overrides["_ac"] = ac def render(self, data, parser_name, negate=False): """render""" # add path to the data before rendering data = data.copy() data.update(self._overrides) # call the original method return super(NtpTemplate, self).render(data, parser_name, negate) # fmt: off PARSERS = [ # set system ntp allow_clients address
{ "name": "allow_clients", "getval": re.compile( r""" ^set\s(?Psystem|service)\sntp\s(?Pallow-clients|allow-client)\saddress (\s(?P\S+))? $""", re.VERBOSE, ), "setval": "{{_path}} ntp {{_ac}} address {{allow_clients}}", "result": { "allow_clients": ["{{ipaddress}}"], }, }, # set system ntp allow_clients { "name": "allow_clients_delete", "getval": re.compile( r""" ^set\s(?Psystem|service)\sntp\s(?Pallow-clients|allow-client) $""", re.VERBOSE, ), "setval": "{{_path}} ntp {{_ac}}", "result": { }, }, # set system ntp listen_address
{ "name": "listen_addresses", "getval": re.compile( r""" ^set\s(?Psystem|service)\sntp\slisten-address (\s(?P\S+))? $""", re.VERBOSE, ), "setval": "{{_path}} ntp listen-address {{listen_addresses}}", "result": { "listen_addresses": ["{{ip_address}}"], }, }, # set system ntp listen_address { "name": "listen_addresses_delete", "getval": re.compile( r""" ^set\s(?Psystem|service)\sntp\slisten-address $""", re.VERBOSE, ), "setval": "{{_path}} ntp listen-address", "result": { }, }, # set {{path}} ntp - for deleting the ntp configuration { "name": "service_delete", "getval": re.compile( r""" ^set\s(?Psystem|service)\sntp$ $""", re.VERBOSE, ), "setval": "{{_path}} ntp", "result": { }, }, # set system ntp server { "name": "server", "getval": re.compile( r""" ^set\s(?Psystem|service)\sntp\sserver (\s(?P\S+)) $""", re.VERBOSE, ), "setval": "{{_path}} ntp server {{server}}", "result": { "servers": { "{{name}}": { "server": "{{name}}", }, }, }, }, # set system ntp server { "name": "options", "getval": re.compile( r""" ^set\s(?Psystem|service)\sntp\sserver \s(?P\S+) \s(?Pdynamic|preempt|pool|noselect|prefer|nts|interleave|ptp) $""", re.VERBOSE, ), "setval": "{{_path}} ntp server {{server}} {{options}}", "result": { "servers": { "{{name}}": { "server": "{{name}}", "options": ["{{options}}"], }, }, }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py index af04da3b..5183aec4 100644 --- a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py +++ b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py @@ -1,773 +1,772 @@ # -*- coding: utf-8 -*- # Copyright 2020 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The Ospf_interfaces parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( get_interface_type, get_interface_with_vif, ) def _get_parameters(data): if data["afi"] == "ipv6": val = ["ospfv3", "ipv6"] else: val = ["ospf", "ip"] return val def _tmplt_ospf_int_delete(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] return command def _tmplt_ospf_int_cost(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " cost {cost}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_auth_password(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " authentication plaintext-password {plaintext_password}".format( **config_data["address_family"]["authentication"], ) ) return command def _tmplt_ospf_int_auth_md5(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " authentication md5 key-id {key_id} ".format( **config_data["address_family"]["authentication"]["md5_key"], ) + "md5-key {key}".format(**config_data["address_family"]["authentication"]["md5_key"]) ) return command def _tmplt_ospf_int_auth_md5_delete(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " authentication" ) return command def _tmplt_ospf_int_bw(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " bandwidth {bandwidth}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_hello_interval(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " hello-interval {hello_interval}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_dead_interval(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " dead-interval {dead_interval}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_mtu_ignore(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " mtu-ignore" ) return command def _tmplt_ospf_int_network(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " network {network}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_priority(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " priority {priority}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_retransmit_interval(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " retransmit-interval {retransmit_interval}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_transmit_delay(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " transmit-delay {transmit_delay}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_ifmtu(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " ifmtu {ifmtu}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_instance(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " instance-id {instance}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_passive(config_data): int_type = get_interface_type(config_data["name"]) name = get_interface_with_vif(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = "interfaces " + int_type + " " + name + " " + params[1] + " " + params[0] + " passive" return command class Ospf_interfacesTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Ospf_interfacesTemplate, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module, ) # fmt: off PARSERS = [ { "name": "ip_ospf", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) *$""", re.VERBOSE, ), "remval": _tmplt_ospf_int_delete, "compval": "address_family", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', }, }, }, }, { "name": "authentication_password", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+authentication \s+plaintext-password \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_password, "compval": "address_family.authentication", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "authentication": { "plaintext_password": "{{ text }}", }, }, }, }, }, { "name": "authentication_md5", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+authentication \s+md5 \s+key-id \s+(?P\d+) \s+md5-key \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_md5, "remval": _tmplt_ospf_int_auth_md5_delete, "compval": "address_family.authentication", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "authentication": { "md5_key": { "key_id": "{{ id }}", "key": "{{ text }}", }, }, }, }, }, }, { "name": "bandwidth", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+bandwidth \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_bw, "compval": "address_family.bandwidth", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "bandwidth": "{{ bw }}", }, }, }, }, { "name": "cost", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+cost \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_cost, "compval": "address_family.cost", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "cost": "{{ val }}", }, }, }, }, { "name": "hello_interval", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+hello-interval \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_hello_interval, "compval": "address_family.hello_interval", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "hello_interval": "{{ val }}", }, }, }, }, { "name": "dead_interval", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+dead-interval \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_dead_interval, "compval": "address_family.dead_interval", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "dead_interval": "{{ val }}", }, }, }, }, { "name": "mtu_ignore", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+(?Pmtu-ignore) *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_mtu_ignore, "compval": "address_family.mtu_ignore", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "mtu_ignore": "{{ True if mtu is defined }}", }, }, }, }, { "name": "network", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+network \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_network, "compval": "address_family.network", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "network": "{{ val }}", }, }, }, }, { "name": "priority", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+priority \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_priority, "compval": "address_family.priority", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "priority": "{{ val }}", }, }, }, }, { "name": "retransmit_interval", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+retransmit-interval \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_retransmit_interval, "compval": "address_family.retransmit_interval", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "retransmit_interval": "{{ val }}", }, }, }, }, { "name": "transmit_delay", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+transmit-delay \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_transmit_delay, "compval": "address_family.transmit_delay", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "transmit_delay": "{{ val }}", }, }, }, }, { "name": "ifmtu", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+ifmtu \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_ifmtu, "compval": "address_family.ifmtu", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "ifmtu": "{{ val }}", }, }, }, }, { "name": "instance", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+instance-id \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_instance, "compval": "address_family.instance", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "instance": "{{ val }}", }, }, }, }, { "name": "passive", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+(?Ppassive) *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_passive, "compval": "address_family.passive", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "passive": "{{ True if pass is defined }}", }, }, }, }, { "name": "interface_name", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) (?:\s+vif\s+(?P\d+))? .*$""", re.VERBOSE, ), "setval": "set interface {{ type }} {{ name }}", "result": { "name": "{{ name + '.' + vif if vif is defined else name }}", }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py index 484d7c44..8d09011f 100644 --- a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py +++ b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py @@ -1,647 +1,632 @@ # -*- coding: utf-8 -*- # Copyright 2020 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The Ospf_interfaces parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) def _get_parameters(data): if data["afi"] == "ipv6": val = ["ospfv3", "ipv6"] else: val = ["ospf", "ip"] return val def _tmplt_ospf_int_delete(config_data): params = _get_parameters(config_data["address_family"]) - command = ( - "protocols " + params[0] + " interface {name}".format(**config_data) - ) + command = "protocols " + params[0] + " interface {name}".format(**config_data) return command def _tmplt_ospf_int_cost(config_data): params = _get_parameters(config_data["address_family"]) command = ( "protocols " + params[0] + " interface {name}".format(**config_data) + " cost {cost}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_auth_password(config_data): params = _get_parameters(config_data["address_family"]) command = ( "protocols " + params[0] + " interface {name}".format(**config_data) + " authentication plaintext-password {plaintext_password}".format( **config_data["address_family"]["authentication"] ) ) return command def _tmplt_ospf_int_auth_md5(config_data): params = _get_parameters(config_data["address_family"]) command = ( "protocols " + params[0] + " interface {name}".format(**config_data) + " authentication md5 key-id {key_id} ".format( **config_data["address_family"]["authentication"]["md5_key"] ) + "md5-key {key}".format(**config_data["address_family"]["authentication"]["md5_key"]) ) return command def _tmplt_ospf_int_auth_md5_delete(config_data): params = _get_parameters(config_data["address_family"]) command = ( - "protocols " - + params[0] - + " interface {name}".format(**config_data) - + " authentication" + "protocols " + params[0] + " interface {name}".format(**config_data) + " authentication" ) return command def _tmplt_ospf_int_bw(config_data): params = _get_parameters(config_data["address_family"]) command = ( "protocols " + params[0] + " interface {name}".format(**config_data) + " bandwidth {bandwidth}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_hello_interval(config_data): params = _get_parameters(config_data["address_family"]) command = ( "protocols " + params[0] + " interface {name}".format(**config_data) + " hello-interval {hello_interval}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_dead_interval(config_data): params = _get_parameters(config_data["address_family"]) command = ( "protocols " + params[0] + " interface {name}".format(**config_data) + " dead-interval {dead_interval}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_mtu_ignore(config_data): params = _get_parameters(config_data["address_family"]) - command = ( - "protocols " - + params[0] - + " interface {name}".format(**config_data) - + " mtu-ignore" - ) + command = "protocols " + params[0] + " interface {name}".format(**config_data) + " mtu-ignore" return command def _tmplt_ospf_int_network(config_data): params = _get_parameters(config_data["address_family"]) command = ( "protocols " + params[0] + " interface {name}".format(**config_data) + " network {network}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_priority(config_data): params = _get_parameters(config_data["address_family"]) command = ( "protocols " + params[0] + " interface {name}".format(**config_data) + " priority {priority}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_retransmit_interval(config_data): params = _get_parameters(config_data["address_family"]) command = ( "protocols " + params[0] + " interface {name}".format(**config_data) + " retransmit-interval {retransmit_interval}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_transmit_delay(config_data): params = _get_parameters(config_data["address_family"]) command = ( "protocols " + params[0] + " interface {name}".format(**config_data) + " transmit-delay {transmit_delay}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_ifmtu(config_data): params = _get_parameters(config_data["address_family"]) command = ( "protocols " + params[0] + " interface {name}".format(**config_data) + " ifmtu {ifmtu}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_instance(config_data): params = _get_parameters(config_data["address_family"]) command = ( "protocols " + params[0] + " interface {name}".format(**config_data) + " instance-id {instance}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_passive(config_data): params = _get_parameters(config_data["address_family"]) - command = ( - "protocols " - + params[0] - + " interface {name}".format(**config_data) - + " passive" - ) + command = "protocols " + params[0] + " interface {name}".format(**config_data) + " passive" return command class Ospf_interfacesTemplate14(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Ospf_interfacesTemplate14, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module ) # fmt: off PARSERS = [ { "name": "ip_ospf", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+)\s*$""", re.VERBOSE, ), "remval": _tmplt_ospf_int_delete, "compval": "address_family", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', } } } }, { "name": "authentication_password", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+authentication \s+plaintext-password \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_password, "compval": "address_family.authentication", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "authentication": { "plaintext_password": "{{ text }}" } } } } }, { "name": "authentication_md5", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+authentication \s+md5 \s+key-id \s+(?P\d+) \s+md5-key \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_md5, "remval": _tmplt_ospf_int_auth_md5_delete, "compval": "address_family.authentication", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "authentication": { "md5_key": { "key_id": "{{ id }}", "key": "{{ text }}" } } } } } }, { "name": "bandwidth", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+bandwidth \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_bw, "compval": "address_family.bandwidth", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "bandwidth": "{{ bw }}" } } } }, { "name": "cost", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+cost \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_cost, "compval": "address_family.cost", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "cost": "{{ val }}" } } } }, { "name": "hello_interval", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+hello-interval \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_hello_interval, "compval": "address_family.hello_interval", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "hello_interval": "{{ val }}" } } } }, { "name": "dead_interval", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+dead-interval \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_dead_interval, "compval": "address_family.dead_interval", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "dead_interval": "{{ val }}" } } } }, { "name": "mtu_ignore", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+(?Pmtu-ignore) *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_mtu_ignore, "compval": "address_family.mtu_ignore", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "mtu_ignore": "{{ True if mtu is defined }}" } } } }, { "name": "network", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+network \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_network, "compval": "address_family.network", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "network": "{{ val }}" } } } }, { "name": "priority", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+priority \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_priority, "compval": "address_family.priority", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "priority": "{{ val }}" } } } }, { "name": "retransmit_interval", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+retransmit-interval \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_retransmit_interval, "compval": "address_family.retransmit_interval", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "retransmit_interval": "{{ val }}" } } } }, { "name": "transmit_delay", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+transmit-delay \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_transmit_delay, "compval": "address_family.transmit_delay", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "transmit_delay": "{{ val }}" } } } }, { "name": "ifmtu", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+ifmtu \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_ifmtu, "compval": "address_family.ifmtu", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "ifmtu": "{{ val }}" } } } }, { "name": "instance", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+instance-id \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_instance, "compval": "address_family.instance", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "instance": "{{ val }}" } } } }, { "name": "passive", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) \s+(?Ppassive) *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_passive, "compval": "address_family.passive", "result": { "name": "{{ name }}", "address_family": { '{{ "ipv4" if proto == "ospf" else "ipv6" }}': { "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}', "passive": "{{ True if pass is defined }}" } } } }, { "name": "interface_name", "getval": re.compile( r""" ^set \s+protocols \s+(?Pospf|ospfv3) \s+interface \s+(?P\S+) .*$""", re.VERBOSE, ), "setval": "set protocols {{ proto }} interface {{ name }}", "result": { "name": "{{ name }}", } }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/prefix_lists.py b/plugins/module_utils/network/vyos/rm_templates/prefix_lists.py index 0e99cfea..0e071199 100644 --- a/plugins/module_utils/network/vyos/rm_templates/prefix_lists.py +++ b/plugins/module_utils/network/vyos/rm_templates/prefix_lists.py @@ -1,278 +1,277 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The Prefix_lists parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) class Prefix_listsTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Prefix_listsTemplate, self).__init__( lines=lines, tmplt=self, module=module, prefix=prefix, ) # fmt: off PARSERS = [ # policy prefix-list { "name": "name", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) $""", re.VERBOSE, ), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }}", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", }, }, }, }, }, # policy prefix-list description { "name": "description", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \sdescription\s'(?P.+)' $""", re.VERBOSE, ), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} description '{{ description }}'", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "description": "{{ description }}", }, }, }, }, }, # policy prefix-list rule { "name": "sequence", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \srule\s(?P\d+) $""", re.VERBOSE, ), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} rule {{ sequence }}", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "entries": { "{{ sequence }}": { "sequence": "{{ sequence }}", }, }, }, }, }, }, }, # policy prefix-list rule action { "name": "action", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \srule\s(?P\d+) \saction\s'(?Ppermit|deny)' $""", re.VERBOSE, ), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} rule {{ sequence }} action '{{ action }}'", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "entries": { "{{ sequence }}": { "sequence": "{{ sequence }}", "action": "{{ action }}", }, }, }, }, }, }, }, # policy prefix-list rule description { "name": "rule_description", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \srule\s(?P\d+) \sdescription\s'(?P.+)' $""", re.VERBOSE, ), "compval": "description", "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} rule {{ sequence }} description '{{ description }}'", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "entries": { "{{ sequence }}": { "sequence": "{{ sequence }}", "description": "{{ rule_description }}", }, }, }, }, }, }, }, # policy prefix-list rule ge { "name": "ge", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \srule\s(?P\d+) \sge\s'(?P\d+)' $""", re.VERBOSE, ), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} rule {{ sequence }} ge '{{ ge }}'", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "entries": { "{{ sequence }}": { "sequence": "{{ sequence }}", "ge": "{{ ge }}", }, }, }, }, }, }, }, # policy prefix-list rule le { "name": "le", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \srule\s(?P\d+) \sle\s'(?P\d+)' $""", re.VERBOSE, ), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} rule {{ sequence }} le '{{ le }}'", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "entries": { "{{ sequence }}": { "sequence": "{{ sequence }}", "le": "{{ le }}", }, }, }, }, }, }, }, # policy prefix-list rule prefix { "name": "prefix", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \srule\s(?P\d+) \sprefix\s'(?P\S+)' $""", re.VERBOSE, ), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} rule {{ sequence }} prefix '{{ prefix }}'", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "entries": { "{{ sequence }}": { "sequence": "{{ sequence }}", "prefix": "{{ prefix }}", }, }, }, }, }, }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/route_maps.py b/plugins/module_utils/network/vyos/rm_templates/route_maps.py index 51d0ea1c..fd8fdd9d 100644 --- a/plugins/module_utils/network/vyos/rm_templates/route_maps.py +++ b/plugins/module_utils/network/vyos/rm_templates/route_maps.py @@ -1,1358 +1,1357 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The Route_maps parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) class Route_mapsTemplate(NetworkTemplate): def __init__(self, lines=None): prefix = {"set": "set", "remove": "delete"} super(Route_mapsTemplate, self).__init__(lines=lines, tmplt=self, prefix=prefix) # fmt: off PARSERS = [ { "name": "route_map", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "route_map", "setval": "policy route-map {{route_map}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', }, }, }, }, { "name": "sequence", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\s*$""", re.VERBOSE, ), "compval": "sequence", "setval": "policy route-map {{route_map}} rule {{sequence}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", }, }, }, }, }, }, { "name": "call", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scall\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} call {{call}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "call": "{{call}}", }, }, }, }, }, }, { "name": "description", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sdescription\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} description {{description}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "description": "{{description}}", }, }, }, }, }, }, { "name": "action", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\saction\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} action {{action}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "action": "{{action}}", }, }, }, }, }, }, { "name": "continue_sequence", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scontinue\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} continue {{continue_sequence}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "continue_sequence": "{{continue}}", }, }, }, }, }, }, { "name": "on_match_next", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\son-match\s(?Pnext)\s*$""", re.VERBOSE, ), "compval": "on_match.next", "setval": "policy route-map {{route_map}} rule {{sequence}} on-match next", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "on_match": { "next": "{{True if next is defined}}", }, }, }, }, }, }, }, { "name": "on_match_goto", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\son-match\sgoto\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "on_match.goto", "setval": "policy route-map {{route_map}} rule {{sequence}} on-match goto {{on_match.goto}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "on_match": { "goto": "{{goto}}", }, }, }, }, }, }, }, { "name": "set_aggregator_ip", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sip\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.aggregator.ip", "setval": "policy route-map {{route_map}} rule {{sequence}} set aggregator ip {{set.aggregator.ip}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "aggregator": { "ip": "{{ip}}", }, }, }, }, }, }, }, }, { "name": "set_aggregator_as", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sas\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.aggregator.as", "setval": "policy route-map {{route_map}} rule {{sequence}} set aggregator as {{set.aggregator.as}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "aggregator": { "as": "{{as}}", }, }, }, }, }, }, }, }, { "name": "set_as_path_exclude", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sas-path-exclude\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.as_path_exclude", "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path-exclude {{set.as_path_exclude}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "as_path_exclude": "{{as}}", }, }, }, }, }, }, }, { "name": "set_as_path_prepend", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sas-path-prepend\s(?P.*) $""", re.VERBOSE, ), "compval": "set.as_path_prepend", "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path-prepend '{{set.as_path_prepend}}'", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "as_path_prepend": "{{as}}", }, }, }, }, }, }, }, { "name": "set_atomic_aggregate", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\s(?Patomic-aggregate)\s*$""", re.VERBOSE, ), "compval": "set.atomic_aggregate", "setval": "policy route-map {{route_map}} rule {{sequence}} set atomic-aggregate", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "atomic_aggregate": "{{True if as is defined}}", }, }, }, }, }, }, }, { "name": "set_bgp_extcommunity_rt", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sbgp-extcommunity-rt\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.bgp_extcommunity_rt", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set bgp-extcommunity-rt {{set.bgp_extcommunity_rt}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "bgp_extcommunity_rt": "{{bgp}}", }, }, }, }, }, }, }, { "name": "set_comm_list", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\scommunity-list\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.community.community_list", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match community community-list {{set.comm_list.comm_list}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "community": {"community_list": "{{comm_list}}"}, }, }, }, }, }, }, }, { "name": "set_comm_list_delete", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\scomm-list\s(?Pdelete)\s*$""", re.VERBOSE, ), "compval": "set.comm_list.comm_list", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set comm-list delete", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "comm_list": {"delete": "{{True if delete is defined}}"}, }, }, }, }, }, }, }, { "name": "set_extcommunity_rt", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity-rt\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_rt", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set extcommunity-rt {{set.extcommunity_rt}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "extcommunity_rt": "{{extcommunity_rt}}", }, }, }, }, }, }, }, { "name": "set_extcommunity_soo", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity-soo\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_soo", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set extcommunity-soo {{set.extcommunity_soo}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "extcommunity_soo": "{{set.extcommunity_soo}}", }, }, }, }, }, }, }, { "name": "set_extcommunity_bandwidth", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\sbandwidth\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_bandwidth", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set extcommunity bandwidth {{set.extcommunity_bandwidth}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "extcommunity_bandwidth": "{{extcommunity_bw}}", }, }, }, }, }, }, }, { "name": "set_extcommunity_bandwidth_non_transitive", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+) \sset\sextcommunity\s(?Pbandwidth-non-transitive)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_bandwidth_non_transitive", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set extcommunity bandwidth-non-transitive", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "extcommunity_bandwidth_non_transitive": "{{True if extcommunity_bw_nt is defined}}", }, }, }, }, }, }, }, { "name": "set_ip_next_hop", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sip-next-hop\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.ip_next_hop", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set ip-next-hop {{set.ip_next_hop}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "ip_next_hop": "{{ip_next_hop}}", }, }, }, }, }, }, }, { "name": "set_ipv6_next_hop", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sipv6-next-hop \s(?Pglobal|local) \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.ipv6_next_hop", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set ipv6-next-hop {{set.ipv6_next_hop.ip_type}} {{set.ipv6_next_hop.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "ipv6_next_hop": { "ip_type": "{{type}}", "value": "{{value}}", }, }, }, }, }, }, }, }, { "name": "set_large_community", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\slarge-community\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.large_community", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set large-community {{set.large_community}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "large_community": "{{large_community}}", }, }, }, }, }, }, }, { "name": "set_local_preference", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\slocal-preference\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.local_preference", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set local-preference {{set.local_preference}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "local_preference": "{{local_preference}}", }, }, }, }, }, }, }, { "name": "set_metric", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.metric", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set metric {{set.metric}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "metric": "{{metric}}", }, }, }, }, }, }, }, { "name": "set_metric_type", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric-type\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.metric_type", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set metric-type {{set.metric_type}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "metric_type": "{{metric_type}}", }, }, }, }, }, }, }, { "name": "set_origin", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sorigin\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.origin", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set origin {{set.origin}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "origin": "{{origin}}", }, }, }, }, }, }, }, { "name": "set_originator_id", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\soriginator-id\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.originator_id", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set originator-id {{set.originator_id}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "originator_id": "{{originator_id}}", }, }, }, }, }, }, }, { "name": "set_src", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\ssrc\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.src", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set src {{set.src}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "src": "{{src}}", }, }, }, }, }, }, }, { "name": "set_tag", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\stag\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.tag", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set tag {{set.tag}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "tag": "{{tag}}", }, }, }, }, }, }, }, { "name": "set_weight", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sweight\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.weight", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set weight {{set.weight}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "weight": "{{weight}}", }, }, }, }, }, }, }, { "name": "set_table", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\stable\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.weight", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set table {{set.table}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "table": "{{table}}", }, }, }, }, }, }, }, { "name": "set_community", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\scommunity\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.community.value", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set community {{set.community.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "community": { "value": "{{value}}", }, }, }, }, }, }, }, }, { "name": "match_as_path", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sas-path\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.as_path", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match as-path {{match.as_path}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "as_path": "{{as_path}}", }, }, }, }, }, }, }, { "name": "match_community_community_list", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\scommunity-list\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.community.community_list", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match community community-list {{match.community.community_list}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "community": {"community_list": "{{community_list}}"}, }, }, }, }, }, }, }, { "name": "match_community_exact_match", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\sexact-match(?P)\s*$""", re.VERBOSE, ), "compval": "match.community.exact_match", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match community exact-match", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "community": {"exact_match": "{{True if exact_match is defined}}"}, }, }, }, }, }, }, }, { "name": "match_extcommunity", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sextcommunity\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.extcommunity", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match extcommunity {{match.extcommunity}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "extcommunity": "{{extcommunity}}", }, }, }, }, }, }, }, { "name": "match_interface", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sinterface\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.interface", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match interface {{match.interface}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "interface": "{{interface}}", }, }, }, }, }, }, }, { "name": "match_large_community_large_community_list", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\slarge-community\slarge-community-list\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.large_community_large_community_list", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match large-community large-community-list {{match.large_community_large_community_list}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "large_community_large_community_list": "{{lc}}", }, }, }, }, }, }, }, { "name": "match_metric", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\smetric\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.metric", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match metric {{match.metric}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "metric": "{{metric}}", }, }, }, }, }, }, }, { "name": "match_origin", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sorigin\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.origin", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match origin {{match.origin}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "origin": "{{origin}}", }, }, }, }, }, }, }, { "name": "match_peer", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\speer\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.peer", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match peer {{match.peer}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "peer": "{{peer}}", }, }, }, }, }, }, }, { "name": "match_ip_address", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\saddress \s(?Paccess-list|prefix-list) \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.address", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ip address {{match.ip.address.list_type}} {{match.ip.address.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ip": { "address": { "list_type": "{{list_type}}", "value": "{{value}}", }, }, }, }, }, }, }, }, }, { "name": "match_ip_next_hop", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\snexthop \s(?Paccess-list|prefix-list) \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.next_hop", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ip nexthop {{match.ip.next_hop.list_type}} {{match.ip.next_hop.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ip": { "next_hop": { "list_type": "{{list_type}}", "value": "{{value}}", }, }, }, }, }, }, }, }, }, { "name": "match_ip_route_source", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\sroute-source \s(?Paccess-list|prefix-list) \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.route_source", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ip route-source {{match.ip.route_source.list_type}} {{match.ip.route_source.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ip": { "route_source": { "list_type": "{{list_type}}", "value": "{{value}}", }, }, }, }, }, }, }, }, }, { "name": "match_ipv6_address", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sipv6\saddress \s(?Paccess-list|prefix-list) \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ipv6.address", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ipv6 address {{match.ipv6.address.list_type}} {{match.ipv6.address.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ipv6": { "address": { "list_type": "{{list_type}}", "value": "{{value}}", }, }, }, }, }, }, }, }, }, { "name": "match_ipv6_nexthop", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sipv6\snexthop \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ipv6.next_hop", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ipv6 nexthop {{match.ipv6.next_hop}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ipv6": { "next_hop": "{{value}}", }, }, }, }, }, }, }, }, { "name": "match_protocol", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sprotocol\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.protocol", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match protocol {{match.protocol}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "protocol": "{{value}}", }, }, }, }, }, }, }, { "name": "match_rpki", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\srpki \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.rpki", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match rpki {{match.rpki}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "rpki": "{{value}}", }, }, }, }, }, }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py b/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py index ea61a555..cf2d6b67 100644 --- a/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py +++ b/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py @@ -1,1364 +1,1363 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The Route_maps parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) class Route_mapsTemplate14(NetworkTemplate): def __init__(self, lines=None): prefix = {"set": "set", "remove": "delete"} super(Route_mapsTemplate14, self).__init__(lines=lines, tmplt=self, prefix=prefix) # fmt: off PARSERS = [ { "name": "route_map", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "route_map", "setval": "policy route-map {{route_map}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', }, }, }, }, { "name": "sequence", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+) *$""", re.VERBOSE, ), "compval": "sequence", "setval": "policy route-map {{route_map}} rule {{sequence}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", }, }, }, }, }, }, { "name": "call", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scall\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} call {{call}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "call": "{{call}}", }, }, }, }, }, }, { "name": "description", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sdescription\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} description {{description}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "description": "{{description}}", }, }, }, }, }, }, { "name": "action", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\saction\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} action {{action}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "action": "{{action}}", }, }, }, }, }, }, { "name": "continue_sequence", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scontinue\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} continue {{continue_sequence}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "continue_sequence": "{{continue}}", }, }, }, }, }, }, { "name": "on_match_next", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\son-match\s(?Pnext) *$""", re.VERBOSE, ), "compval": "on_match.next", "setval": "policy route-map {{route_map}} rule {{sequence}} on-match next", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "on_match": { "next": "{{True if next is defined}}", }, }, }, }, }, }, }, { "name": "on_match_goto", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\son-match\sgoto\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "on_match.goto", "setval": "policy route-map {{route_map}} rule {{sequence}} on-match goto {{on_match.goto}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "on_match": { "goto": "{{goto}}", }, }, }, }, }, }, }, { "name": "set_aggregator_ip", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sip\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.aggregator.ip", "setval": "policy route-map {{route_map}} rule {{sequence}} set aggregator ip {{set.aggregator.ip}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "aggregator": { "ip": "{{ip}}", }, }, }, }, }, }, }, }, { "name": "set_aggregator_as", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sas\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.aggregator.as", "setval": "policy route-map {{route_map}} rule {{sequence}} set aggregator as {{set.aggregator.as}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "aggregator": { "as": "{{as}}", }, }, }, }, }, }, }, }, { "name": "set_as_path_exclude", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sas-path\sexclude\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.as_path_exclude", "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path exclude {{set.as_path_exclude}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "as_path_exclude": "{{as}}", }, }, }, }, }, }, }, { "name": "set_as_path_prepend", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sas-path\sprepend\s(?P.*) $""", re.VERBOSE, ), "compval": "set.as_path_prepend", "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path prepend '{{set.as_path_prepend}}'", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "as_path_prepend": "{{as}}", }, }, }, }, }, }, }, { "name": "set_atomic_aggregate", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\s(?Patomic-aggregate) *$""", re.VERBOSE, ), "compval": "set.atomic_aggregate", "setval": "policy route-map {{route_map}} rule {{sequence}} set atomic-aggregate", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "atomic_aggregate": "{{True if as is defined}}", }, }, }, }, }, }, }, { "name": "set_bgp_extcommunity_rt", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sbgp-extcommunity-rt\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.bgp_extcommunity_rt", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set bgp-extcommunity-rt {{set.bgp_extcommunity_rt}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "bgp_extcommunity_rt": "{{bgp}}", }, }, }, }, }, }, }, { "name": "set_comm_list", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\scommunity-list\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.community.community_list", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match community community-list {{set.comm_list.comm_list}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "community": {"community_list": "{{comm_list}}"}, }, }, }, }, }, }, }, { "name": "set_comm_list_delete", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\scomm-list\s(?Pdelete)\s*$""", re.VERBOSE, ), "compval": "set.comm_list.comm_list", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set comm-list delete", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "comm_list": {"delete": "{{True if delete is defined}}"}, }, }, }, }, }, }, }, { "name": "set_extcommunity_rt", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\srt\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_rt", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set extcommunity rt {{set.extcommunity_rt}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "extcommunity_rt": "{{extcommunity_rt}}", }, }, }, }, }, }, }, { "name": "set_extcommunity_soo", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\ssoo\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_soo", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set extcommunity soo {{set.extcommunity_soo}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "extcommunity_soo": "{{extcommunity_soo}}", }, }, }, }, }, }, }, { "name": "set_extcommunity_bandwidth", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\sbandwidth\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_bandwidth", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set extcommunity bandwidth {{set.extcommunity_bandwidth}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "extcommunity_bandwidth": "{{extcommunity_bw}}", }, }, }, }, }, }, }, { "name": "set_extcommunity_bandwidth_non_transitive", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\s(?Pbandwidth-non-transitive) *$""", re.VERBOSE, ), "compval": "set.extcommunity_bandwidth_non_transitive", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set extcommunity bandwidth-non-transitive", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "extcommunity_bandwidth_non_transitive": "{{True if extcommunity_bw_nt is defined}}", }, }, }, }, }, }, }, { "name": "set_ip_next_hop", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sip-next-hop\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.ip_next_hop", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set ip-next-hop {{set.ip_next_hop}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "ip_next_hop": "{{ip_next_hop}}", }, }, }, }, }, }, }, { "name": "set_ipv6_next_hop", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sipv6-next-hop \s(?Pglobal|local) \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.ipv6_next_hop", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set ipv6-next-hop {{set.ipv6_next_hop.ip_type}} {{set.ipv6_next_hop.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "ipv6_next_hop": { "ip_type": "{{type}}", "value": "{{value}}", }, }, }, }, }, }, }, }, { "name": "set_large_community", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\slarge-community\s(?Pnone|replace\s(?P\S+)) $""", re.VERBOSE, ), "compval": "set.large_community", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set large-community {{set.large_community if set.large_community == 'none' else 'replace ' + set.large_community}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "large_community": "{{op if op == 'none' else large_community}}", }, }, }, }, }, }, }, { "name": "set_local_preference", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\slocal-preference\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.local_preference", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set local-preference {{set.local_preference}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "local_preference": "{{local_preference}}", }, }, }, }, }, }, }, { "name": "set_metric", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.metric", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set metric {{set.metric}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "metric": "{{metric}}", }, }, }, }, }, }, }, { "name": "set_metric_type", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric-type\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.metric_type", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set metric-type {{set.metric_type}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "metric_type": "{{metric_type}}", }, }, }, }, }, }, }, { "name": "set_origin", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sorigin\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.origin", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set origin {{set.origin}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "origin": "{{origin}}", }, }, }, }, }, }, }, { "name": "set_originator_id", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\soriginator-id\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.originator_id", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set originator-id {{set.originator_id}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "originator_id": "{{originator_id}}", }, }, }, }, }, }, }, { "name": "set_src", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\ssrc\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.src", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set src {{set.src}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "src": "{{src}}", }, }, }, }, }, }, }, { "name": "set_tag", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\stag\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.tag", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set tag {{set.tag}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "tag": "{{tag}}", }, }, }, }, }, }, }, { "name": "set_weight", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sweight\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.weight", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set weight {{set.weight}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "weight": "{{weight}}", }, }, }, }, }, }, }, { "name": "set_table", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\stable\s(?P
\S+)\s*$""", re.VERBOSE, ), "compval": "set.weight", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set table {{set.table}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "table": "{{table}}", }, }, }, }, }, }, }, { "name": "set_community", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\scommunity\s(?Pnone|replace\s(?P\S+)) $""", re.VERBOSE, ), "compval": "set.community.value", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set community {{set.community.value if set.community.value == 'none' else 'replace ' + set.community.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "community": { "value": "{{op if op == 'none' else value}}", }, }, }, }, }, }, }, }, { "name": "match_as_path", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sas-path\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.as_path", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match as-path {{match.as_path}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "as_path": "{{as_path}}", }, }, }, }, }, }, }, { "name": "match_community_community_list", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\scommunity-list\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.community.community_list", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match community community-list {{match.community.community_list}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "community": {"community_list": "{{community_list}}"}, }, }, }, }, }, }, }, { "name": "match_community_exact_match", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\sexact-match(?P) *$""", re.VERBOSE, ), "compval": "match.community.exact_match", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match community exact-match", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "community": {"exact_match": "{{True if exact_match is defined}}"}, }, }, }, }, }, }, }, { "name": "match_extcommunity", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sextcommunity\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.extcommunity", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match extcommunity {{match.extcommunity}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "extcommunity": "{{extcommunity}}", }, }, }, }, }, }, }, { "name": "match_interface", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sinterface\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.interface", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match interface {{match.interface}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "interface": "{{interface}}", }, }, }, }, }, }, }, { "name": "match_large_community_large_community_list", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\slarge-community\slarge-community-list\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.large_community_large_community_list", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match large-community large-community-list {{match.large_community_large_community_list}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "large_community_large_community_list": "{{lc}}", }, }, }, }, }, }, }, { "name": "match_metric", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\smetric\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.metric", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match metric {{match.metric}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "metric": "{{metric}}", }, }, }, }, }, }, }, { "name": "match_origin", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sorigin\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.origin", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match origin {{match.origin}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "origin": "{{origin}}", }, }, }, }, }, }, }, { "name": "match_peer", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\speer\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.peer", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match peer {{match.peer}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "peer": "{{peer}}", }, }, }, }, }, }, }, { "name": "match_ip_address", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\saddress \s(?Paccess-list|prefix-list) \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.address", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ip address {{match.ip.address.list_type}} {{match.ip.address.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ip": { "address": { "list_type": "{{list_type}}", "value": "{{value}}", }, }, }, }, }, }, }, }, }, { "name": "match_ip_next_hop", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\snexthop \s(?Paccess-list|prefix-list) \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.next_hop", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ip nexthop {{match.ip.next_hop.list_type}} {{match.ip.next_hop.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ip": { "next_hop": { "list_type": "{{list_type}}", "value": "{{value}}", }, }, }, }, }, }, }, }, }, { "name": "match_ip_route_source", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\sroute-source \s(?Paccess-list|prefix-list) \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.route_source", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ip route-source {{match.ip.route_source.list_type}} {{match.ip.route_source.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ip": { "route_source": { "list_type": "{{list_type}}", "value": "{{value}}", }, }, }, }, }, }, }, }, }, { "name": "match_ipv6_address", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sipv6\saddress \s(?Paccess-list|prefix-list) \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ipv6.address", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ipv6 address {{match.ipv6.address.list_type}} {{match.ipv6.address.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ipv6": { "address": { "list_type": "{{list_type}}", "value": "{{value}}", }, }, }, }, }, }, }, }, }, { "name": "match_ipv6_nexthop", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sipv6\snexthop \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ipv6.next_hop", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ipv6 nexthop {{match.ipv6.next_hop}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ipv6": { "next_hop": "{{value}}", }, }, }, }, }, }, }, }, { "name": "match_protocol", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sprotocol\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.protocol", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match protocol {{match.protocol}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "protocol": "{{value}}", }, }, }, }, }, }, }, { "name": "match_rpki", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\srpki \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.rpki", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match rpki {{match.rpki}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "rpki": "{{value}}", }, }, }, }, }, }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/snmp_server.py b/plugins/module_utils/network/vyos/rm_templates/snmp_server.py index ff1c6465..bd76a5ae 100644 --- a/plugins/module_utils/network/vyos/rm_templates/snmp_server.py +++ b/plugins/module_utils/network/vyos/rm_templates/snmp_server.py @@ -1,602 +1,601 @@ # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The Snmp_server parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) def _tmplt_snmp_server_communities(config_data): config_data = config_data["communities"] command = [] cmd = "service snmp community {name}".format(**config_data) if "authorization_type" in config_data: auth_cmd = cmd + " authorization {authorization_type}".format(**config_data) command.append(auth_cmd) if "clients" in config_data: for c in config_data["clients"]: client_cmd = cmd + " client " + c command.append(client_cmd) if "networks" in config_data: for n in config_data["networks"]: network_command = cmd + " network " + n command.append(network_command) if not command: command.append(cmd) return command def _tmplt_snmp_server_trap_target(config_data): config_data = config_data["trap_target"] command = "service snmp trap-target {address}".format(**config_data) if "authorization_type" in config_data: command += " authorization {authorization_type}".format(**config_data) if "client" in config_data: command += " client {client}".format(**config_data) if "network" in config_data: command += " network {network}".format(**config_data) return command def _tmplt_snmp_server_v3_groups(config_data): config_data = config_data["snmp_v3"]["groups"] command = [] cmd = "service snmp v3 group {group}".format(**config_data) if "mode" in config_data: mode_cmd = cmd + " mode {mode}".format(**config_data) command.append(mode_cmd) if "seclevel" in config_data: sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) command.append(sec_cmd) if "view" in config_data: view_cmd = cmd + " view {view}".format(**config_data) command.append(view_cmd) return command def _tmplt_snmp_server_v3_trap_target(config_data): config_data = config_data["snmp_v3"]["trap_targets"] command = "service snmp v3 trap-target {address} ".format(**config_data) if "authentication" in config_data: command += " auth" config_data = config_data["authentication"] if "privacy" in config_data: command += " privacy" config_data = config_data["privacy"] if "type" in config_data: command += " type {mode}".format(**config_data) if "encrypted_key" in config_data: command += " encrypted-password {encrypted_key}".format(**config_data) if "plaintext_key" in config_data: command += " plaintext-password {plaintext_key}".format(**config_data) return command def _tmplt_snmp_server_v3_user(config_data): config_data = config_data["snmp_v3"]["users"] command = [] cmd = "service snmp v3 user {user}".format(**config_data) for k in ["authentication", "privacy"]: if config_data.get(k): config = config_data[k] if k == "authentication": val = " auth" else: val = " privacy" if "type" in config: type_cmd = cmd + val + " type {type}".format(**config) command.append(type_cmd) if "encrypted_key" in config: enc_cmd = cmd + val + " encrypted-password {encrypted_key}".format(**config) command.append(enc_cmd) if "plaintext_key" in config: plain_cmd = cmd + val + " plaintext-password {plaintext_key}".format(**config) command.append(plain_cmd) return command def _tmplt_snmp_server_v3_views(config_data): config_data = config_data["snmp_v3"]["views"] command = "service snmp v3 view {view} oid {oid}".format(**config_data) if "exclude" in config_data: command += " exclude {exclude}".format(**config_data) if "mask" in config_data: command += " mask {mask}".format(**config_data) return command class Snmp_serverTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Snmp_serverTemplate, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module, ) # fmt: off PARSERS = [ # service snmp community <> { "name": "communities", "getval": re.compile( r""" ^set\sservice\ssnmp\scommunity \s+(?P\S+) \s*(?Pauthorization\srw|authorization\sro)? \s*(client\s(?P\S+))? \s*(network\s(?P\S+))? $""", re.VERBOSE, ), "setval": _tmplt_snmp_server_communities, "result": { "communities": { "{{ name }}": { "name": "{{ name }}", "clients": ['{{ client if client is defined else "None" }}'], "networks": ['{{ network if network is defined else "None" }}'], "authorization_type": '{{ auth.split(" ")[1] if auth is defined else None }}', }, }, }, }, # service snmp contact <> { "name": "contact", "getval": re.compile( r""" ^set\sservice\ssnmp\scontact \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp contact {{ contact }}", "result": { "contact": "{{ name }}", }, }, # service snmp description <> { "name": "description", "getval": re.compile( r""" ^set\sservice\ssnmp\sdescription \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp description {{ description }}", "result": { "description": "{{ name }}", }, }, # service snmp listen-address <> port <> { "name": "listen_addresses", "getval": re.compile( r""" ^set\sservice\ssnmp\slisten-address \s+(?P\S+) \s*(port)? \s*(?P\d+)? $""", re.VERBOSE, ), "setval": "service snmp listen-address {{ listen_addresses.address }}" "{{ (' port ' + listen_addresses.port|string) if listen_addresses.port is defined else '' }}", "result": { "listen_addresses": { "{{ addr }}": { "address": "{{ addr }}", "port": "{{ port }}", }, }, }, }, # service snmp location <> { "name": "location", "getval": re.compile( r""" ^set\sservice\ssnmp\slocation \s(?P.*) $""", re.VERBOSE, ), "setval": "service snmp location {{ '\\'' + location + '\\''}}", "result": { "location": "{{ name }}", }, }, # service snmp smux-peer <> { "name": "smux_peer", "getval": re.compile( r""" ^set\sservice\ssnmp\ssmux-peer \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp smux-peer {{ smux_peer }}", "result": { "smux_peer": "{{ name }}", }, }, # service snmp trap-source <> { "name": "trap_source", "getval": re.compile( r""" ^set\sservice\ssnmp\strap-source \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp trap-source {{ trap_source }}", "result": { "trap_source": "{{ name }}", }, }, # service snmp trap-target <> { "name": "trap_target", "getval": re.compile( r""" ^set\sservice\ssnmp\strap-target \s+(?P\S+) \s*(?Pcommunity\s\S+)? \s*(?Pport\s\d+)? $""", re.VERBOSE, ), "setval": _tmplt_snmp_server_trap_target, "result": { "trap_target": { "address": "{{ name }}", "community": "{{ comm.split(" ")[1] if comm is defined else None }}", "port": "{{ port.split(" ")[1] if port is defined else None }}", }, }, }, # service snmp v3 engineid <> { "name": "snmp_v3.engine_id", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\sengineid \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp v3 engineid {{ snmp_v3.engine_id }}", "result": { "snmp_v3": { "engine_id": "{{ name }}", }, }, }, # service snmp v3 group <> { "name": "snmp_v3.groups", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\sgroup \s+(?P\S+) \s*(?Pmode\s\S+)? \s*(?Pseclevel\s\S+)? \s*(?Pview\s\S+)? $""", re.VERBOSE, ), "setval": _tmplt_snmp_server_v3_groups, "result": { "snmp_v3": { "groups": { "{{ name }}": { "group": "{{ name }}", "mode": '{{ mode.split(" ")[1] if mode is defined else None }}', "seclevel": '{{ sec.split(" ")[1] if sec is defined else None }}', "view": '{{ view.split(" ")[1] if view is defined else None }}', }, }, }, }, }, # service snmp v3 trap-target <> auth <> { "name": "snmp_v3.trap_targets.authentication", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+auth \s*(?Pencrypted-password\s\S+)? \s*(?Pplaintext-password\s\S+)? \s*(?Ptype\s\S+)? $""", re.VERBOSE, ), "setval": _tmplt_snmp_server_v3_trap_target, "result": { "snmp_v3": { "trap_targets": { "{{ name }}": { "address": "{{ name }}", "authentication": { "encrypted_key": '{{ enc.split(" ")[1] if enc is defined else None }}', "plaintext_key": '{{ plain.split(" ")[1] if plain is defined else None }}', "type": '{{ type.split(" ")[1] if type is defined else None }}', }, }, }, }, }, }, # service snmp v3 trap-target <> port <> { "name": "snmp_v3.trap_targets.port", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+(?Pport\s\d+)? $""", re.VERBOSE, ), "setval": "service snmp v3 trap-target port {{ snmp_v3.trap_targets.port }}", "result": { "snmp_v3": { "trap_targets": { "{{ name }}": { "address": "{{ name }}", "port": "{{ port }}", }, }, }, }, }, # service snmp v3 trap-target <> protocol <> { "name": "snmp_v3.trap_targets.protocol", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+(?Pprotocol\s\S+)? $""", re.VERBOSE, ), "setval": "service snmp v3 trap-target protocol {{ snmp_v3.trap_targets.protocol }}", "result": { "snmp_v3": { "trap_targets": { "{{ name }}": { "address": "{{ name }}", "protocol": "{{ protocol }}", }, }, }, }, }, # service snmp v3 trap-target <> type <> { "name": "snmp_v3.trap_targets.type", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+(?Ptype\s\S+)? $""", re.VERBOSE, ), "setval": "service snmp v3 trap-target type {{ snmp_v3.trap_targets.type }}", "result": { "snmp_v3": { "trap_targets": { "{{ name }}": { "address": "{{ name }}", "type": "{{ type }}", }, }, }, }, }, # service snmp v3 trap-target <> user <> { "name": "snmp_v3.trap_targets.user", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+(?Puser\s\S+)? $""", re.VERBOSE, ), "setval": "service snmp v3 trap-target user {{ snmp_v3.trap_targets.user }}", "result": { "snmp_v3": { "trap_targets": { "{{ name }}": { "address": "{{ name }}", "user": "{{ user }}", }, }, }, }, }, # service snmp v3 trap-target <> privacy <> { "name": "snmp_v3.trap_targets.privacy", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+privacy \s*(?Pencrypted-password\s\S+)? \s*(?Pplaintext-password\s\S+)? \s*(?Ptype\s\S+)? $""", re.VERBOSE, ), "setval": _tmplt_snmp_server_v3_trap_target, "result": { "snmp_v3": { "trap_targets": { "{{ name }}": { "address": "{{ name }}", "privacy": { "encrypted_key": '{{ enc.split(" ")[1] if enc is defined else None }}', "plaintext_key": '{{ plain.split(" ")[1] if plain is defined else None }}', "type": '{{ type.split(" ")[1] if type is defined else None }}', }, }, }, }, }, }, # service snmp v3 user <> auth <> { "name": "snmp_v3.users.authentication", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\suser \s+(?P\S+) \s+auth \s*(?Pencrypted-password\s\S+)? \s*(?Pplaintext-password\s\S+)? \s*(?Ptype\s\S+)? $""", re.VERBOSE, ), "setval": _tmplt_snmp_server_v3_user, "result": { "snmp_v3": { "users": { "{{ name }}": { "user": "{{ name }}", "authentication": { "encrypted_key": '{{ enc.split(" ")[1] if enc is defined else None }}', "plaintext_key": '{{ plain.split(" ")[1] if plain is defined else None }}', "type": '{{ type.split(" ")[1] if type is defined else None }}', }, }, }, }, }, }, # service snmp v3 user <> privacy <> { "name": "snmp_v3.users.privacy", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\suser \s+(?P\S+) \s+privacy \s*(?Pencrypted-password\s\S+)? \s*(?Pplaintext-password\s\S+)? \s*(?Ptype\s\S+)? $""", re.VERBOSE, ), "setval": _tmplt_snmp_server_v3_user, "result": { "snmp_v3": { "users": { "{{ name }}": { "user": "{{ name }}", "privacy": { "encrypted_key": '{{ enc.split(" ")[1] if enc is defined else None }}', "plaintext_key": '{{ plain.split(" ")[1] if plain is defined else None }}', "type": '{{ type.split(" ")[1] if type is defined else None }}', }, }, }, }, }, }, # service snmp v3 user <> group <> { "name": "snmp_v3.users.group", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\suser \s+(?P\S+) \s+(?Pgroup\s.+)? $""", re.VERBOSE, ), "setval": "service snmp v3 user {{ snmp_v3.users.user }} group {{ snmp_v3.users.group }}", "result": { "snmp_v3": { "users": { "{{ name }}": { "user": "{{ name }}", "group": "{{ group.split(" ")[1] if group is defined else None }}", }, }, }, }, }, # service snmp v3 user <> mode <> { "name": "snmp_v3.users.mode", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\suser \s+(?P\S+) \s+(?Pmode\s\S+)? $""", re.VERBOSE, ), "setval": "service snmp v3 user {{ snmp_v3.users.user }} mode {{ snmp_v3.users.mode }}", "result": { "snmp_v3": { "users": { "{{ name }}": { "user": "{{ name }}", "mode": "{{ mode }}", }, }, }, }, }, # service snmp v3 view <> { "name": "snmp_v3.views", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\sview \s+(?P\S+) \s+(?Poid\s\S+) \s*(?Pexclude\s\S+)? \s*(?Pmask\s\S+)? $""", re.VERBOSE, ), "setval": _tmplt_snmp_server_v3_views, "result": { "snmp_v3": { "views": { "{{ name }}": { "view": "{{ name }}", "oid": '{{ oid.split(" ")[1] if oid is defined else None }}', "exclude": '{{ ex.split(" ")[1] if ex is defined else None }}', "mask": '{{ mask.split(" ")[1] if mask is defined else None }}', }, }, }, }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/vrf.py b/plugins/module_utils/network/vyos/rm_templates/vrf.py index 5171404e..79928547 100644 --- a/plugins/module_utils/network/vyos/rm_templates/vrf.py +++ b/plugins/module_utils/network/vyos/rm_templates/vrf.py @@ -1,249 +1,248 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The VRF parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) class VrfTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(VrfTemplate, self).__init__(lines=lines, tmplt=self, prefix=prefix, module=module) # fmt: off PARSERS = [ { "name": "table_id", "getval": re.compile( r""" ^set \s+vrf \s+name \s+(?P\S+) \s+table \s+'(?P\S+)' $""", re.VERBOSE, ), "setval": "vrf name {{ name }} table {{ table_id }}", "result": { "name": "{{ name }}", "table_id": "{{ tid }}", }, }, { "name": "bind_to_all", "getval": re.compile( r""" ^set \svrf \s(?Pbind-to-all) $""", re.VERBOSE, ), "setval": "vrf bind-to-all", "result": { "bind_to_all": "{{ True if bta is defined }}", }, }, { "name": "vni", "getval": re.compile( r""" ^set \s+vrf \s+name \s+(?P\S+) \s+vni \s'(?P\S+)' $""", re.VERBOSE, ), "setval": "vrf name {{name}} vni {{vni}}", "result": { "name": "{{ name }}", "vni": "{{ vni }}", }, }, { "name": "description", "getval": re.compile( r""" ^set \svrf \sname \s(?P\S+) \sdescription \s(?P\S+) $""", re.VERBOSE, ), "setval": "vrf name {{name}} description {{description}}", "result": { "name": "{{ name }}", "description": "{{ desc }}", }, }, { "name": "disable_vrf", "getval": re.compile( r""" ^set \svrf \sname \s(?P\S+) \s(?Pdisable) $""", re.VERBOSE, ), "setval": "vrf name {{name}} disable", "compval": "disable", "result": { "name": "{{ name }}", "disable": "{{ True if disable is defined }}", }, }, # { # "name": "address_family", # "getval": re.compile( # r""" # ^set # \svrf # \sname # \s(?P\S+) # \s(?Pip|ipv6) # $""", # re.VERBOSE, # ), # "setval": "vrf name {{name}} {{ af }}", # 'compval': "address_family", # "result": { # "name": "{{ name }}", # "address_family": { # '{{ "ipv4" if af == "ip" else "ipv6" }}': { # "afi": '{{ "ipv4" if af == "ip" else "ipv6" }}', # }, # }, # }, # }, # { # "name": "address_family.disable_forwarding", # "getval": re.compile( # r""" # ^set # \svrf # \sname # \s(?P\S+) # \s(?Pip|ipv6) # \s(?Pdisable-forwarding) # $""", # re.VERBOSE, # ), # "setval": "vrf name {{name}} {{ afi }} disable-forwarding", # # "compval": "address_family.ipv6.disable_forwarding", # "result": { # "name": "{{ name }}", # "address_family": { # '{{ "ipv4" if af == "ip" else "ipv6" }}': { # "afi": '{{ "ipv4" if af == "ip" else "ipv6" }}', # "disable_forwarding": "{{ True if df is defined }}", # }, # }, # }, # }, { "name": "disable_forwarding", "getval": re.compile( r""" ^set \svrf \sname \s(?P\S+) \s(?Pip|ipv6) \s(?Pdisable-forwarding) $""", re.VERBOSE, ), "setval": "vrf name {{name}} {{ afi }} disable-forwarding", "compval": "disable_forwarding", "result": { "name": "{{ name }}", 'address_family': [{ "afi": '{{ "ipv4" if af == "ip" else "ipv6" }}', "disable_forwarding": "{{ True if df is defined }}", }], }, }, { "name": "disable_nht", "getval": re.compile( r""" ^set \svrf \sname \s(?P\S+) \s(?Pip|ipv6) \snht \s(?Pno-resolve-via-default) $""", re.VERBOSE, ), "setval": "vrf name {{name}} {{ afi }} nht no-resolve-via-default", "compval": "nht_no_resolve_via_default", "result": { "name": "{{ name }}", "address_family": [{ "afi": '{{ "ipv4" if af == "ip" else "ipv6" }}', "nht_no_resolve_via_default": "{{ True if nht is defined }}", }], }, }, { "name": "route_maps", "getval": re.compile( r""" ^set \svrf \sname \s(?P\S+) \s(?Pip|ipv6) \sprotocol \s(?P\S+) \sroute-map \s'(?P\S+)' $""", re.VERBOSE, ), "setval": "vrf name {{name}} {{ afi }} protocol {{ route_maps.protocol }} route-map {{ route_maps.rm_name }}", "compval": "route_maps", "remval": "vrf name {{name}} {{ afi }} protocol {{ route_maps.protocol }}", "result": { "name": "{{ name }}", "address_family": [{ "afi": '{{ "ipv4" if af == "ip" else "ipv6" }}', "route_maps": [{ "rm_name": "{{ rm }}", "protocol": "{{ proto }}", }], }], }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py index 78445c02..5030e22e 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -1,309 +1,307 @@ # -*- 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 from ansible.module_utils.basic import missing_required_lib - try: import ipaddress HAS_IPADDRESS = True except ImportError: HAS_IPADDRESS = False 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" elif interface.startswith("vtun"): return "openvpn" elif interface.startswith("wg"): return "wireguard" elif interface.startswith("tun"): return "tunnel" elif interface.startswith("br"): return "bridge" elif interface.startswith("dum"): return "dummy" def get_interface_with_vif(interface): """Gets virtual interface if any or return as is""" vlan = None interface_real = interface if "." in interface: interface_real, vlan = interface.split(".") if vlan is not None: interface_real = interface_real + " vif " + vlan return interface_real 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 want_dict.items(): 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: """ if not HAS_IPADDRESS: raise Exception(missing_required_lib("ipaddress")) 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 functi 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 def in_target_not_none(h, key): """ This function checks whether the target exist,key present in target config, and the value is not None. :param h: target config. :param key: attribute name. :return: True/False. """ return True if h and key in h and h[key] is not None else False def combine(a, b, recursive=False, list_merge="replace"): """ Merge two dictionaries (shallow or deep). :param a: dict :param b: dict :param recursive: bool, deep merge :param list_merge: str, only 'replace' is supported (default Ansible behavior) """ if not isinstance(a, dict) or not isinstance(b, dict): raise ValueError("combine expects two dictionaries") result = a.copy() for k, v in b.items(): if recursive and k in result and isinstance(result[k], dict) and isinstance(v, dict): result[k] = combine(result[k], v, recursive=True, list_merge=list_merge) else: result[k] = v return result diff --git a/plugins/module_utils/network/vyos/vyos.py b/plugins/module_utils/network/vyos/vyos.py index 4987d6b0..92c0f1b8 100644 --- a/plugins/module_utils/network/vyos/vyos.py +++ b/plugins/module_utils/network/vyos/vyos.py @@ -1,116 +1,114 @@ # This code is part of Ansible, but is an independent component. # This particular file snippet, and this file snippet only, is BSD licensed. # Modules you write using this snippet, which is embedded dynamically by Ansible # still belong to the author of the module, and may assign their own license # to the complete work. # # (c) 2016 Red Hat Inc. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # from __future__ import absolute_import, division, print_function - __metaclass__ = type import json from ansible.module_utils._text import to_text from ansible.module_utils.connection import Connection, ConnectionError - _DEVICE_CONFIGS = {} def get_connection(module): if hasattr(module, "_vyos_connection"): return module._vyos_connection capabilities = get_capabilities(module) network_api = capabilities.get("network_api") if network_api == "cliconf": module._vyos_connection = Connection(module._socket_path) else: module.fail_json(msg="Invalid connection type %s" % network_api) return module._vyos_connection def get_capabilities(module): if hasattr(module, "_vyos_capabilities"): return module._vyos_capabilities try: capabilities = Connection(module._socket_path).get_capabilities() except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) module._vyos_capabilities = json.loads(capabilities) return module._vyos_capabilities def get_config(module, flags=None, format=None): flags = [] if flags is None else flags global _DEVICE_CONFIGS # If _DEVICE_CONFIGS is non-empty and module.params["match"] is "none", # return the cached device configurations. This avoids redundant calls # to the connection when no specific match criteria are provided. if _DEVICE_CONFIGS != {} and ( module.params["match"] is not None and module.params["match"] == "none" ): return to_text(_DEVICE_CONFIGS) else: connection = get_connection(module) try: out = connection.get_config(flags=flags, format=format) except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) cfg = to_text(out, errors="surrogate_then_replace").strip() _DEVICE_CONFIGS = cfg return cfg def run_commands(module, commands, check_rc=True): connection = get_connection(module) try: response = connection.run_commands(commands=commands, check_rc=check_rc) except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) return response def load_config(module, commands, commit=False, comment=None, confirm=None): connection = get_connection(module) try: response = connection.edit_config( candidate=commands, commit=commit, comment=comment, confirm=confirm ) except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) return response.get("diff") def get_os_version(module): connection = get_connection(module) if connection.get_device_info(): os_version = connection.get_device_info()["network_os_major_version"] return os_version diff --git a/plugins/modules/vyos_banner.py b/plugins/modules/vyos_banner.py index c6598023..98a5b0ba 100644 --- a/plugins/modules/vyos_banner.py +++ b/plugins/modules/vyos_banner.py @@ -1,187 +1,186 @@ #!/usr/bin/python # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function - __metaclass__ = type # (c) 2017, Ansible by Red Hat, inc # # This file is part of Ansible by Red Hat # # 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 . # DOCUMENTATION = """ module: vyos_banner author: Trishna Guha (@trishnaguha) short_description: Manage multiline banners on VyOS devices description: - This will configure both pre-login and post-login banners on remote devices running VyOS. It allows playbooks to add or remote banner text from the active running configuration. version_added: 1.0.0 notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025. - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). options: banner: description: - Specifies which banner that should be configured on the remote device. required: true choices: - pre-login - post-login type: str text: description: - The banner text that should be present in the remote device running configuration. This argument accepts a multiline string, with no empty lines. Requires I(state=present). type: str state: description: - Specifies whether or not the configuration is present in the current devices active running configuration. default: present type: str choices: - present - absent extends_documentation_fragment: - vyos.vyos.vyos """ EXAMPLES = """ - name: configure the pre-login banner vyos.vyos.vyos_banner: banner: pre-login text: | this is my pre-login banner that contains a multiline string state: present - name: remove the post-login banner vyos.vyos.vyos_banner: banner: post-login state: absent """ RETURN = """ commands: description: The list of configuration mode commands to send to the device returned: always type: list sample: - banner pre-login - this is my pre-login banner - that contains a multiline - string """ import re from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( get_config, load_config, ) def spec_to_commands(updates, module): commands = list() want, have = updates state = module.params["state"] if state == "absent": if have.get("state") != "absent" or ( have.get("state") != "absent" and "text" in have.keys() and have["text"] ): commands.append("delete system login banner %s" % module.params["banner"]) elif state == "present": if want["text"] and want["text"].encode().decode("unicode_escape") != have.get("text"): banner_cmd = "set system login banner %s " % module.params["banner"] banner_cmd += want["text"].strip() commands.append(banner_cmd) return commands def config_to_dict(module): data = get_config(module) output = None obj = {"banner": module.params["banner"], "state": "absent"} for line in data.split("\n"): if line.startswith("set system login banner %s" % obj["banner"]): match = re.findall(r"%s (.*)" % obj["banner"], line, re.M) output = match if output: obj["text"] = output[0].encode().decode("unicode_escape") obj["state"] = "present" return obj def map_params_to_obj(module): text = module.params["text"] if text: text = "%r" % (str(text).strip()) return { "banner": module.params["banner"], "text": text, "state": module.params["state"], } def main(): """main entry point for module execution""" argument_spec = dict( banner=dict(required=True, choices=["pre-login", "post-login"]), text=dict(), state=dict(default="present", choices=["present", "absent"]), ) required_if = [("state", "present", ("text",))] module = AnsibleModule( argument_spec=argument_spec, required_if=required_if, supports_check_mode=True, ) warnings = list() result = {"changed": False} if warnings: result["warnings"] = warnings want = map_params_to_obj(module) have = config_to_dict(module) commands = spec_to_commands((want, have), module) result["commands"] = commands if commands: commit = not module.check_mode load_config(module, commands, commit=commit) result["changed"] = True module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_bgp_address_family.py b/plugins/modules/vyos_bgp_address_family.py index c59b43e4..4172643e 100644 --- a/plugins/modules/vyos_bgp_address_family.py +++ b/plugins/modules/vyos_bgp_address_family.py @@ -1,1238 +1,1237 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The module file for vyos_bgp_address_family """ from __future__ import absolute_import, division, print_function - __metaclass__ = type DOCUMENTATION = """ module: vyos_bgp_address_family version_added: 1.0.0 short_description: BGP Address Family resource module description: - This module manages BGP address family configuration of interfaces on devices running VYOS. - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025 - The provided examples of commands are valid for VyOS 1.4+ author: Gomathi Selvi Srinivasan (@GomathiselviS) options: config: description: A dict of BGP global configuration for interfaces. type: dict suboptions: as_number: description: - AS number type: int address_family: description: BGP address-family parameters. type: list elements: dict suboptions: afi: description: BGP address family settings. type: str choices: ['ipv4', 'ipv6'] aggregate_address: description: - BGP aggregate network. type: list elements: dict suboptions: prefix: description: BGP aggregate network. type: str as_set: description: Generate AS-set path information for this aggregate address. type: bool summary_only: description: Announce the aggregate summary network only. type: bool networks: description: BGP network type: list elements: dict suboptions: prefix: description: BGP network address type: str path_limit: description: AS path hop count limit type: int backdoor: description: Network as a backdoor route. type: bool route_map: description: Route-map to modify route attributes type: str redistribute: description: Redistribute routes from other protocols into BGP type: list elements: dict suboptions: protocol: description: types of routes to be redistributed. type: str choices: ['connected', 'kernel', 'ospf', 'ospfv3', 'rip', 'ripng', 'static'] table: description: Redistribute non-main Kernel Routing Table. type: str route_map: description: Route map to filter redistributed routes type: str metric: description: Metric for redistributed routes. type: int neighbors: description: BGP neighbor type: list elements: dict suboptions: neighbor_address: description: BGP neighbor address (v4/v6). type: str address_family: description: address family. type: list elements: dict suboptions: afi: description: BGP neighbor parameters. type: str choices: ['ipv4', 'ipv6'] allowas_in: description: Number of occurrences of AS number. type: int as_override: description: AS for routes sent to this neighbor to be the local AS. type: bool attribute_unchanged: description: BGP attributes are sent unchanged. type: dict suboptions: as_path: description: as_path attribute type: bool med: description: med attribute type: bool next_hop: description: next_hop attribute type: bool capability: description: Advertise capabilities to this neighbor. type: dict suboptions: dynamic: description: Advertise dynamic capability to this neighbor. type: bool orf: description: Advertise ORF capability to this neighbor. type: str choices: ['send', 'receive'] default_originate: description: Send default route to this neighbor type: str distribute_list: description: Access-list to filter route updates to/from this neighbor. type: list elements: dict suboptions: action: description: Access-list to filter outgoing/incoming route updates to this neighbor type: str choices: ['export', 'import'] acl: description: Access-list number. type: int filter_list: description: As-path-list to filter route updates to/from this neighbor. type: list elements: dict suboptions: action: description: filter outgoing/incoming route updates type: str choices: ['export', 'import'] path_list: description: As-path-list to filter type: str maximum_prefix: description: Maximum number of prefixes to accept from this neighbor nexthop-self Nexthop for routes sent to this neighbor to be the local router. type: int nexthop_local: description: Nexthop attributes. type: bool nexthop_self: description: Nexthop for routes sent to this neighbor to be the local router. type: bool peer_group: description: IPv4 peer group for this peer type: str prefix_list: description: Prefix-list to filter route updates to/from this neighbor. type: list elements: dict suboptions: action: description: filter outgoing/incoming route updates type: str choices: ['export', 'import'] prefix_list: description: Prefix-list to filter type: str remove_private_as: description: Remove private AS numbers from AS path in outbound route updates type: bool route_map: description: Route-map to filter route updates to/from this neighbor. type: list elements: dict suboptions: action: description: filter outgoing/incoming route updates type: str choices: ['export', 'import'] route_map: description: route-map to filter type: str route_reflector_client: description: Neighbor as a route reflector client type: bool route_server_client: description: Neighbor is route server client type: bool soft_reconfiguration: description: Soft reconfiguration for neighbor type: bool unsupress_map: description: Route-map to selectively unsuppress suppressed routes type: str weight: description: Default weight for routes from this neighbor type: int running_config: type: str description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VYOS device by executing the command B(show configuration command | match bgp). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. state: description: - The state the configuration should be left in. type: str choices: - merged - replaced - deleted - gathered - parsed - rendered - purged - overridden default: merged """ EXAMPLES = """ # Using merged # Before state # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # vyos@vyos:~$ - name: Merge provided configuration with device configuration vyos.vyos.vyos_bgp_address_family: config: as_number: "100" address_family: - afi: "ipv4" redistribute: - protocol: "static" metric: 50 neighbors: - neighbor_address: "20.33.1.1/24" address_family: - afi: "ipv4" allowas_in: 4 as_override: true attribute_unchanged: med: true - afi: "ipv6" default_originate: "map01" distribute_list: - action: "export" acl: 10 - neighbor_address: "100.11.34.12" address_family: - afi: "ipv4" maximum_prefix: 45 nexthop_self: true route_map: - action: "export" route_map: "map01" - action: "import" route_map: "map01" weight: 50 # After State: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 100 # set protocols bgp address-family ipv4-unicast redistribute static metric '50' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast as-override # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med # set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map 'map01' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export '10' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix '45' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast route-map export 'map01' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast route-map import 'map01' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast weight '50' # vyos@vyos:~$ # # Module Execution: # # "after": { # "address_family": [ # { # "afi": "ipv4", # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "maximum_prefix": 45, # "nexthop_self": true, # "route_map": [ # { # "action": "export", # "route_map": "map01" # }, # { # "action": "import", # "route_map": "map01" # } # ], # "weight": 50 # } # ], # "neighbor_address": "100.11.34.12" # }, # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # }, # { # "afi": "ipv6", # "default_originate": "map01", # "distribute_list": [ # { # "acl": 10, # "action": "export" # } # ] # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # }, # "before": {}, # "changed": true, # "commands": [ # "set protocols bgp address-family ipv4-unicast redistribute static metric 50", # "set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number 4", # "set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast as-override", # "set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med", # "set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map map01", # "set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export 10", # "set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix 45", # "set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self", # "set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast route-map export map01", # "set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast route-map import map01", # "set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast weight 50" # ], # # Using replaced: # Before state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 100 # set protocols bgp address-family ipv4-unicast redistribute static metric '50' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast as-override # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med # set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map 'map01' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export '10' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix '45' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast route-map export 'map01' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast route-map import 'map01' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast weight '50' # vyos@vyos:~$ - name: Replace provided configuration with device configuration vyos.vyos.vyos_bgp_address_family: config: as_number: "100" neighbors: - neighbor_address: "100.11.34.12" address_family: - afi: "ipv4" allowas_in: 4 as_override: true attribute_unchanged: med: true - afi: "ipv6" default_originate: "map01" distribute_list: - action: "export" acl: 10 - neighbor_address: "20.33.1.1/24" address_family: - afi: "ipv6" maximum_prefix: 45 nexthop_self: true state: replaced # After State: # # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 100 # set protocols bgp address-family ipv4-unicast redistribute static metric '50' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast # set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast maximum-prefix '45' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast nexthop-self # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast allowas-in number '4' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast as-override # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged med # set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast default-originate route-map 'map01' # set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast distribute-list export '10' # vyos@vyos:~$ # # # # Module Execution: # "after": { # "address_family": [ # { # "afi": "ipv4", # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # }, # { # "afi": "ipv6", # "default_originate": "map01", # "distribute_list": [ # { # "acl": 10, # "action": "export" # } # ] # } # ], # "neighbor_address": "100.11.34.12" # }, # { # "address_family": [ # { # "afi": "ipv4" # }, # { # "afi": "ipv6", # "maximum_prefix": 45, # "nexthop_self": true # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # }, # "before": { # "address_family": [ # { # "afi": "ipv4", # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "maximum_prefix": 45, # "nexthop_self": true, # "route_map": [ # { # "action": "export", # "route_map": "map01" # }, # { # "action": "import", # "route_map": "map01" # } # ], # "weight": 50 # } # ], # "neighbor_address": "100.11.34.12" # }, # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # }, # { # "afi": "ipv6", # "default_originate": "map01", # "distribute_list": [ # { # "acl": 10, # "action": "export" # } # ] # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # }, # "changed": true, # "commands": [ # "delete protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list", # "delete protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate", # "delete protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged", # "delete protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast as-override", # "delete protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in", # "delete protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast weight", # "delete protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast route-map", # "delete protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self", # "delete protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix", # "set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast allowas-in number 4", # "set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast as-override", # "set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged med", # "set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast default-originate route-map map01", # "set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast distribute-list export 10", # "set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast maximum-prefix 45", # "set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast nexthop-self" # ], # Using overridden # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 100 # set protocols bgp address-family ipv4-unicast network 35.1.1.0/24 backdoor # set protocols bgp address-family ipv4-unicast redistribute static metric '50' # set protocols bgp address-family ipv6-unicast aggregate-address 6601:1:1:1::/64 summary-only # set protocols bgp address-family ipv6-unicast network 5001:1:1:1::/64 route-map 'map01' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast # set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast maximum-prefix '45' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast nexthop-self # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast allowas-in number '4' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast as-override # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged med # set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast default-originate route-map 'map01' # set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast distribute-list export '10' # vyos@vyos:~$ - name: Override vyos.vyos.vyos_bgp_address_family: config: as_number: "100" neighbors: - neighbor_address: "100.11.34.12" address_family: - afi: "ipv6" maximum_prefix: 45 nexthop_self: true route_map: - action: "import" route_map: "map01" address_family: - afi: "ipv4" aggregate_address: - prefix: "60.9.2.0/24" summary_only: true - afi: "ipv6" redistribute: - protocol: "static" metric: 50 state: overridden # After State # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 100 # set protocols bgp address-family ipv4-unicast aggregate-address 60.9.2.0/24 summary-only # set protocols bgp address-family ipv6-unicast redistribute static metric '50' # set protocols bgp neighbor 20.33.1.1/24 # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast # set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast maximum-prefix '45' # set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast nexthop-self # set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast route-map import 'map01' # vyos@vyos:~$ # Module Execution: # "after": { # "address_family": [ # { # "afi": "ipv4", # "aggregate_address": [ # { # "prefix": "60.9.2.0/24", # "summary_only": true # } # ] # }, # { # "afi": "ipv6", # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4" # }, # { # "afi": "ipv6", # "maximum_prefix": 45, # "nexthop_self": true, # "route_map": [ # { # "action": "import", # "route_map": "map01" # } # ] # } # ], # "neighbor_address": "100.11.34.12" # } # ] # }, # "before": { # "address_family": [ # { # "afi": "ipv4", # "networks": [ # { # "backdoor": true, # "prefix": "35.1.1.0/24" # } # ], # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # }, # { # "afi": "ipv6", # "aggregate_address": [ # { # "prefix": "6601:1:1:1::/64", # "summary_only": true # } # ], # "networks": [ # { # "prefix": "5001:1:1:1::/64", # "route_map": "map01" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # }, # { # "afi": "ipv6", # "default_originate": "map01", # "distribute_list": [ # { # "acl": 10, # "action": "export" # } # ] # } # ], # "neighbor_address": "100.11.34.12" # }, # { # "address_family": [ # { # "afi": "ipv4" # }, # { # "afi": "ipv6", # "maximum_prefix": 45, # "nexthop_self": true # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # }, # "changed": true, # "commands": [ # "delete protocols bgp neighbor 20.33.1.1/24 address-family", # "delete protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast distribute-list", # "delete protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast default-originate", # "delete protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged", # "delete protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast as-override", # "delete protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast allowas-in", # "delete protocols bgp address-family ipv6 aggregate-address", # "delete protocols bgp address-family ipv6 network", # "delete protocols bgp address-family ipv4 network", # "delete protocols bgp address-family ipv4 redistribute", # "set protocols bgp address-family ipv4-unicast aggregate-address 60.9.2.0/24 summary-only", # "set protocols bgp address-family ipv6-unicast redistribute static metric 50", # "set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast maximum-prefix 45", # "set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast nexthop-self", # "set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast route-map import map01" # ], # # Using deleted: # Before State: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 100 # set protocols bgp address-family ipv4-unicast aggregate-address 60.9.2.0/24 summary-only # set protocols bgp address-family ipv4-unicast redistribute static metric '50' # set protocols bgp address-family ipv6-unicast redistribute static metric '50' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast as-override # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med # set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map 'map01' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export '10' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix '45' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast route-map export 'map01' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast route-map import 'map01' # set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast weight '50' # set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast maximum-prefix '45' # set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast nexthop-self # set protocols bgp neighbor 100.11.34.12 address-family ipv6-unicast route-map import 'map01' # vyos@vyos:~$ - name: Delete vyos.vyos.vyos_bgp_address_family: config: as_number: "100" neighbors: - neighbor_address: "20.33.1.1/24" address_family: - afi: "ipv6" - neighbor_address: "100.11.34.12" address_family: - afi: "ipv4" state: deleted # After State: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 100 # set protocols bgp address-family ipv6-unicast redistribute static metric '50' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast as-override # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med # set protocols bgp neighbor 100.11.34.12 # vyos@vyos:~$ # # # Module Execution: # # "after": { # "address_family": [ # { # "afi": "ipv6", # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # }, # "before": { # "address_family": [ # { # "afi": "ipv4", # "aggregate_address": [ # { # "prefix": "60.9.2.0/24", # "summary_only": true # } # ], # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # }, # { # "afi": "ipv6", # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "maximum_prefix": 45, # "nexthop_self": true, # "route_map": [ # { # "action": "export", # "route_map": "map01" # }, # { # "action": "import", # "route_map": "map01" # } # ], # "weight": 50 # }, # { # "afi": "ipv6", # "maximum_prefix": 45, # "nexthop_self": true, # "route_map": [ # { # "action": "import", # "route_map": "map01" # } # ] # } # ], # "neighbor_address": "100.11.34.12" # }, # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # }, # { # "afi": "ipv6", # "default_originate": "map01", # "distribute_list": [ # { # "acl": 10, # "action": "export" # } # ] # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # }, # "changed": true, # "commands": [ # "delete protocols bgp address-family ipv4-unicast", # "delete protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast", # "delete protocols bgp neighbor 100.11.34.12 address-family" # ], # # using parsed: # parsed.cfg # set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-set # set protocols bgp 65536 address-family ipv4-unicast network 192.1.13.0/24 route-map 'map01' # set protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24 backdoor # set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric '20' # set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast route-map export 'map01' # set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration inbound # set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop - name: parse configs vyos.vyos.vyos_bgp_address_family: running_config: "{{ lookup('file', './parsed.cfg') }}" state: parsed # Module execution result: # # "parsed": { # "address_family": [ # { # "afi": "ipv4", # "aggregate_address": [ # { # "as_set": true, # "prefix": "192.0.2.0/24" # } # ], # "networks": [ # { # "prefix": "192.1.13.0/24", # "route_map": "map01" # }, # { # "backdoor": true, # "prefix": "192.2.13.0/24" # } # ] # }, # { # "afi": "ipv6", # "redistribute": [ # { # "metric": 20, # "protocol": "ripng" # } # ] # } # ], # "as_number": 65536, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "route_map": [ # { # "action": "export", # "route_map": "map01" # } # ], # "soft_reconfiguration": true # } # ], # "neighbor_address": "192.0.2.25" # }, # { # "address_family": [ # { # "afi": "ipv6", # "attribute_unchanged": { # "next_hop": true # } # } # ], # "neighbor_address": "203.0.113.5" # } # ] # # Using gathered: # Native config: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 100 # set protocols bgp address-family ipv4-unicast network 35.1.1.0/24 backdoor # set protocols bgp address-family ipv4-unicast redistribute static metric '50' # set protocols bgp address-family ipv6-unicast aggregate-address 6601:1:1:1::/64 summary-only # set protocols bgp address-family ipv6-unicast network 5001:1:1:1::/64 route-map 'map01' # set protocols bgp address-family ipv6-unicast redistribute static metric '50' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast as-override # set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med # set protocols bgp neighbor 100.11.34.12 - name: gather configs vyos.vyos.vyos_bgp_address_family: state: gathered # Module execution result: # # "gathered": { # "address_family": [ # { # "afi": "ipv4", # "networks": [ # { # "backdoor": true, # "prefix": "35.1.1.0/24" # } # ], # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # }, # { # "afi": "ipv6", # "aggregate_address": [ # { # "prefix": "6601:1:1:1::/64", # "summary_only": true # } # ], # "networks": [ # { # "prefix": "5001:1:1:1::/64", # "route_map": "map01" # } # ], # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # Using rendered: - name: Render vyos.vyos.vyos_bgp_address_family: config: as_number: "100" address_family: - afi: "ipv4" redistribute: - protocol: "static" metric: 50 neighbors: - neighbor_address: "20.33.1.1/24" address_family: - afi: "ipv4" allowas_in: 4 as_override: true attribute_unchanged: med: true - afi: "ipv6" default_originate: "map01" distribute_list: - action: "export" acl: 10 - neighbor_address: "100.11.34.12" address_family: - afi: "ipv4" maximum_prefix: 45 nexthop_self: true route_map: - action: "export" route_map: "map01" - action: "import" route_map: "map01" weight: 50 state: rendered # Module Execution: # "rendered": [ # "set protocols bgp address-family ipv4-unicast redistribute static metric 50", # "set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number 4", # "set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast as-override", # "set protocols bgp neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med", # "set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map map01", # "set protocols bgp neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export 10", # "set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix 45", # "set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self", # "set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast route-map export map01", # "set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast route-map import map01", # "set protocols bgp neighbor 100.11.34.12 address-family ipv4-unicast weight 50" # ] """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: list sample: - sample command 1 - sample command 2 - sample command 3 rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - sample command 1 - sample command 2 - sample command 3 gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_address_family.bgp_address_family import ( Bgp_address_familyArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_address_family.bgp_address_family import ( Bgp_address_family, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ module = AnsibleModule( argument_spec=Bgp_address_familyArgs.argument_spec, mutually_exclusive=[["config", "running_config"]], required_if=[ ["state", "merged", ["config"]], ["state", "replaced", ["config"]], ["state", "overridden", ["config"]], ["state", "rendered", ["config"]], ["state", "parsed", ["running_config"]], ], supports_check_mode=True, ) result = Bgp_address_family(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_bgp_global.py b/plugins/modules/vyos_bgp_global.py index 7e9f63b1..02f5e590 100644 --- a/plugins/modules/vyos_bgp_global.py +++ b/plugins/modules/vyos_bgp_global.py @@ -1,1407 +1,1406 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The module file for vyos_bgp_global """ from __future__ import absolute_import, division, print_function - __metaclass__ = type DOCUMENTATION = """ module: vyos_bgp_global version_added: 1.0.0 short_description: BGP global resource module description: - This module manages BGP global configuration of interfaces on devices running VYOS. - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025 - The provided examples of commands are valid for VyOS 1.4+ author: - Gomathi Selvi Srinivasan (@GomathiselviS) options: config: description: A dict of BGP global configuration for interfaces. type: dict suboptions: as_number: description: - AS number. type: int # aggregate_address: # description: # - BGP aggregate network. # type: list # elements: dict # suboptions: # prefix: # description: # - BGP aggregate network. # type: str # as_set: # description: # - Generate AS-set path information for this aggregate address. # type: bool # summary_only: # description: # - Announce the aggregate summary network only. # type: bool #maximum_paths: --> moved to address-family before 1.3 # description: BGP multipaths # type: list # elements: dict # suboptions: # path: # description: BGP multipaths # type: str # count: # description: No. of paths. # type: int neighbor: description: BGP neighbor type: list elements: dict suboptions: address: description: - BGP neighbor address (v4/v6). type: str advertisement_interval: description: - Minimum interval for sending routing updates. type: int capability: description: - Advertise capabilities to this neighbor. type: dict suboptions: dynamic: description: - Advertise dynamic capability to this neighbor. type: bool extended_nexthop: description: - Advertise extended nexthop capability to this neighbor. type: bool default_originate: description: - Send default route to this neighbor type: str description: description: - Description of the neighbor type: str disable_capability_negotiation: description: - Disbale capability negotiation with the neighbor type: bool disable_connected_check: description: - Disable check to see if EBGP peer's address is a connected route. type: bool disable_send_community: description: - Disable sending community attributes to this neighbor. type: str choices: ['extended', 'standard'] ebgp_multihop: description: - Allow this EBGP neighbor to not be on a directly connected network. Specify the number of hops. type: int local_as: description: local as number not to be prepended to updates from EBGP peers type: int override_capability: description: Ignore capability negotiation with specified neighbor. type: bool passive: description: Do not initiate a session with this neighbor type: bool password: description: BGP MD5 password type: str peer_group_name: description: IPv4 peer group for this peer type: str peer_group: description: True if all the configs under this neighbor key is for peer group template. type: bool port: description: Neighbor's BGP port type: int remote_as: description: Neighbor BGP AS number type: int shutdown: description: Administratively shut down neighbor type: bool solo: # <-- added in 1.3 description: Do not send back prefixes learned from the neighbor type: bool strict_capability_match: description: Enable strict capability negotiation type: bool timers: description: Neighbor timers type: dict suboptions: connect: description: BGP connect timer for this neighbor. type: int holdtime: description: BGP hold timer for this neighbor type: int keepalive: description: BGP keepalive interval for this neighbor type: int ttl_security: description: Number of the maximum number of hops to the BGP peer type: int update_source: description: Source IP of routing updates type: str # network: # description: BGP network # type: list # elements: dict # suboptions: # address: # description: BGP network address # type: str # backdoor: # description: Network as a backdoor route # type: bool # route_map: # description: Route-map to modify route attributes # type: str # redistribute: # description: Redistribute routes from other protocols into BGP # type: list # elements: dict # suboptions: # protocol: # description: types of routes to be redistributed. # type: str # choices: ['connected', 'kernel', 'ospf', 'rip', 'static'] # route_map: # description: Route map to filter redistributed routes # type: str # metric: # description: Metric for redistributed routes. # type: int timers: description: BGP protocol timers type: dict suboptions: keepalive: description: Keepalive interval type: int holdtime: description: Hold time interval type: int bgp_params: description: BGP parameters type: dict suboptions: always_compare_med: description: Always compare MEDs from different neighbors type: bool bestpath: description: Default bestpath selection mechanism type: dict suboptions: as_path: description: AS-path attribute comparison parameters type: str choices: ['confed', 'ignore'] compare_routerid: description: Compare the router-id for identical EBGP paths type: bool med: description: MED attribute comparison parameters type: str choices: ['confed', 'missing-as-worst'] cluster_id: description: Route-reflector cluster-id type: str confederation: description: AS confederation parameters type: list elements: dict suboptions: identifier: description: Confederation AS identifier type: int peers: description: Peer ASs in the BGP confederation type: int dampening: description: Enable route-flap dampening type: dict suboptions: half_life: description: Half-life penalty in seconds type: int max_suppress_time: description: Maximum duration to suppress a stable route type: int re_use: description: Time to start reusing a route type: int start_suppress_time: description: When to start suppressing a route type: int default: description: BGP defaults type: dict suboptions: local_pref: description: Default local preference type: int no_ipv4_unicast: description: | Deactivate IPv4 unicast for a peer by default Deprecated: Unavailable after 1.4 type: bool deterministic_med: description: Compare MEDs between different peers in the same AS type: bool disable_network_import_check: description: Disable IGP route check for network statements type: bool distance: description: Administrative distances for BGP routes type: list elements: dict suboptions: type: description: Type of route type: str choices: ['external', 'internal', 'local'] value: description: distance type: int prefix: description: Administrative distance for a specific BGP prefix type: int enforce_first_as: description: Require first AS in the path to match peer's AS type: bool graceful_restart: description: Maximum time to hold onto restarting peer's stale paths type: int log_neighbor_changes: description: Log neighbor up/down changes and reset reason type: bool no_client_to_client_reflection: description: Disable client to client route reflection type: bool no_fast_external_failover: description: Disable immediate session reset if peer's connected link goes down type: bool router_id: description: BGP router-id type: str scan_time: description: BGP route scanner interval type: int state: description: - The state the configuration should be left in. - State I(purged) removes all the BGP configurations from the target device. Use caution with this state.('delete protocols bgp ') - State I(deleted) only removes BGP attributes that this modules manages and does not negate the BGP process completely. Thereby, preserving address-family related configurations under BGP context. - Running states I(deleted) and I(replaced) will result in an error if there are address-family configuration lines present under neighbor context that is is to be removed. Please use the M(vyos.vyos.vyos_bgp_address_family) module for prior cleanup. - Refer to examples for more details. type: str choices: [deleted, merged, purged, replaced, gathered, rendered, parsed] default: merged running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the EOS device by executing the command B(show running-config | section bgp). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str """ EXAMPLES = """ # Using merged # Before state # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # vyos@vyos:~$ - name: Merge provided configuration with device configuration vyos.vyos.vyos_bgp_global: config: as_number: "65536" aggregate_address: - prefix: "203.0.113.0/24" as_set: true - prefix: "192.0.2.0/24" summary_only: true network: - address: "192.1.13.0/24" backdoor: true redistribute: - protocol: "kernel" metric: 45 - protocol: "connected" route_map: "map01" maximum_paths: - path: "ebgp" count: 20 - path: "ibgp" count: 55 timers: keepalive: 35 bgp_params: bestpath: as_path: "confed" compare_routerid: true default: no_ipv4_unicast: true router_id: "192.1.2.9" confederation: - peers: 20 - peers: 55 - identifier: 66 neighbor: - address: "192.0.2.25" disable_connected_check: true timers: holdtime: 30 keepalive: 10 - address: "203.0.113.5" attribute_unchanged: as_path: true med: true ebgp_multihop: 2 remote_as: 101 update_source: "192.0.2.25" - address: "5001::64" maximum_prefix: 34 distribute_list: - acl: 20 action: "export" - acl: 40 action: "import" state: merged # After State # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 65536 # set protocols bgp aggregate-address 192.0.2.0/24 'summary-only' # set protocols bgp aggregate-address 203.0.113.0/24 'as-set' # set protocols bgp maximum-paths ebgp '20' # set protocols bgp maximum-paths ibgp '55' # set protocols bgp neighbor 192.0.2.25 'disable-connected-check' # set protocols bgp neighbor 192.0.2.25 timers holdtime '30' # set protocols bgp neighbor 192.0.2.25 timers keepalive '10' # set protocols bgp neighbor 203.0.113.5 attribute-unchanged 'as-path' # set protocols bgp neighbor 203.0.113.5 attribute-unchanged 'med' # set protocols bgp neighbor 203.0.113.5 attribute-unchanged 'next-hop' # set protocols bgp neighbor 203.0.113.5 ebgp-multihop '2' # set protocols bgp neighbor 203.0.113.5 remote-as '101' # set protocols bgp neighbor 203.0.113.5 update-source '192.0.2.25' # set protocols bgp neighbor 5001::64 distribute-list export '20' # set protocols bgp neighbor 5001::64 distribute-list import '40' # set protocols bgp neighbor 5001::64 maximum-prefix '34' # set protocols bgp network 192.1.13.0/24 'backdoor' # set protocols bgp parameters bestpath as-path 'confed' # set protocols bgp parameters bestpath 'compare-routerid' # set protocols bgp parameters confederation identifier '66' # set protocols bgp parameters confederation peers '20' # set protocols bgp parameters confederation peers '55' # set protocols bgp parameters default 'no-ipv4-unicast' # set protocols bgp parameters router-id '192.1.2.9' # set protocols bgp redistribute connected route-map 'map01' # set protocols bgp redistribute kernel metric '45' # set protocols bgp timers keepalive '35' # vyos@vyos:~$ # # # Module Execution: # # "after": { # "aggregate_address": [ # { # "prefix": "192.0.2.0/24", # "summary_only": true # }, # { # "prefix": "203.0.113.0/24", # "as_set": true # } # ], # "as_number": 65536, # "bgp_params": { # "bestpath": { # "as_path": "confed", # "compare_routerid": true # }, # "confederation": [ # { # "identifier": 66 # }, # { # "peers": 20 # }, # { # "peers": 55 # } # ], # "default": { # "no_ipv4_unicast": true # }, # "router_id": "192.1.2.9" # }, # "maximum_paths": [ # { # "count": 20, # "path": "ebgp" # }, # { # "count": 55, # "path": "ibgp" # } # ], # "neighbor": [ # { # "address": "192.0.2.25", # "disable_connected_check": true, # "timers": { # "holdtime": 30, # "keepalive": 10 # } # }, # { # "address": "203.0.113.5", # "attribute_unchanged": { # "as_path": true, # "med": true, # "next_hop": true # }, # "ebgp_multihop": 2, # "remote_as": 101, # "update_source": "192.0.2.25" # }, # { # "address": "5001::64", # "distribute_list": [ # { # "acl": 20, # "action": "export" # }, # { # "acl": 40, # "action": "import" # } # ], # "maximum_prefix": 34 # } # ], # "network": [ # { # "address": "192.1.13.0/24", # "backdoor": true # } # ], # "redistribute": [ # { # "protocol": "connected", # "route_map": "map01" # }, # { # "metric": 45, # "protocol": "kernel" # } # ], # "timers": { # "keepalive": 35 # } # }, # "before": {}, # "changed": true, # "commands": [ # "set protocols bgp neighbor 192.0.2.25 disable-connected-check", # "set protocols bgp neighbor 192.0.2.25 timers holdtime 30", # "set protocols bgp neighbor 192.0.2.25 timers keepalive 10", # "set protocols bgp neighbor 203.0.113.5 attribute-unchanged as-path", # "set protocols bgp neighbor 203.0.113.5 attribute-unchanged med", # "set protocols bgp neighbor 203.0.113.5 attribute-unchanged next-hop", # "set protocols bgp neighbor 203.0.113.5 ebgp-multihop 2", # "set protocols bgp neighbor 203.0.113.5 remote-as 101", # "set protocols bgp neighbor 203.0.113.5 update-source 192.0.2.25", # "set protocols bgp neighbor 5001::64 maximum-prefix 34", # "set protocols bgp neighbor 5001::64 distribute-list export 20", # "set protocols bgp neighbor 5001::64 distribute-list import 40", # "set protocols bgp redistribute kernel metric 45", # "set protocols bgp redistribute connected route-map map01", # "set protocols bgp network 192.1.13.0/24 backdoor", # "set protocols bgp aggregate-address 203.0.113.0/24 as-set", # "set protocols bgp aggregate-address 192.0.2.0/24 summary-only", # "set protocols bgp parameters bestpath as-path confed", # "set protocols bgp parameters bestpath compare-routerid", # "set protocols bgp parameters default no-ipv4-unicast", # "set protocols bgp parameters router-id 192.1.2.9", # "set protocols bgp parameters confederation peers 20", # "set protocols bgp parameters confederation peers 55", # "set protocols bgp parameters confederation identifier 66", # "set protocols bgp maximum-paths ebgp 20", # "set protocols bgp maximum-paths ibgp 55", # "set protocols bgp timers keepalive 35" # ], # Using replaced: # -------------- # Before state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 65536 # set protocols bgp aggregate-address 192.0.2.0/24 'summary-only' # set protocols bgp aggregate-address 203.0.113.0/24 'as-set' # set protocols bgp maximum-paths ebgp '20' # set protocols bgp maximum-paths ibgp '55' # set protocols bgp neighbor 192.0.2.25 'disable-connected-check' # set protocols bgp neighbor 192.0.2.25 timers holdtime '30' # set protocols bgp neighbor 192.0.2.25 timers keepalive '10' # set protocols bgp neighbor 203.0.113.5 attribute-unchanged 'as-path' # set protocols bgp neighbor 203.0.113.5 attribute-unchanged 'med' # set protocols bgp neighbor 203.0.113.5 attribute-unchanged 'next-hop' # set protocols bgp neighbor 203.0.113.5 ebgp-multihop '2' # set protocols bgp neighbor 203.0.113.5 remote-as '101' # set protocols bgp neighbor 203.0.113.5 update-source '192.0.2.25' # set protocols bgp neighbor 5001::64 distribute-list export '20' # set protocols bgp neighbor 5001::64 distribute-list import '40' # set protocols bgp neighbor 5001::64 maximum-prefix '34' # set protocols bgp network 192.1.13.0/24 'backdoor' # set protocols bgp parameters bestpath as-path 'confed' # set protocols bgp parameters bestpath 'compare-routerid' # set protocols bgp parameters confederation identifier '66' # set protocols bgp parameters confederation peers '20' # set protocols bgp parameters confederation peers '55' # set protocols bgp parameters default 'no-ipv4-unicast' # set protocols bgp parameters router-id '192.1.2.9' # set protocols bgp redistribute connected route-map 'map01' # set protocols bgp redistribute kernel metric '45' # set protocols bgp timers keepalive '35' # vyos@vyos:~$ - name: Replace vyos.vyos.vyos_bgp_global: config: as_number: "65536" network: - address: "203.0.113.0/24" route_map: map01 redistribute: - protocol: "static" route_map: "map01" neighbor: - address: "192.0.2.40" advertisement_interval: 72 capability: orf: "receive" bgp_params: bestpath: as_path: "confed" state: replaced # After state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 65536 # set protocols bgp neighbor 192.0.2.40 advertisement-interval '72' # set protocols bgp neighbor 192.0.2.40 capability orf prefix-list 'receive' # set protocols bgp network 203.0.113.0/24 route-map 'map01' # set protocols bgp parameters bestpath as-path 'confed' # set protocols bgp redistribute static route-map 'map01' # vyos@vyos:~$ # # # Module Execution: # # "after": { # "as_number": 65536, # "bgp_params": { # "bestpath": { # "as_path": "confed" # } # }, # "neighbor": [ # { # "address": "192.0.2.40", # "advertisement_interval": 72, # "capability": { # "orf": "receive" # } # } # ], # "network": [ # { # "address": "203.0.113.0/24", # "route_map": "map01" # } # ], # "redistribute": [ # { # "protocol": "static", # "route_map": "map01" # } # ] # }, # "before": { # "aggregate_address": [ # { # "prefix": "192.0.2.0/24", # "summary_only": true # }, # { # "prefix": "203.0.113.0/24", # "as_set": true # } # ], # "as_number": 65536, # "bgp_params": { # "bestpath": { # "as_path": "confed", # "compare_routerid": true # }, # "confederation": [ # { # "identifier": 66 # }, # { # "peers": 20 # }, # { # "peers": 55 # } # ], # "default": { # "no_ipv4_unicast": true # }, # "router_id": "192.1.2.9" # }, # "maximum_paths": [ # { # "count": 20, # "path": "ebgp" # }, # { # "count": 55, # "path": "ibgp" # } # ], # "neighbor": [ # { # "address": "192.0.2.25", # "disable_connected_check": true, # "timers": { # "holdtime": 30, # "keepalive": 10 # } # }, # { # "address": "203.0.113.5", # "attribute_unchanged": { # "as_path": true, # "med": true, # "next_hop": true # }, # "ebgp_multihop": 2, # "remote_as": 101, # "update_source": "192.0.2.25" # }, # { # "address": "5001::64", # "distribute_list": [ # { # "acl": 20, # "action": "export" # }, # { # "acl": 40, # "action": "import" # } # ], # "maximum_prefix": 34 # } # ], # "network": [ # { # "address": "192.1.13.0/24", # "backdoor": true # } # ], # "redistribute": [ # { # "protocol": "connected", # "route_map": "map01" # }, # { # "metric": 45, # "protocol": "kernel" # } # ], # "timers": { # "keepalive": 35 # } # }, # "changed": true, # "commands": [ # "delete protocols bgp timers", # "delete protocols bgp maximum-paths ", # "delete protocols bgp maximum-paths ", # "delete protocols bgp parameters router-id 192.1.2.9", # "delete protocols bgp parameters default", # "delete protocols bgp parameters confederation", # "delete protocols bgp parameters bestpath compare-routerid", # "delete protocols bgp aggregate-address", # "delete protocols bgp network 192.1.13.0/24", # "delete protocols bgp redistribute kernel", # "delete protocols bgp redistribute kernel", # "delete protocols bgp redistribute connected", # "delete protocols bgp redistribute connected", # "delete protocols bgp neighbor 5001::64", # "delete protocols bgp neighbor 203.0.113.5", # "delete protocols bgp neighbor 192.0.2.25", # "set protocols bgp neighbor 192.0.2.40 advertisement-interval 72", # "set protocols bgp neighbor 192.0.2.40 capability orf prefix-list receive", # "set protocols bgp redistribute static route-map map01", # "set protocols bgp network 203.0.113.0/24 route-map map01" # ], # Using deleted: # ------------- # Before state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 65536 # set protocols bgp neighbor 192.0.2.40 advertisement-interval '72' # set protocols bgp neighbor 192.0.2.40 capability orf prefix-list 'receive' # set protocols bgp network 203.0.113.0/24 route-map 'map01' # set protocols bgp parameters bestpath as-path 'confed' # set protocols bgp redistribute static route-map 'map01' # vyos@vyos:~$ - name: Delete configuration vyos.vyos.vyos_bgp_global: config: as_number: "65536" state: deleted # After state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp '65536' # vyos@vyos:~$ # # # Module Execution: # # "after": { # "as_number": 65536 # }, # "before": { # "as_number": 65536, # "bgp_params": { # "bestpath": { # "as_path": "confed" # } # }, # "neighbor": [ # { # "address": "192.0.2.40", # "advertisement_interval": 72, # "capability": { # "orf": "receive" # } # } # ], # "network": [ # { # "address": "203.0.113.0/24", # "route_map": "map01" # } # ], # "redistribute": [ # { # "protocol": "static", # "route_map": "map01" # } # ] # }, # "changed": true, # "commands": [ # "delete protocols bgp neighbor 192.0.2.40", # "delete protocols bgp redistribute", # "delete protocols bgp network", # "delete protocols bgp parameters" # ], # Using purged: # Before state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 65536 # set protocols bgp aggregate-address 192.0.2.0/24 'summary-only' # set protocols bgp aggregate-address 203.0.113.0/24 'as-set' # set protocols bgp maximum-paths ebgp '20' # set protocols bgp maximum-paths ibgp '55' # set protocols bgp neighbor 192.0.2.25 'disable-connected-check' # set protocols bgp neighbor 192.0.2.25 timers holdtime '30' # set protocols bgp neighbor 192.0.2.25 timers keepalive '10' # set protocols bgp neighbor 203.0.113.5 attribute-unchanged 'as-path' # set protocols bgp neighbor 203.0.113.5 attribute-unchanged 'med' # set protocols bgp neighbor 203.0.113.5 attribute-unchanged 'next-hop' # set protocols bgp neighbor 203.0.113.5 ebgp-multihop '2' # set protocols bgp neighbor 203.0.113.5 remote-as '101' # set protocols bgp neighbor 203.0.113.5 update-source '192.0.2.25' # set protocols bgp neighbor 5001::64 distribute-list export '20' # set protocols bgp neighbor 5001::64 distribute-list import '40' # set protocols bgp neighbor 5001::64 maximum-prefix '34' # set protocols bgp network 192.1.13.0/24 'backdoor' # set protocols bgp parameters bestpath as-path 'confed' # set protocols bgp parameters bestpath 'compare-routerid' # set protocols bgp parameters confederation identifier '66' # set protocols bgp parameters confederation peers '20' # set protocols bgp parameters confederation peers '55' # set protocols bgp parameters default 'no-ipv4-unicast' # set protocols bgp parameters router-id '192.1.2.9' # set protocols bgp redistribute connected route-map 'map01' # set protocols bgp redistribute kernel metric '45' # set protocols bgp timers keepalive '35' # vyos@vyos:~$ - name: Purge configuration vyos.vyos.vyos_bgp_global: config: as_number: "65536" state: purged # After state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # vyos@vyos:~$ # # Module Execution: # # "after": {}, # "before": { # "aggregate_address": [ # { # "prefix": "192.0.2.0/24", # "summary_only": true # }, # { # "prefix": "203.0.113.0/24", # "as_set": true # } # ], # "as_number": 65536, # "bgp_params": { # "bestpath": { # "as_path": "confed", # "compare_routerid": true # }, # "confederation": [ # { # "identifier": 66 # }, # { # "peers": 20 # }, # { # "peers": 55 # } # ], # "default": { # "no_ipv4_unicast": true # }, # "router_id": "192.1.2.9" # }, # "maximum_paths": [ # { # "count": 20, # "path": "ebgp" # }, # { # "count": 55, # "path": "ibgp" # } # ], # "neighbor": [ # { # "address": "192.0.2.25", # "disable_connected_check": true, # "timers": { # "holdtime": 30, # "keepalive": 10 # } # }, # { # "address": "203.0.113.5", # "attribute_unchanged": { # "as_path": true, # "med": true, # "next_hop": true # }, # "ebgp_multihop": 2, # "remote_as": 101, # "update_source": "192.0.2.25" # }, # { # "address": "5001::64", # "distribute_list": [ # { # "acl": 20, # "action": "export" # }, # { # "acl": 40, # "action": "import" # } # ], # "maximum_prefix": 34 # } # ], # "network": [ # { # "address": "192.1.13.0/24", # "backdoor": true # } # ], # "redistribute": [ # { # "protocol": "connected", # "route_map": "map01" # }, # { # "metric": 45, # "protocol": "kernel" # } # ], # "timers": { # "keepalive": 35 # } # }, # "changed": true, # "commands": [ # "delete protocols bgp 65536" # ], # Deleted in presence of address family under neighbors: # Before state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 65536 # set protocols bgp neighbor 192.0.2.43 advertisement-interval '72' # set protocols bgp neighbor 192.0.2.43 capability 'dynamic' # set protocols bgp neighbor 192.0.2.43 'disable-connected-check' # set protocols bgp neighbor 192.0.2.43 timers holdtime '30' # set protocols bgp neighbor 192.0.2.43 timers keepalive '10' # set protocols bgp neighbor 203.0.113.0 address-family 'ipv6-unicast' # set protocols bgp neighbor 203.0.113.0 capability orf prefix-list 'receive' # set protocols bgp network 203.0.113.0/24 route-map 'map01' # set protocols bgp parameters 'always-compare-med' # set protocols bgp parameters bestpath as-path 'confed' # set protocols bgp parameters bestpath 'compare-routerid' # set protocols bgp parameters dampening half-life '33' # set protocols bgp parameters dampening max-suppress-time '20' # set protocols bgp parameters dampening re-use '60' # set protocols bgp parameters dampening start-suppress-time '5' # set protocols bgp parameters default 'no-ipv4-unicast' # set protocols bgp parameters distance global external '66' # set protocols bgp parameters distance global internal '20' # set protocols bgp parameters distance global local '10' # set protocols bgp redistribute static route-map 'map01' # vyos@vyos:~$ ^C # vyos@vyos:~$ - name: Delete configuration vyos.vyos.vyos_bgp_global: config: as_number: "65536" state: deleted # Module Execution: # # "changed": false, # "invocation": { # "module_args": { # "config": { # "aggregate_address": null, # "as_number": 65536, # "bgp_params": null, # "maximum_paths": null, # "neighbor": null, # "network": null, # "redistribute": null, # "timers": null # }, # "running_config": null, # "state": "deleted" # } # }, # "msg": "Use the _bgp_address_family module to delete the address_family under neighbor 203.0.113.0, before replacing/deleting the neighbor." # } # using gathered: # -------------- # Before state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp system-as 65536 # set protocols bgp neighbor 192.0.2.43 advertisement-interval '72' # set protocols bgp neighbor 192.0.2.43 capability 'dynamic' # set protocols bgp neighbor 192.0.2.43 'disable-connected-check' # set protocols bgp neighbor 192.0.2.43 timers holdtime '30' # set protocols bgp neighbor 192.0.2.43 timers keepalive '10' # set protocols bgp neighbor 203.0.113.0 address-family 'ipv6-unicast' # set protocols bgp neighbor 203.0.113.0 capability orf prefix-list 'receive' # set protocols bgp network 203.0.113.0/24 route-map 'map01' # set protocols bgp parameters 'always-compare-med' # set protocols bgp parameters bestpath as-path 'confed' # set protocols bgp parameters bestpath 'compare-routerid' # set protocols bgp parameters dampening half-life '33' # set protocols bgp parameters dampening max-suppress-time '20' # set protocols bgp parameters dampening re-use '60' # set protocols bgp parameters dampening start-suppress-time '5' # set protocols bgp parameters default 'no-ipv4-unicast' # set protocols bgp parameters distance global external '66' # set protocols bgp parameters distance global internal '20' # set protocols bgp parameters distance global local '10' # set protocols bgp redistribute static route-map 'map01' # vyos@vyos:~$ ^C - name: gather configs vyos.vyos.vyos_bgp_global: state: gathered # Module Execution: # "gathered": { # "as_number": 65536, # "bgp_params": { # "always_compare_med": true, # "bestpath": { # "as_path": "confed", # "compare_routerid": true # }, # "default": { # "no_ipv4_unicast": true # }, # "distance": [ # { # "type": "external", # "value": 66 # }, # { # "type": "internal", # "value": 20 # }, # { # "type": "local", # "value": 10 # } # ] # }, # "neighbor": [ # { # "address": "192.0.2.43", # "advertisement_interval": 72, # "capability": { # "dynamic": true # }, # "disable_connected_check": true, # "timers": { # "holdtime": 30, # "keepalive": 10 # } # }, # { # "address": "203.0.113.0", # "capability": { # "orf": "receive" # } # } # ], # "network": [ # { # "address": "203.0.113.0/24", # "route_map": "map01" # } # ], # "redistribute": [ # { # "protocol": "static", # "route_map": "map01" # } # ] # }, # # Using parsed: # ------------ # parsed.cfg # set protocols bgp neighbor 192.0.2.43 advertisement-interval '72' # set protocols bgp neighbor 192.0.2.43 capability 'dynamic' # set protocols bgp neighbor 192.0.2.43 'disable-connected-check' # set protocols bgp neighbor 192.0.2.43 timers holdtime '30' # set protocols bgp neighbor 192.0.2.43 timers keepalive '10' # set protocols bgp neighbor 203.0.113.0 address-family 'ipv6-unicast' # set protocols bgp neighbor 203.0.113.0 capability orf prefix-list 'receive' # set protocols bgp network 203.0.113.0/24 route-map 'map01' # set protocols bgp parameters 'always-compare-med' # set protocols bgp parameters bestpath as-path 'confed' # set protocols bgp parameters bestpath 'compare-routerid' # set protocols bgp parameters dampening half-life '33' # set protocols bgp parameters dampening max-suppress-time '20' # set protocols bgp parameters dampening re-use '60' # set protocols bgp parameters dampening start-suppress-time '5' # set protocols bgp parameters default 'no-ipv4-unicast' # set protocols bgp parameters distance global external '66' # set protocols bgp parameters distance global internal '20' # set protocols bgp parameters distance global local '10' # set protocols bgp redistribute static route-map 'map01' - name: parse configs vyos.vyos.vyos_bgp_global: running_config: "{{ lookup('file', './parsed.cfg') }}" state: parsed tags: - parsed # Module execution: # "parsed": { # "as_number": 65536, # "bgp_params": { # "always_compare_med": true, # "bestpath": { # "as_path": "confed", # "compare_routerid": true # }, # "default": { # "no_ipv4_unicast": true # }, # "distance": [ # { # "type": "external", # "value": 66 # }, # { # "type": "internal", # "value": 20 # }, # { # "type": "local", # "value": 10 # } # ] # }, # "neighbor": [ # { # "address": "192.0.2.43", # "advertisement_interval": 72, # "capability": { # "dynamic": true # }, # "disable_connected_check": true, # "timers": { # "holdtime": 30, # "keepalive": 10 # } # }, # { # "address": "203.0.113.0", # "capability": { # "orf": "receive" # } # } # ], # "network": [ # { # "address": "203.0.113.0/24", # "route_map": "map01" # } # ], # "redistribute": [ # { # "protocol": "static", # "route_map": "map01" # } # ] # } # # Using rendered: # -------------- - name: Render vyos.vyos.vyos_bgp_global: config: as_number: "65536" network: - address: "203.0.113.0/24" route_map: map01 redistribute: - protocol: "static" route_map: "map01" bgp_params: always_compare_med: true dampening: start_suppress_time: 5 max_suppress_time: 20 half_life: 33 re_use: 60 distance: - type: "internal" value: 20 - type: "local" value: 10 - type: "external" value: 66 bestpath: as_path: "confed" compare_routerid: true default: no_ipv4_unicast: true neighbor: - address: "192.0.2.43" disable_connected_check: true advertisement_interval: 72 capability: dynamic: true timers: holdtime: 30 keepalive: 10 - address: "203.0.113.0" capability: orf: "receive" state: rendered # Module Execution: # "rendered": [ # "set protocols bgp neighbor 192.0.2.43 disable-connected-check", # "set protocols bgp neighbor 192.0.2.43 advertisement-interval 72", # "set protocols bgp neighbor 192.0.2.43 capability dynamic", # "set protocols bgp neighbor 192.0.2.43 timers holdtime 30", # "set protocols bgp neighbor 192.0.2.43 timers keepalive 10", # "set protocols bgp neighbor 203.0.113.0 capability orf prefix-list receive", # "set protocols bgp redistribute static route-map map01", # "set protocols bgp network 203.0.113.0/24 route-map map01", # "set protocols bgp parameters always-compare-med", # "set protocols bgp parameters dampening half-life 33", # "set protocols bgp parameters dampening max-suppress-time 20", # "set protocols bgp parameters dampening re-use 60", # "set protocols bgp parameters dampening start-suppress-time 5", # "set protocols bgp parameters distance global internal 20", # "set protocols bgp parameters distance global local 10", # "set protocols bgp parameters distance global external 66", # "set protocols bgp parameters bestpath as-path confed", # "set protocols bgp parameters bestpath compare-routerid", # "set protocols bgp parameters default no-ipv4-unicast" # ] """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: list sample: - set protocols bgp redistribute static route-map map01 - set protocols bgp network 203.0.113.0/24 route-map map01 - set protocols bgp parameters always-compare-med rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - set protocols bgp redistribute static route-map map01 - set protocols bgp network 203.0.113.0/24 route-map map01 - set protocols bgp parameters always-compare-med gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_global.bgp_global import ( Bgp_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_global.bgp_global import ( Bgp_global, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ module = AnsibleModule( argument_spec=Bgp_globalArgs.argument_spec, mutually_exclusive=[["config", "running_config"]], required_if=[ ["state", "merged", ["config"]], ["state", "replaced", ["config"]], ["state", "overridden", ["config"]], ["state", "rendered", ["config"]], ["state", "parsed", ["running_config"]], ], supports_check_mode=True, ) result = Bgp_global(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_command.py b/plugins/modules/vyos_command.py index 766370c2..5131dd88 100644 --- a/plugins/modules/vyos_command.py +++ b/plugins/modules/vyos_command.py @@ -1,228 +1,227 @@ #!/usr/bin/python # # 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 . # from __future__ import absolute_import, division, print_function - __metaclass__ = type DOCUMENTATION = """ module: vyos_command author: Nathaniel Case (@Qalthos) short_description: Run one or more commands on VyOS devices description: - The command module allows running one or more commands on remote devices running VyOS. This module can also be introspected to validate key parameters before returning successfully. If the conditional statements are not met in the wait period, the task fails. - Certain C(show) commands in VyOS produce many lines of output and use a custom pager that can cause this module to hang. If the value of the environment variable C(ANSIBLE_VYOS_TERMINAL_LENGTH) is not set, the default number of 10000 is used. version_added: 1.0.0 extends_documentation_fragment: - vyos.vyos.vyos options: commands: description: - The ordered set of commands to execute on the remote device running VyOS. The output from the command execution is returned to the playbook. If the I(wait_for) argument is provided, the module is not returned until the condition is satisfied or the number of retries has been exceeded. - If a command sent to the device requires answering a prompt, it is possible to pass a dict containing command, answer and prompt. Common answers are 'y' or "\\r" (carriage return, must be double quotes). Refer below examples. required: true type: list elements: raw wait_for: description: - Specifies what to evaluate from the output of the command and what conditionals to apply. This argument will cause the task to wait for a particular conditional to be true before moving forward. If the conditional is not true by the configured I(retries), the task fails. See examples. type: list elements: str aliases: - waitfor match: description: - The I(match) argument is used in conjunction with the I(wait_for) argument to specify the match policy. Valid values are C(all) or C(any). If the value is set to C(all) then all conditionals in the wait_for must be satisfied. If the value is set to C(any) then only one of the values must be satisfied. default: all type: str choices: - any - all retries: description: - Specifies the number of retries a command should be tried before it is considered failed. The command is run on the target device every retry and evaluated against the I(wait_for) conditionals. - The commands are run once when I(retries) is set to C(0). default: 9 type: int interval: description: - Configures the interval in seconds to wait between I(retries) of the command. If the command does not pass the specified conditions, the interval indicates how long to wait before trying the command again. default: 1 type: int notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025. - Running C(show system boot-messages all) will cause the module to hang since VyOS is using a custom pager setting to display the output of that command. - If a command sent to the device requires answering a prompt, it is possible to pass a dict containing I(command), I(answer) and I(prompt). See examples. - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). """ EXAMPLES = """ - name: show configuration on ethernet devices eth0 and eth1 vyos.vyos.vyos_command: commands: - show interfaces ethernet {{ item }} with_items: - eth0 - eth1 - name: run multiple commands and check if version output contains specific version string vyos.vyos.vyos_command: commands: - show version - show hardware cpu wait_for: - result[0] contains 'VyOS 1.1.7' - name: run command that requires answering a prompt vyos.vyos.vyos_command: commands: - command: rollback 1 prompt: Proceed with reboot? [confirm][y] answer: y """ RETURN = """ stdout: description: The set of responses from the commands returned: always apart from low level errors (such as action plugin) type: list sample: ['...', '...'] stdout_lines: description: The value of stdout split into a list returned: always type: list sample: [['...', '...'], ['...'], ['...']] failed_conditions: description: The list of conditionals that have failed returned: failed type: list sample: ['...', '...'] warnings: description: The list of warnings (if any) generated by module based on arguments returned: always type: list sample: ['...', '...'] """ import time from ansible.module_utils._text import to_text from ansible.module_utils.basic import AnsibleModule from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import ( Conditional, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( to_lines, transform_commands, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import run_commands def parse_commands(module, warnings): commands = transform_commands(module) if module.check_mode: for item in list(commands): if not item["command"].startswith("show"): warnings.append( "Only show commands are supported when using check mode, not " "executing %s" % item["command"], ) commands.remove(item) return commands def main(): spec = dict( commands=dict(type="list", required=True, elements="raw"), wait_for=dict(type="list", aliases=["waitfor"], elements="str"), match=dict(default="all", choices=["all", "any"]), retries=dict(default=9, type="int"), interval=dict(default=1, type="int"), ) module = AnsibleModule(argument_spec=spec, supports_check_mode=True) warnings = list() result = {"changed": False, "warnings": warnings} commands = parse_commands(module, warnings) wait_for = module.params["wait_for"] or list() conditionals = [] try: conditionals = [Conditional(c) for c in wait_for] except AttributeError as exc: module.fail_json(msg=to_text(exc)) retries = module.params["retries"] interval = module.params["interval"] match = module.params["match"] # Always run at least once, and then `retries` more times. for item in range(retries + 1): responses = run_commands(module, commands) for item in list(conditionals): if item(responses): if match == "any": conditionals = list() break conditionals.remove(item) if not conditionals: break time.sleep(interval) if conditionals: failed_conditions = [item.raw for item in conditionals] msg = "One or more conditional statements have not been satisfied" module.fail_json(msg=msg, failed_conditions=failed_conditions) result.update({"stdout": responses, "stdout_lines": list(to_lines(responses))}) module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_config.py b/plugins/modules/vyos_config.py index 666bb629..46e51f3b 100644 --- a/plugins/modules/vyos_config.py +++ b/plugins/modules/vyos_config.py @@ -1,451 +1,449 @@ #!/usr/bin/python # # 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 . # from __future__ import absolute_import, division, print_function - __metaclass__ = type DOCUMENTATION = """ module: vyos_config author: Nathaniel Case (@Qalthos) short_description: Manage VyOS configuration on remote device description: - This module provides configuration file management of VyOS devices. It provides arguments for managing both the configuration file and state of the active configuration. All configuration statements are based on `set` and `delete` commands in the device configuration. version_added: 1.0.0 extends_documentation_fragment: - vyos.vyos.vyos notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025. - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). - To ensure idempotency and correct diff the configuration lines in the relevant module options should be similar to how they appear if present in the running configuration on device including the indentation. options: lines: description: - The ordered set of commands that should be configured in the section. The commands must be the exact same commands as found in the device running-config as found in the device running-config to ensure idempotency and correct diff. Be sure to note the configuration command syntax as some commands are automatically modified by the device config parser. type: list elements: str src: description: - The C(src) argument specifies the path to the source config file to load. The source config file can either be in bracket format or set format. The source file can include Jinja2 template variables. The configuration lines in the source file should be similar to how it will appear if present in the running-configuration of the device including indentation to ensure idempotency and correct diff. type: path match: description: - The C(match) argument controls the method used to match against the current active configuration. By default, the desired config is matched against the active config and the deltas are loaded. If the C(match) argument is set to C(none) the active configuration is ignored and the configuration is always loaded. type: str default: line choices: - line - none backup: description: - The C(backup) argument will backup the current devices active configuration to the Ansible control host prior to making any changes. If the C(backup_options) value is not given, the backup file will be located in the backup folder in the playbook root directory or role root directory, if playbook is part of an ansible role. If the directory does not exist, it is created. type: bool default: false comment: description: - Allows a commit description to be specified to be included when the configuration is committed. If the configuration is not changed or committed, this argument is ignored. default: configured by vyos_config type: str confirm: description: - The C(confirm) argument will tell vyos to revert to the previous configuration if not explicitly confirmed after applying the new config. When set to C(automatic) this module will automatically confirm the configuration, if the current session remains working with the new config. When set to C(manual), this module does not issue the confirmation itself. type: str default: none choices: - automatic - manual - none confirm_timeout: description: - Minutes to wait for confirmation before reverting the configuration. Does not apply when C(confirm) is set to C(none) . type: int default: 10 config: description: - The C(config) argument specifies the base configuration to use to compare against the desired configuration. If this value is not specified, the module will automatically retrieve the current active configuration from the remote device. The configuration lines in the option value should be similar to how it will appear if present in the running-configuration of the device including indentation to ensure idempotency and correct diff. type: str save: description: - The C(save) argument controls whether or not changes made to the active configuration are saved to disk. This is independent of committing the config. When set to C(true), the active configuration is saved. type: bool default: false backup_options: description: - This is a dict object containing configurable options related to backup file path. The value of this option is read only when C(backup) is set to C(true), if C(backup) is set to C(false) this option will be silently ignored. suboptions: filename: description: - The filename to be used to store the backup configuration. If the filename is not given it will be generated based on the hostname, current time and date in format defined by _config.@ type: str dir_path: description: - This option provides the path ending with directory name in which the backup configuration file will be stored. If the directory does not exist it will be first created and the filename is either the value of C(filename) or default filename as described in C(filename) options description. If the path value is not given in that case a I(backup) directory will be created in the current working directory and backup configuration will be copied in C(filename) within I(backup) directory. type: path type: dict allow_password_change: description: - The C(allow_password_change) argument specifies whether any configuration lines which would change a user's password should be filtered out. By default only plaintext password changes are allowed and any encrypted-password keys are filtered out. In order to allow all password updates, both plaintext and encrypted, set this argument to C(all). type: str default: plaintext choices: - all - plaintext - encrypted - none """ EXAMPLES = """ - name: configure the remote device vyos.vyos.vyos_config: lines: - set system host-name {{ inventory_hostname }} - set service lldp - delete service dhcp-server - name: backup and load from file vyos.vyos.vyos_config: src: vyos.cfg backup: true - name: render a Jinja2 template onto the VyOS router vyos.vyos.vyos_config: src: vyos_template.j2 - name: revert after ten minutes, if connection is lost vyos.vyos.vyos_config: src: vyos_template.j2 confirm: automatic - name: for idempotency, use full-form commands vyos.vyos.vyos_config: lines: # - set int eth eth2 description 'OUTSIDE' - set interface ethernet eth2 description 'OUTSIDE' - name: configurable backup path vyos.vyos.vyos_config: backup: true backup_options: filename: backup.cfg dir_path: /home/user """ RETURN = """ commands: description: The list of configuration commands sent to the device returned: always type: list sample: ['...', '...'] filtered: description: The list of configuration commands removed to avoid a load failure returned: always type: list sample: ['...', '...'] backup_path: description: The full path to the backup file returned: when backup is yes type: str sample: /playbooks/ansible/backup/vyos_config.2016-07-16@22:28:34 filename: description: The name of the backup file returned: when backup is yes and filename is not specified in backup options type: str sample: vyos_config.2016-07-16@22:28:34 shortname: description: The full path to the backup file excluding the timestamp returned: when backup is yes and filename is not specified in backup options type: str sample: /playbooks/ansible/backup/vyos_config date: description: The date extracted from the backup file name returned: when backup is yes type: str sample: "2016-07-16" time: description: The time extracted from the backup file name returned: when backup is yes type: str sample: "22:28:34" """ import re from ansible.module_utils._text import to_text from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.connection import ConnectionError from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( get_config, get_connection, load_config, run_commands, ) - DEFAULT_COMMENT = "configured by vyos_config" PASSWORD_NEEDLE = re.compile( r"set system login user \S+ authentication (encrypted|plaintext)-password", ) def get_candidate(module): contents = module.params["src"] or module.params["lines"] if module.params["src"]: contents = contents.splitlines() if len(contents) > 0: line = contents[0].split() if len(line) > 0 and line[0] in ("set", "delete"): contents = format_commands(contents) contents = "\n".join(contents) return contents def format_commands(commands): """ This function format the input commands and removes the prepend white spaces for command lines having 'set' or 'delete' and it skips empty lines. :param commands: :return: list of commands """ return [ line.strip() if line.split()[0] in ("set", "delete") else line for line in commands if len(line.strip()) > 0 ] def diff_config(commands, config): config = [str(c).replace("'", "") for c in config.splitlines()] updates = list() visited = set() for line in commands: item = str(line).replace("'", "") if not item.startswith("set") and not item.startswith("delete"): raise ValueError("line must start with either `set` or `delete`") elif item.startswith("set") and item not in config: updates.append(line) elif item.startswith("delete"): if not config: updates.append(line) else: item = re.sub(r"delete", "set", item) for entry in config: if entry.startswith(item) and line not in visited: updates.append(line) visited.add(line) return list(updates) def sanitize_config(config, result, allow): result["filtered"] = list() if allow == "all": return index_to_filter = list() for index, line in enumerate(list(config)): found = PASSWORD_NEEDLE.search(line) if found is None: continue if allow == found[1]: continue result["filtered"].append(line) index_to_filter.append(index) # Delete all filtered configs for filter_index in sorted(index_to_filter, reverse=True): del config[filter_index] def run(module, result): # get the current active config from the node or passed in via # the config param config = module.params["config"] or get_config(module) # create the candidate config object from the arguments candidate = get_candidate(module) # create loadable config that includes only the configuration updates connection = get_connection(module) try: response = connection.get_diff( candidate=candidate, running=config, diff_match=module.params["match"], ) except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) commands = response.get("config_diff") allow_password_change = module.params["allow_password_change"] sanitize_config(commands, result, allow=allow_password_change) result["commands"] = commands commit = not module.check_mode comment = module.params["comment"] confirm = None if module.params["confirm"] == "automatic" or module.params["confirm"] == "manual": confirm = module.params["confirm_timeout"] diff = None if commands: diff = load_config(module, commands, commit=commit, comment=comment, confirm=confirm) if module.params["confirm"] == "automatic": run_commands(module, ["configure", "confirm", "exit"]) if result.get("filtered"): result["warnings"].append( "Some configuration commands were removed, please see the filtered key", ) result["changed"] = True if module._diff: result["diff"] = {"prepared": diff} def main(): backup_spec = dict(filename=dict(), dir_path=dict(type="path")) argument_spec = dict( src=dict(type="path"), lines=dict(type="list", elements="str"), match=dict(default="line", choices=["line", "none"]), comment=dict(default=DEFAULT_COMMENT), confirm=dict(choices=["automatic", "manual", "none"], default="none"), confirm_timeout=dict(type="int", default=10), config=dict(), backup=dict(type="bool", default=False), backup_options=dict(type="dict", options=backup_spec), save=dict(type="bool", default=False), allow_password_change=dict( default="plaintext", choices=["all", "encrypted", "plaintext", "none"], ), ) mutually_exclusive = [("lines", "src")] module = AnsibleModule( argument_spec=argument_spec, mutually_exclusive=mutually_exclusive, supports_check_mode=True, ) warnings = list() result = dict(changed=False, warnings=warnings) if module.params["backup"]: result["__backup__"] = get_config(module=module) if any((module.params["src"], module.params["lines"])): run(module, result) if module.params["save"]: diff = run_commands(module, commands=["configure", "compare saved"])[1] if diff not in { "[edit]", "No changes between working and saved configurations.\n\n[edit]", }: if not module.check_mode: run_commands(module, commands=["save"]) result["changed"] = True run_commands(module, commands=["exit"]) if result.get("changed") and any((module.params["src"], module.params["lines"])): msg = ( "To ensure idempotency and correct diff the input configuration lines should be" " similar to how they appear if present in" " the running configuration on device" ) if module.params["src"]: msg += " including the indentation" if "warnings" in result: result["warnings"].append(msg) else: result["warnings"] = msg module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_facts.py b/plugins/modules/vyos_facts.py index abc3c707..3d6d1b05 100644 --- a/plugins/modules/vyos_facts.py +++ b/plugins/modules/vyos_facts.py @@ -1,177 +1,176 @@ #!/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) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The module file for vyos_facts """ DOCUMENTATION = """ module: vyos_facts short_description: Get facts about vyos devices. description: - Collects facts from network devices running the vyos operating system. This module places the facts gathered in the fact tree keyed by the respective resource name. The facts module will always collect a base set of facts from the device and can enable or disable collection of additional facts. version_added: 1.0.0 author: - Nathaniel Case (@qalthos) - Nilashish Chakraborty (@Nilashishc) - Rohit Thakur (@rohitthakur2590) extends_documentation_fragment: - vyos.vyos.vyos notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025. - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). options: gather_subset: description: - When supplied, this argument will restrict the facts collected to a given subset. Possible values for this argument include C(all), C(default), C(config), C(neighbors) and C(min). Can specify a list of values to include a larger subset. Values can also be used with an initial C(!) to specify that a specific subset should not be collected. required: false default: 'min' type: list elements: str gather_network_resources: description: - When supplied, this argument will restrict the facts collected to a given subset. Possible values for this argument include all and the resources like interfaces. Can specify a list of values to include a larger subset. Values can also be used with an initial C(!) to specify that a specific subset should not be collected. Valid subsets are 'all', 'interfaces', 'l3_interfaces', 'lag_interfaces', 'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules', 'firewall_global', 'firewall_interfaces', 'ospfv3', 'ospfv2'. required: false type: list elements: str available_network_resources: description: When 'True' a list of network resources for which resource modules are available will be provided. type: bool default: false """ EXAMPLES = """ # Gather all facts - vyos.vyos.vyos_facts: gather_subset: all gather_network_resources: all # collect only the config and default facts - vyos.vyos.vyos_facts: gather_subset: config # collect everything exception the config - vyos.vyos.vyos_facts: gather_subset: '!config' # Collect only the interfaces facts - vyos.vyos.vyos_facts: gather_subset: - '!all' - '!min' gather_network_resources: - interfaces # Do not collect interfaces facts - vyos.vyos.vyos_facts: gather_network_resources: - '!interfaces' # Collect interfaces and minimal default facts - vyos.vyos.vyos_facts: gather_subset: min gather_network_resources: interfaces """ RETURN = """ ansible_net_config: description: The running-config from the device returned: when config is configured type: str ansible_net_commits: description: The set of available configuration revisions returned: when present type: list ansible_net_hostname: description: The configured system hostname returned: always type: str ansible_net_model: description: The device model string returned: always type: str ansible_net_serialnum: description: The serial number of the device returned: always type: str ansible_net_version: description: The version of the software running returned: always type: str ansible_net_neighbors: description: The set of LLDP neighbors returned: when interface is configured type: list ansible_net_gather_subset: description: The list of subsets gathered by the module returned: always type: list ansible_net_api: description: The name of the transport returned: always type: str ansible_net_python_version: description: The Python version Ansible controller is using returned: always type: str ansible_net_gather_network_resources: description: The list of fact resource subsets collected from the device returned: always type: list """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.facts.facts import ( FactsArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( FACT_RESOURCE_SUBSETS, Facts, ) def main(): """ Main entry point for module execution :returns: ansible_facts """ argument_spec = FactsArgs.argument_spec module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) warnings = [] ansible_facts = {} if module.params.get("available_network_resources"): ansible_facts["available_network_resources"] = sorted(FACT_RESOURCE_SUBSETS.keys()) result = Facts(module).get_facts() additional_facts, additional_warnings = result ansible_facts.update(additional_facts) warnings.extend(additional_warnings) module.exit_json(ansible_facts=ansible_facts, warnings=warnings) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_firewall_global.py b/plugins/modules/vyos_firewall_global.py index 50f92e91..9abeb5a1 100644 --- a/plugins/modules/vyos_firewall_global.py +++ b/plugins/modules/vyos_firewall_global.py @@ -1,1288 +1,1287 @@ #!/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_firewall_global """ from __future__ import absolute_import, division, print_function - __metaclass__ = type ANSIBLE_METADATA = { "metadata_version": "1.1", "status": ["preview"], "supported_by": "network", } DOCUMENTATION = """ --- module: vyos_firewall_global short_description: Firewall global resource module description: - This module manage global policies or configurations for firewall on VyOS devices. version_added: '1.0.0' notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025. - The provided examples of commands are valid for VyOS 1.4+ - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Rohit Thakur (@rohitthakur2590) options: config: description: - A dictionary of Firewall global configuration options. type: dict suboptions: route_redirects: description: - A dictionary of Firewall ICMP redirect and source route global configuration options. type: list elements: dict suboptions: afi: description: - Specifies IP address type type: str choices: - ipv4 - ipv6 required: true icmp_redirects: description: - Specifies whether to allow sending/receiving of IPv4/v6 ICMP redirect messages. type: dict suboptions: send: description: - Permits or denies transmitting packets ICMP redirect messages. type: bool receive: description: - Permits or denies receiving packets ICMP redirect messages. type: bool ip_src_route: description: - Specifies whether or not to process source route IP options. type: bool ping: description: - Policy for handling of all IPv4 ICMP echo requests. type: dict suboptions: all: description: - Enables or disables response to all IPv4 ICMP Echo Request (ping) messages. - The system responds to IPv4 ICMP Echo Request messages. type: bool broadcast: description: - Enables or disables response to broadcast IPv4 ICMP Echo Request and Timestamp Request messages. - IPv4 ICMP Echo and Timestamp Request messages are not processed. type: bool config_trap: description: - SNMP trap generation on firewall configuration changes. type: bool validation: description: - Specifies a policy for source validation by reversed path, as defined in RFC 3704. - (disable) No source validation is performed. - (loose) Enable Loose Reverse Path Forwarding as defined in RFC3704. - (strict) Enable Strict Reverse Path Forwarding as defined in RFC3704. type: str choices: - strict - loose - disable group: description: - Defines a group of objects for referencing in firewall rules. type: dict suboptions: address_group: description: - Defines a group of IP addresses for referencing in firewall rules. type: list elements: dict suboptions: afi: description: - Specifies IP address type type: str default: ipv4 choices: - ipv4 - ipv6 required: false name: description: - Name of the firewall address group. type: str required: true description: description: - Allows you to specify a brief description for the address group. type: str members: description: - Address-group members. - IPv4 address to match. - IPv4 range to match. type: list elements: dict suboptions: address: description: - IP address. type: str network_group: description: - Defines a group of networks for referencing in firewall rules. type: list elements: dict suboptions: afi: description: - Specifies network address type type: str default: ipv4 choices: - ipv4 - ipv6 required: false name: description: - Name of the firewall network group. type: str required: true description: description: - Allows you to specify a brief description for the network group. type: str members: description: - Adds an IPv4 network to the specified network group. - The format is ip-address/prefix. type: list elements: dict suboptions: address: description: - IP address. type: str port_group: description: - Defines a group of ports for referencing in firewall rules. type: list elements: dict suboptions: name: description: - Name of the firewall port group. type: str required: true description: description: - A brief description for the port group. type: str members: description: - Port-group member. type: list elements: dict suboptions: port: description: - Specified port. type: str log_martians: description: - Specifies whether or not to record packets with invalid addresses in the log. - (True) Logs packets with invalid addresses. - (False) Does not log packets with invalid addresses. type: bool syn_cookies: description: - Specifies policy for using TCP SYN cookies with IPv4. - (True) Enables TCP SYN cookies with IPv4. - (False) Disables TCP SYN cookies with IPv4. type: bool twa_hazards_protection: description: - RFC1337 TCP TIME-WAIT assassination hazards protection. type: bool state_policy: description: - Specifies global firewall state-policy. type: list elements: dict suboptions: connection_type: description: - Specifies connection type. type: str choices: - established - invalid - related action: description: - Action for packets part of an established connection. type: str choices: - accept - drop - reject log: description: - Enable logging of packets part of an established connection. type: bool log_level: description: - Specify log level for packets logged. - Only available in 1.4+ type: str choices: - emerg - alert - crit - err - warn - notice - info - debug 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 'firewall') type: str state: description: - The state the configuration should be left in. type: str choices: - merged - replaced - deleted - gathered - rendered - parsed default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@vyos# run show configuration commands | grep firewall # # - name: Merge the provided configuration with the existing running configuration vyos.vyos.vyos_firewall_global: config: validation: strict config_trap: true log_martians: true syn_cookies: true twa_hazards_protection: true ping: all: true broadcast: true state_policy: - connection_type: established action: accept log: true log_level: emerg - connection_type: invalid action: reject route_redirects: - afi: ipv4 ip_src_route: true icmp_redirects: send: true receive: false group: address_group: - name: MGMT-HOSTS description: This group has the Management hosts address list members: - address: 192.0.1.1 - address: 192.0.1.3 - address: 192.0.1.5 network_group: - name: MGMT description: This group has the Management network addresses members: - address: 192.0.1.0/24 state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": [] # # "commands": [ # "set firewall group address-group MGMT-HOSTS address 192.0.1.1", # "set firewall group address-group MGMT-HOSTS address 192.0.1.3", # "set firewall group address-group MGMT-HOSTS address 192.0.1.5", # "set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list'", # "set firewall group address-group MGMT-HOSTS", # "set firewall group network-group MGMT network 192.0.1.0/24", # "set firewall group network-group MGMT description 'This group has the Management network addresses'", # "set firewall group network-group MGMT", # "set firewall global-options ip-src-route 'enable'", # "set firewall global-options receive-redirects 'disable'", # "set firewall global-options send-redirects 'enable'", # "set firewall global-options config-trap 'enable'", # "set firewall global-options state-policy established action 'accept'", # "set firewall global-options state-policy established log, # "set firewall global-options state-policy established log-level 'emerg'", # "set firewall global-options state-policy invalid action 'reject'", # "set firewall global-options broadcast-ping 'enable'", # "set firewall global-options all-ping 'enable'", # "set firewall global-options log-martians 'enable'", # "set firewall global-options twa-hazards-protection 'enable'", # "set firewall global-options syn-cookies 'enable'", # "set firewall global-options source-validation 'strict'" # ] # # "after": { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "This group has the Management hosts address list", # "members": [ # { # "address": "192.0.1.1" # }, # { # "address": "192.0.1.3" # }, # { # "address": "192.0.1.5" # } # ], # "name": "MGMT-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep firewall # set firewall global-options all-ping 'enable' # set firewall global-options broadcast-ping 'enable' # set firewall global-options config-trap 'enable' # set firewall global-options ip-src-route 'enable' # set firewall global-options log-martians 'enable' # set firewall global-options receive-redirects 'disable' # set firewall global-options send-redirects 'enable' # set firewall global-options source-validation 'strict' # set firewall global-options state-policy established action 'accept' # set firewall global-options state-policy established log 'enable' # set firewall global-options state-policy invalid action 'reject' # set firewall global-options syn-cookies 'enable' # set firewall global-options twa-hazards-protection 'enable' # set firewall group address-group MGMT-HOSTS address '192.0.1.1' # set firewall group address-group MGMT-HOSTS address '192.0.1.3' # set firewall group address-group MGMT-HOSTS address '192.0.1.5' # set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list' # set firewall group network-group MGMT description 'This group has the Management network addresses' # set firewall group network-group MGMT network '192.0.1.0/24' # # # Using parsed # # - name: Render the commands for provided configuration vyos.vyos.vyos_firewall_global: running_config: | set firewall global-options all-ping 'enable' set firewall global-options broadcast-ping 'enable' set firewall global-options config-trap 'enable' set firewall global-options ip-src-route 'enable' set firewall global-options log-martians 'enable' set firewall global-options receive-redirects 'disable' set firewall global-options send-redirects 'enable' set firewall global-options source-validation 'strict' set firewall global-options state-policy established action 'accept' set firewall global-options state-policy established log 'enable' set firewall global-options state-policy invalid action 'reject' set firewall global-options syn-cookies 'enable' set firewall global-options twa-hazards-protection 'enable'" set firewall group address-group ENG-HOSTS address '192.0.3.1' set firewall group address-group ENG-HOSTS address '192.0.3.2' set firewall group address-group ENG-HOSTS description 'Sales office hosts address list' set firewall group address-group SALES-HOSTS address '192.0.2.1' set firewall group address-group SALES-HOSTS address '192.0.2.2' set firewall group address-group SALES-HOSTS address '192.0.2.3' set firewall group address-group SALES-HOSTS description 'Sales office hosts address list' set firewall group network-group MGMT description 'This group has the Management network addresses' set firewall group network-group MGMT network '192.0.1.0/24' state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.3.1" # }, # { # "address": "192.0.3.2" # } # ], # "name": "ENG-HOSTS" # }, # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.2.1" # }, # { # "address": "192.0.2.2" # }, # { # "address": "192.0.2.3" # } # ], # "name": "SALES-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # } # # # Using deleted # # Before state # ------------- # # vyos@192# run show configuration commands | grep firewall # set firewall global-options all-ping 'enable' # set firewall global-options broadcast-ping 'enable' # set firewall global-options config-trap 'enable' # set firewall global-options ip-src-route 'enable' # set firewall global-options log-martians 'enable' # set firewall global-options receive-redirects 'disable' # set firewall global-options send-redirects 'enable' # set firewall global-options source-validation 'strict' # set firewall global-options state-policy established action 'accept' # set firewall global-options state-policy established log 'enable' # set firewall global-options state-policy invalid action 'reject' # set firewall global-options syn-cookies 'enable' # set firewall global-options twa-hazards-protection 'enable' # set firewall group address-group MGMT-HOSTS address '192.0.1.1' # set firewall group address-group MGMT-HOSTS address '192.0.1.3' # set firewall group address-group MGMT-HOSTS address '192.0.1.5' # set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list' # set firewall group network-group MGMT description 'This group has the Management network addresses' # set firewall group network-group MGMT network '192.0.1.0/24' - name: Delete attributes of firewall. vyos.vyos.vyos_firewall_global: config: state_policy: config_trap: log_martians: syn_cookies: twa_hazards_protection: route_redirects: ping: group: state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "This group has the Management hosts address list", # "members": [ # { # "address": "192.0.1.1" # }, # { # "address": "192.0.1.3" # }, # { # "address": "192.0.1.5" # } # ], # "name": "MGMT-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # "commands": [ # "delete firewall global-options source-validation", # "delete firewall global-options group", # "delete firewall global-options log-martians", # "delete firewall global-options ip-src-route", # "delete firewall global-options receive-redirects", # "delete firewall global-options send-redirects", # "delete firewall global-options config-trap", # "delete firewall global-options state-policy", # "delete firewall global-options syn-cookies", # "delete firewall global-options broadcast-ping", # "delete firewall global-options all-ping", # "delete firewall global-options twa-hazards-protection" # ] # # "after": [] # # After state # ------------ # vyos@192# run show configuration commands | grep firewall # set 'firewall' # # # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall global-options all-ping 'enable' # set firewall global-options broadcast-ping 'enable' # set firewall global-options config-trap 'enable' # set firewall global-options ip-src-route 'enable' # set firewall global-options log-martians 'enable' # set firewall global-options receive-redirects 'disable' # set firewall global-options send-redirects 'enable' # set firewall global-options source-validation 'strict' # set firewall global-options state-policy established action 'accept' # set firewall global-options state-policy established log 'enable' # set firewall global-options state-policy invalid action 'reject' # set firewall global-options syn-cookies 'enable' # set firewall global-options twa-hazards-protection 'enable' # set firewall group address-group MGMT-HOSTS address '192.0.1.1' # set firewall group address-group MGMT-HOSTS address '192.0.1.3' # set firewall group address-group MGMT-HOSTS address '192.0.1.5' # set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list' # set firewall group network-group MGMT description 'This group has the Management network addresses' # set firewall group network-group MGMT network '192.0.1.0/24' - name: Replace firewall global attributes configuration. vyos.vyos.vyos_firewall_global: config: validation: strict config_trap: true log_martians: true syn_cookies: true twa_hazards_protection: true ping: null all: true broadcast: true state_policy: - connection_type: established action: accept log: true - connection_type: invalid action: reject route_redirects: - afi: ipv4 ip_src_route: true icmp_redirects: send: true receive: false group: address_group: - name: SALES-HOSTS description: Sales office hosts address list members: - address: 192.0.2.1 - address: 192.0.2.2 - address: 192.0.2.3 - name: ENG-HOSTS description: Sales office hosts address list members: - address: 192.0.3.1 - address: 192.0.3.2 network_group: - name: MGMT description: This group has the Management network addresses members: - address: 192.0.1.0/24 state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "This group has the Management hosts address list", # "members": [ # { # "address": "192.0.1.1" # }, # { # "address": "192.0.1.3" # }, # { # "address": "192.0.1.5" # } # ], # "name": "MGMT-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # # "commands": [ # "delete firewall group address-group MGMT-HOSTS", # "set firewall group address-group SALES-HOSTS address 192.0.2.1", # "set firewall group address-group SALES-HOSTS address 192.0.2.2", # "set firewall group address-group SALES-HOSTS address 192.0.2.3", # "set firewall group address-group SALES-HOSTS description 'Sales office hosts address list'", # "set firewall group address-group SALES-HOSTS", # "set firewall group address-group ENG-HOSTS address 192.0.3.1", # "set firewall group address-group ENG-HOSTS address 192.0.3.2", # "set firewall group address-group ENG-HOSTS description 'Sales office hosts address list'", # "set firewall group address-group ENG-HOSTS" # ] # # "after": { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.3.1" # }, # { # "address": "192.0.3.2" # } # ], # "name": "ENG-HOSTS" # }, # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.2.1" # }, # { # "address": "192.0.2.2" # }, # { # "address": "192.0.2.3" # } # ], # "name": "SALES-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep firewall # set firewall global-options all-ping 'enable' # set firewall global-options broadcast-ping 'enable' # set firewall global-options config-trap 'enable' # set firewall global-options ip-src-route 'enable' # set firewall global-options log-martians 'enable' # set firewall global-options receive-redirects 'disable' # set firewall global-options send-redirects 'enable' # set firewall global-options source-validation 'strict' # set firewall global-options state-policy established action 'accept' # set firewall global-options state-policy established log 'enable' # set firewall global-options state-policy invalid action 'reject' # set firewall global-options syn-cookies 'enable' # set firewall global-options twa-hazards-protection 'enable' # set firewall group address-group ENG-HOSTS address '192.0.3.1' # set firewall group address-group ENG-HOSTS address '192.0.3.2' # set firewall group address-group ENG-HOSTS description 'Sales office hosts address list' # set firewall group address-group SALES-HOSTS address '192.0.2.1' # set firewall group address-group SALES-HOSTS address '192.0.2.2' # set firewall group address-group SALES-HOSTS address '192.0.2.3' # set firewall group address-group SALES-HOSTS description 'Sales office hosts address list' # set firewall group network-group MGMT description 'This group has the Management network addresses' # set firewall group network-group MGMT network '192.0.1.0/24' # # # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep firewall # set firewall global-options all-ping 'enable' # set firewall global-options broadcast-ping 'enable' # set firewall global-options config-trap 'enable' # set firewall global-options ip-src-route 'enable' # set firewall global-options log-martians 'enable' # set firewall global-options receive-redirects 'disable' # set firewall global-options send-redirects 'enable' # set firewall global-options source-validation 'strict' # set firewall global-options state-policy established action 'accept' # set firewall global-options state-policy established log 'enable' # set firewall global-options state-policy invalid action 'reject' # set firewall global-options syn-cookies 'enable' # set firewall global-options twa-hazards-protection 'enable' # set firewall group address-group ENG-HOSTS address '192.0.3.1' # set firewall group address-group ENG-HOSTS address '192.0.3.2' # set firewall group address-group ENG-HOSTS description 'Sales office hosts address list' # set firewall group address-group SALES-HOSTS address '192.0.2.1' # set firewall group address-group SALES-HOSTS address '192.0.2.2' # set firewall group address-group SALES-HOSTS address '192.0.2.3' # set firewall group address-group SALES-HOSTS description 'Sales office hosts address list' # set firewall group network-group MGMT description 'This group has the Management network addresses' # set firewall group network-group MGMT network '192.0.1.0/24' - name: Gather firewall global config with provided configurations vyos.vyos.vyos_firewall_global: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.3.1" # }, # { # "address": "192.0.3.2" # } # ], # "name": "ENG-HOSTS" # }, # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.2.1" # }, # { # "address": "192.0.2.2" # }, # { # "address": "192.0.2.3" # } # ], # "name": "SALES-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep firewall # set firewall global-options all-ping 'enable' # set firewall global-options broadcast-ping 'enable' # set firewall global-options config-trap 'enable' # set firewall global-options ip-src-route 'enable' # set firewall global-options log-martians 'enable' # set firewall global-options receive-redirects 'disable' # set firewall global-options send-redirects 'enable' # set firewall global-options source-validation 'strict' # set firewall global-options state-policy established action 'accept' # set firewall global-options state-policy established log 'enable' # set firewall global-options state-policy invalid action 'reject' # set firewall global-options syn-cookies 'enable' # set firewall global-options twa-hazards-protection 'enable' # set firewall group address-group ENG-HOSTS address '192.0.3.1' # set firewall group address-group ENG-HOSTS address '192.0.3.2' # set firewall group address-group ENG-HOSTS description 'Sales office hosts address list' # set firewall group address-group SALES-HOSTS address '192.0.2.1' # set firewall group address-group SALES-HOSTS address '192.0.2.2' # set firewall group address-group SALES-HOSTS address '192.0.2.3' # set firewall group address-group SALES-HOSTS description 'Sales office hosts address list' # set firewall group network-group MGMT description 'This group has the Management network addresses' # set firewall group network-group MGMT network '192.0.1.0/24' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_firewall_global: config: validation: strict config_trap: true log_martians: true syn_cookies: true twa_hazards_protection: true ping: null all: true broadcast: true state_policy: - connection_type: established action: accept log: true - connection_type: invalid action: reject route_redirects: - afi: ipv4 ip_src_route: true icmp_redirects: null send: true receive: false group: address_group: - name: SALES-HOSTS description: Sales office hosts address list members: - address: 192.0.2.1 - address: 192.0.2.2 - address: 192.0.2.3 - name: ENG-HOSTS description: Sales office hosts address list members: - address: 192.0.3.1 - address: 192.0.3.2 network_group: - name: MGMT description: This group has the Management network addresses members: - address: 192.0.1.0/24 state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set firewall group address-group SALES-HOSTS address 192.0.2.1", # "set firewall group address-group SALES-HOSTS address 192.0.2.2", # "set firewall group address-group SALES-HOSTS address 192.0.2.3", # "set firewall group address-group SALES-HOSTS description 'Sales office hosts address list'", # "set firewall group address-group SALES-HOSTS", # "set firewall group address-group ENG-HOSTS address 192.0.3.1", # "set firewall group address-group ENG-HOSTS address 192.0.3.2", # "set firewall group address-group ENG-HOSTS description 'Sales office hosts address list'", # "set firewall group address-group ENG-HOSTS", # "set firewall group network-group MGMT network 192.0.1.0/24", # "set firewall group network-group MGMT description 'This group has the Management network addresses'", # "set firewall group network-group MGMT", # "set firewall global-options ip-src-route 'enable'", # "set firewall global-options receive-redirects 'disable'", # "set firewall global-options send-redirects 'enable'", # "set firewall global-options config-trap 'enable'", # "set firewall global-options state-policy established action 'accept'", # "set firewall global-options state-policy established log, # "set firewall global-options state-policy invalid action 'reject'", # "set firewall global-options broadcast-ping 'enable'", # "set firewall global-options all-ping 'enable'", # "set firewall global-options log-martians 'enable'", # "set firewall global-options twa-hazards-protection 'enable'", # "set firewall global-options syn-cookies 'enable'", # "set firewall global-options source-validation 'strict'" # ] # """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - "set firewall group address-group ENG-HOSTS" - "set firewall group address-group ENG-HOSTS address 192.0.3.1" rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - "set firewall group address-group ENG-HOSTS" - "set firewall group address-group ENG-HOSTS address 192.0.3.1" gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_global.firewall_global import ( Firewall_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_global.firewall_global import ( Firewall_global, ) 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", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Firewall_globalArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Firewall_global(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_firewall_interfaces.py b/plugins/modules/vyos_firewall_interfaces.py index ae17bc13..a3210b65 100644 --- a/plugins/modules/vyos_firewall_interfaces.py +++ b/plugins/modules/vyos_firewall_interfaces.py @@ -1,1222 +1,1221 @@ #!/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_firewall_interfaces """ from __future__ import absolute_import, division, print_function - __metaclass__ = type ANSIBLE_METADATA = { "metadata_version": "1.1", "status": ["preview"], "supported_by": "network", } DOCUMENTATION = """ --- module: vyos_firewall_interfaces version_added: '1.0.0' short_description: Firewall interfaces resource module description: Manage firewall rules of interfaces on VyOS network devices. (1.3-) author: - Rohit Thakur (@rohitthakur2590) notes: - Deprecated in VyOS 1.4+, firewalls are no longer connected directly to interfaces. See the Firewall Configuration documentation for how to establish a connection betwen the firewall rulesets and the flow, interface, or zone. options: config: description: A list of firewall rules options for interfaces. type: list elements: dict suboptions: name: description: - Name/Identifier for the interface. type: str required: true access_rules: description: - Specifies firewall rules attached to the interfaces. type: list elements: dict suboptions: afi: description: - Specifies the AFI for the Firewall rules to be configured on this interface. type: str choices: - ipv4 - ipv6 required: true rules: description: - Specifies the firewall rules for the provided AFI. type: list elements: dict suboptions: name: description: - Specifies the name of the IPv4/IPv6 Firewall rule for the interface. type: str direction: description: - Specifies the direction of packets that the firewall rule will be applied on. type: str choices: - in - local - out required: true 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 'firewall'). type: str state: description: - The state the configuration should be left in. type: str choices: - merged - replaced - overridden - deleted - parsed - rendered - gathered default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@192# run show configuration commands | grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # - name: Merge the provided configuration with the existing running configuration vyos.vyos.vyos_firewall_interfaces: config: - access_rules: - afi: ipv4 rules: - name: INBOUND direction: in - name: OUTBOUND direction: out - name: LOCAL direction: local - afi: ipv6 rules: - name: V6-LOCAL direction: local name: eth1 - access_rules: - afi: ipv4 rules: - name: INBOUND direction: in - name: OUTBOUND direction: out - name: LOCAL direction: local - afi: ipv6 rules: - name: V6-LOCAL direction: local name: eth3 state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": [ # { # "name": "eth0" # }, # { # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ] # # "commands": [ # "set interfaces ethernet eth1 firewall in name 'INBOUND'", # "set interfaces ethernet eth1 firewall out name 'OUTBOUND'", # "set interfaces ethernet eth1 firewall local name 'LOCAL'", # "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'", # "set interfaces ethernet eth3 firewall in name 'INBOUND'", # "set interfaces ethernet eth3 firewall out name 'OUTBOUND'", # "set interfaces ethernet eth3 firewall local name 'LOCAL'", # "set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'" # ] # # "after": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth3" # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall in name 'INBOUND' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall local name 'LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth3 firewall local name 'LOCAL' # set interfaces ethernet eth3 firewall out name 'OUTBOUND' # Using merged # Before state: # ------------- # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces bonding 'bond0' # set interfaces bonding 'bond1' # set interfaces bonding bond2 'ip' # set interfaces bonding bond2 'ipv6' # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 'ip' # set interfaces ethernet eth0 'ipv6' # set interfaces ethernet eth0 smp_affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet 'eth1' # set interfaces ethernet 'eth2' - name: Merge provided configuration with device configuration vyos.vyos.vyos_interfaces: config: - name: eth2 description: Configured by Ansible enabled: true vifs: - vlan_id: 200 description: VIF 200 - ETH2 - name: eth3 description: Configured by Ansible mtu: 1500 - name: bond1 description: Bond - 1 mtu: 1200 - name: vti2 description: VTI - 2 enabled: false state: merged # Task Output # ----------- # before: # - enabled: true # name: lo # - enabled: true # name: eth3 # - enabled: true # name: eth2 # - enabled: true # name: eth1 # - duplex: auto # enabled: true # name: eth0 # speed: auto # commands: # - set interfaces ethernet eth2 description 'Configured by Ansible' # - set interfaces ethernet eth2 vif 200 # - set interfaces ethernet eth2 vif 200 description 'VIF 200 - ETH2' # - set interfaces ethernet eth3 description 'Configured by Ansible' # - set interfaces ethernet eth3 mtu '1500' # - set interfaces bonding bond1 # - set interfaces bonding bond1 description 'Bond - 1' # - set interfaces bonding bond1 mtu '1200' # - set interfaces vti vti2 # - set interfaces vti vti2 description 'VTI - 2' # - set interfaces vti vti2 disable # after: # - description: Bond - 1 # enabled: true # mtu: 1200 # name: bond1 # - enabled: true # name: lo # - description: VTI - 2 # enabled: false # name: vti2 # - description: Configured by Ansible # enabled: true # mtu: 1500 # name: eth3 # - description: Configured by Ansible # enabled: true # name: eth2 # vifs: # - description: VIF 200 - ETH2 # enabled: true # vlan_id: '200' # - enabled: true # name: eth1 # - duplex: auto # enabled: true # name: eth0 # speed: auto # After state: # ------------ # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces bonding bond1 description 'Bond - 1' # set interfaces bonding bond1 mtu '1200' # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth2 description 'Configured by Ansible' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth2 vif 200 description 'VIF 200 - ETH2' # set interfaces ethernet eth3 description 'Configured by Ansible' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 mtu '1500' # set interfaces loopback lo # set interfaces vti vti2 description 'VTI - 2' # set interfaces vti vti2 disable # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall in name 'INBOUND' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall local name 'LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth3 firewall local name 'LOCAL' # set interfaces ethernet eth3 firewall out name 'OUTBOUND' # - name: >- Replace device configurations of listed firewall interfaces with provided configurations vyos.vyos.vyos_firewall_interfaces: config: - name: eth1 access_rules: - afi: ipv4 rules: - name: OUTBOUND direction: out - afi: ipv6 rules: - name: V6-LOCAL direction: local - name: eth3 access_rules: - afi: ipv4 rules: - name: INBOUND direction: in state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth3" # } # ] # # "commands": [ # "delete interfaces ethernet eth1 firewall in name", # "delete interfaces ethernet eth1 firewall local name", # "delete interfaces ethernet eth3 firewall local name", # "delete interfaces ethernet eth3 firewall out name", # "delete interfaces ethernet eth3 firewall local ipv6-name" # ] # # "after": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # } # ] # } # ], # "name": "eth3" # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall 'in' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall 'local' # set interfaces ethernet eth3 firewall 'out' # Using overridden # # Before state # -------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall 'in' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall 'local' # set interfaces ethernet eth3 firewall 'out' # - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_firewall_interfaces: config: - name: eth3 access_rules: - afi: ipv4 rules: - name: INBOUND direction: out state: overridden # # # ------------------------- # Module Execution Result # ------------------------- # # "before":[ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # } # ] # } # ], # "name": "eth3" # } # ] # # "commands": [ # "delete interfaces ethernet eth1 firewall", # "delete interfaces ethernet eth3 firewall in name", # "set interfaces ethernet eth3 firewall out name 'INBOUND'" # ] # # "after": [ # { # "name": "eth0" # }, # { # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "out", # "name": "INBOUND" # } # ] # } # ], # "name": "eth3" # } # ] # # # After state # ------------ # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth3 firewall 'in' # set interfaces ethernet eth3 firewall 'local' # set interfaces ethernet eth3 firewall out name 'INBOUND' # Using deleted per interface name # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall in name 'INBOUND' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall local name 'LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth3 firewall local name 'LOCAL' # set interfaces ethernet eth3 firewall out name 'OUTBOUND' # - name: Delete firewall interfaces based on interface name. vyos.vyos.vyos_firewall_interfaces: config: - name: eth1 - name: eth3 state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth3" # } # ] # "commands": [ # "delete interfaces ethernet eth1 firewall", # "delete interfaces ethernet eth3 firewall" # ] # # "after" : [] # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # Using deleted per afi # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall in name 'INBOUND' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall local name 'LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth3 firewall local name 'LOCAL' # set interfaces ethernet eth3 firewall out name 'OUTBOUND' # - name: Delete firewall interfaces config per afi. vyos.vyos.vyos_firewall_interfaces: config: - name: eth1 access_rules: - afi: ipv4 - afi: ipv6 state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "commands": [ # "delete interfaces ethernet eth1 firewall in name", # "delete interfaces ethernet eth1 firewall out name", # "delete interfaces ethernet eth1 firewall local name", # "delete interfaces ethernet eth1 firewall local ipv6-name" # ] # # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # Using deleted without config # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall in name 'INBOUND' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall local name 'LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth3 firewall local name 'LOCAL' # set interfaces ethernet eth3 firewall out name 'OUTBOUND' # - name: Delete firewall interfaces config when empty config provided. vyos.vyos.vyos_firewall_interfaces: state: deleted # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # # # ------------------------ # Module Execution Results # ------------------------ # # "commands": [ # "delete interfaces ethernet eth1 firewall", # "delete interfaces ethernet eth1 firewall" # ] # # Using parsed # # - name: Parse the provided configuration vyos.vyos.vyos_firewall_interfaces: running_config: "set interfaces ethernet eth1 firewall in name 'INBOUND' set interfaces ethernet eth1 firewall out name 'OUTBOUND' set interfaces ethernet eth1 firewall local name 'LOCAL' set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' set interfaces ethernet eth2 firewall in name 'INBOUND' set interfaces ethernet eth2 firewall out name 'OUTBOUND' set interfaces ethernet eth2 firewall local name 'LOCAL' set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth2" # }, # { # "name": "eth3" # } # ] # Using gathered # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall 'in' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall 'local' # set interfaces ethernet eth3 firewall 'out' # - name: Gather listed firewall interfaces. vyos.vyos.vyos_firewall_interfaces: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # } # ] # } # ], # "name": "eth3" # } # ] # # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall 'in' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall 'local' # set interfaces ethernet eth3 firewall 'out' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_firewall_interfaces: config: - name: eth2 access_rules: - afi: ipv4 rules: - name: INGRESS direction: in - name: OUTGRESS direction: out - name: DROP direction: local state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set interfaces ethernet eth2 firewall in name 'INGRESS'", # "set interfaces ethernet eth2 firewall out name 'OUTGRESS'", # "set interfaces ethernet eth2 firewall local name 'DROP'", # "set interfaces ethernet eth2 firewall local ipv6-name 'LOCAL'" # ] """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'" - "set interfaces ethernet eth3 firewall in name 'INBOUND'" rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'" - "set interfaces ethernet eth3 firewall in name 'INBOUND'" gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_interfaces.firewall_interfaces import ( Firewall_interfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_interfaces.firewall_interfaces import ( Firewall_interfaces, ) 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", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Firewall_interfacesArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Firewall_interfaces(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_firewall_rules.py b/plugins/modules/vyos_firewall_rules.py index 7213abd5..fde9dcb8 100644 --- a/plugins/modules/vyos_firewall_rules.py +++ b/plugins/modules/vyos_firewall_rules.py @@ -1,1679 +1,1678 @@ #!/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_firewall_rules """ from __future__ import absolute_import, division, print_function - __metaclass__ = type ANSIBLE_METADATA = { "metadata_version": "1.1", "status": ["preview"], "supported_by": "network", } DOCUMENTATION = """ --- module: vyos_firewall_rules version_added: '1.0.0' short_description: Firewall rules resource module description: This module manages firewall rule-set attributes on VyOS devices author: - Rohit Thakur (@rohitthakur2590) - Gaige B. Paulsen (@gaige) notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025. - The provided examples of commands are valid for VyOS 1.4+ - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). options: config: description: A dictionary of Firewall rule-set options. type: list elements: dict suboptions: afi: description: - Specifies the type of rule-set. type: str choices: - ipv4 - ipv6 required: true rule_sets: description: - The Firewall rule-set list. type: list elements: dict suboptions: filter: description: - Filter type (exclusive to "name"). - Supported in 1.4 and later. type: str choices: ['input', 'output', 'forward'] name: description: - Firewall rule set name. - Required for 1.3- and optional for 1.4+. type: str default_action: description: - Default action for rule-set. - drop (Drop if no prior rules are hit (default)) - reject (Drop and notify source if no prior rules are hit) - accept (Accept if no prior rules are hit) - jump (Jump to another rule-set, 1.4+) type: str choices: ['drop', 'reject', 'accept', 'jump'] default_jump_target: description: - Default jump target if the default action is jump. - Only valid in 1.4 and later. - Only valid when default_action = jump. type: str description: description: - Rule set description. type: str enable_default_log: description: - Option to log packets hitting default-action. type: bool rules: description: - A dictionary that specifies the rule-set configurations. type: list elements: dict suboptions: number: description: - Rule number. type: int required: true description: description: - Description of this rule. type: str action: description: - Specifying the action. - inspect is available < 1.4 - continue, return, jump, queue, synproxy are available >= 1.4 type: str choices: - drop - reject - accept - inspect - continue - return - jump - offload - queue - synproxy destination: description: - Specifying the destination parameters. type: dict suboptions: address: description: - Destination ip address subnet or range. - IPv4/6 address, subnet or range to match. - Match everything except the specified address, subnet or range. - Destination ip address subnet or range. type: str group: description: - Destination group. type: dict suboptions: address_group: description: - Group of addresses. type: str network_group: description: - Group of networks. type: str port_group: description: - Group of ports. type: str port: description: - Multiple destination ports can be specified as a comma-separated list. - The whole list can also be "negated" using '!'. - For example:'!22,telnet,http,123,1001-1005'. type: str disable: description: - Option to disable firewall rule. - aliased to disabled type: bool aliases: ["disabled"] fragment: description: - IP fragment match. type: str choices: - match-frag - match-non-frag icmp: description: - ICMP type and code information. type: dict suboptions: type_name: description: - ICMP type-name. type: str choices: - any - echo-reply - destination-unreachable - network-unreachable - host-unreachable - protocol-unreachable - port-unreachable - fragmentation-needed - source-route-failed - network-unknown - host-unknown - network-prohibited - host-prohibited - TOS-network-unreachable - TOS-host-unreachable - communication-prohibited - host-precedence-violation - precedence-cutoff - source-quench - redirect - network-redirect - host-redirect - TOS-network-redirect - TOS-host-redirect - echo-request - router-advertisement - router-solicitation - time-exceeded - ttl-zero-during-transit - ttl-zero-during-reassembly - parameter-problem - ip-header-bad - required-option-missing - timestamp-request - timestamp-reply - address-mask-request - address-mask-reply - ping - pong - ttl-exceeded code: description: - ICMP code. type: int type: description: - ICMP type. type: int inbound_interface: description: - Inbound interface. - Only valid in 1.4 and later. type: dict suboptions: name: description: - Interface name. - Can have wildcards type: str group: description: - Interface group. type: str ipsec: description: - Inbound ip sec packets. - VyOS 1.4 and older match-ipsec/match-none - VyOS 1.5 and later require -in/-out suffixes type: str choices: - match-ipsec - match-none - match-ipsec-in - match-ipsec-out - match-none-in - match-none-out jump_target: description: - Jump target if the action is jump. - Only valid in 1.4 and later. - Only valid when action = jump. type: str limit: description: - Rate limit using a token bucket filter. type: dict suboptions: burst: description: - Maximum number of packets to allow in excess of rate. type: int rate: description: - format for rate (integer/time unit). - any one of second, minute, hour or day may be used to specify time unit. - eg. 1/second implies rule to be matched at an average of once per second. type: dict suboptions: number: description: - This is the integer value. type: int unit: description: - This is the time unit. type: str log: description: - Option to log packets matching rule. type: str choices: ['disable', 'enable'] offload_target: description: - Match flowtable object. type: str outbound_interface: description: - Match outbound interface. - Only valid in 1.4 and later. type: dict suboptions: name: description: - Interface name. - Can have wildcards type: str group: description: - Interface group. type: str packet_length: description: - Packet length match. - Only valid in 1.4 and later. - Multiple values from 1 to 65535 and ranges are supported type: list elements: dict suboptions: length: description: - Packet length or range. type: str packet_length_exclude: description: - Packet length match. - Only valid in 1.4 and later. - Multiple values from 1 to 65535 and ranges are supported type: list elements: dict suboptions: length: description: - Packet length or range. type: str packet_type: description: - Packet type match. type: str choices: ['broadcast', 'multicast', 'host', 'other'] protocol: description: - Protocol to match (protocol name in /etc/protocols or protocol number or all). - IP protocol name from /etc/protocols (e.g. "tcp" or "udp"). - <0-255> IP protocol number. - tcp_udp Both TCP and UDP. - all All IP protocols. - (!)All IP protocols except for the specified name or number. type: str queue: description: - Queue options. - Only valid in 1.4 and later. - Only valid when action = queue. - Can be a queue number or range. type: str queue_options: description: - Queue options. - Only valid in 1.4 and later. - Only valid when action = queue. type: str choices: ['bypass', 'fanout'] recent: description: - Parameters for matching recently seen sources. type: dict suboptions: count: description: - Source addresses seen more than N times. type: int time: description: - Source addresses seen in the last N seconds. - Since 1.4, this is a string of second/minute/hour type: str source: description: - Source parameters. type: dict suboptions: address: description: - Source ip address subnet or range. - IPv4/6 address, subnet or range to match. - Match everything except the specified address, subnet or range. - Source ip address subnet or range. type: str group: description: - Source group. type: dict suboptions: address_group: description: - Group of addresses. type: str network_group: description: - Group of networks. type: str port_group: description: - Group of ports. type: str port: description: - Multiple source ports can be specified as a comma-separated list. - The whole list can also be "negated" using '!'. - For example:'!22,telnet,http,123,1001-1005'. type: str mac_address: description: - MAC address to match. - Match everything except the specified MAC address. type: str fqdn: description: - Fully qualified domain name. - Available in 1.4 and later. type: str state: description: - Session state. type: dict suboptions: established: description: - Established state. type: bool invalid: description: - Invalid state. type: bool new: description: - New state. type: bool related: description: - Related state. type: bool synproxy: description: - SYN proxy options. - Only valid in 1.4 and later. - Only valid when action = synproxy. type: dict suboptions: mss: description: - Adjust MSS (501-65535) type: int window_scale: description: - Window scale (1-14). type: int tcp: description: - TCP flags to match. type: dict suboptions: flags: description: - list of tcp flags to be matched - 5.0 breaking change to support 1.4+ and 1.3- type: list elements: dict suboptions: flag: description: - TCP flag to be matched. - syn, ack, fin, rst, urg, psh, all (1.3-) - syn, ack, fin, rst, urg, psh, cwr, ecn (1.4+) type: str choices: - ack - cwr - ecn - fin - psh - rst - syn - urg - all invert: description: - Invert the match. type: bool time: description: - Time to match rule. type: dict suboptions: utc: description: - Interpret times for startdate, stopdate, starttime and stoptime to be UTC. type: bool monthdays: description: - Monthdays to match rule on. type: str startdate: description: - Date to start matching rule. type: str starttime: description: - Time of day to start matching rule. type: str stopdate: description: - Date to stop matching rule. type: str stoptime: description: - Time of day to stop matching rule. type: str weekdays: description: - Weekdays to match rule on. 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 firewall). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state the configuration should be left in type: str choices: - merged - replaced - overridden - deleted - gathered - rendered - parsed default: merged """ EXAMPLES = """ # Using deleted to delete firewall rules based on rule-set name # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv4 name Downlink default-action 'accept' # set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' # set firewall ipv4 name Downlink rule 501 action 'accept' # set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' # set firewall ipv4 name Downlink rule 502 action 'reject' # set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec' - name: Delete attributes of given firewall rules. vyos.vyos.vyos_firewall_rules: config: - afi: ipv4 rule_sets: - name: Downlink state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # "commands": [ # "delete firewall ipv4 name Downlink" # ] # # "after": [] # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall group address-group 'inbound' # Using deleted to delete firewall rules based on afi # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall group address-group 'inbound' # set firewall ipv4 name Downlink default-action 'accept' # set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' # set firewall ipv4 name Downlink rule 501 action 'accept' # set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' # set firewall ipv4 name Downlink rule 502 action 'reject' # set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec' - name: Delete attributes of given firewall rules. vyos.vyos.vyos_firewall_rules: config: - afi: ipv4 state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # "commands": [ # "delete firewall ipv4 name" # ] # # "after": [] # After state # ------------ # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # Using deleted to delete all the the firewall rules when provided config is empty # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv4 name Downlink default-action 'accept' # set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' # set firewall ipv4 name Downlink rule 501 action 'accept' # set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' # set firewall ipv4 name Downlink rule 502 action 'reject' # set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec' # - name: Delete attributes of given firewall rules. vyos.vyos.vyos_firewall_rules: state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # "commands": [ # "delete firewall ipv4 name" # ] # # "after": [] # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall group address-group 'inbound' # Using merged # # Before state: # ------------- # # vyos@vyos# run show configuration commands | grep firewall # set firewall group address-group 'inbound' # - name: Merge the provided configuration with the existing running configuration vyos.vyos.vyos_firewall_rules: config: - afi: ipv6 rule_sets: - name: UPLINK description: This is ipv6 specific rule-set default_action: accept rules: - number: 1 action: accept description: Fwipv6-Rule 1 is configured by Ansible ipsec: match-ipsec - number: 2 action: accept description: Fwipv6-Rule 2 is configured by Ansible ipsec: match-ipsec - afi: ipv4 rule_sets: - name: INBOUND description: IPv4 INBOUND rule set default_action: accept rules: - number: 101 action: accept description: Rule 101 is configured by Ansible ipsec: match-ipsec - number: 102 action: reject description: Rule 102 is configured by Ansible ipsec: match-ipsec - number: 103 action: accept description: Rule 103 is configured by Ansible destination: group: address_group: inbound source: address: 192.0.2.0 state: established: true new: false invalid: false related: true state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": [] # # "commands": [ # "set firewall ipv6 name UPLINK default-action 'accept'", # "set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set'", # "set firewall ipv6 name UPLINK rule 1 action 'accept'", # "set firewall ipv6 name UPLINK rule 1", # "set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible'", # "set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec'", # "set firewall ipv6 name UPLINK rule 2 action 'accept'", # "set firewall ipv6 name UPLINK rule 2", # "set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible'", # "set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND default-action 'accept'", # "set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set'", # "set firewall ipv4 name INBOUND rule 101 action 'accept'", # "set firewall ipv4 name INBOUND rule 101", # "set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND rule 102 action 'reject'", # "set firewall ipv4 name INBOUND rule 102", # "set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 103 destination group address-group inbound", # "set firewall ipv4 name INBOUND rule 103", # "set firewall ipv4 name INBOUND rule 103 source address 192.0.2.0", # "set firewall ipv4 name INBOUND rule 103 state established", # "set firewall ipv4 name INBOUND rule 103 state related", # "set firewall ipv4 name INBOUND rule 103 action 'accept'" # ] # # "after": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 102 action 'reject' # set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 103 action 'accept' # set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall ipv4 name INBOUND rule 103 destination group address-group 'inbound' # set firewall ipv4 name INBOUND rule 103 source address '192.0.2.0' # set firewall ipv4 name INBOUND rule 103 state established # set firewall ipv4 name INBOUND rule 103 state related # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 102 action 'reject' # set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 103 action 'accept' # set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall ipv4 name INBOUND rule 103 destination group address-group 'inbound' # set firewall ipv4 name INBOUND rule 103 source address '192.0.2.0' # set firewall ipv4 name INBOUND rule 103 state established # set firewall ipv4 name INBOUND rule 103 state related # - name: >- Replace device configurations of listed firewall rules with provided configurations vyos.vyos.vyos_firewall_rules: config: - afi: ipv6 rule_sets: - name: UPLINK description: This is ipv6 specific rule-set default_action: accept - afi: ipv4 rule_sets: - name: INBOUND description: IPv4 INBOUND rule set default_action: accept rules: - number: 101 action: accept description: Rule 101 is configured by Ansible ipsec: match-ipsec - number: 104 action: reject description: Rule 104 is configured by Ansible ipsec: match-none state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # "commands": [ # "delete firewall ipv6 name UPLINK rule 1", # "delete firewall ipv6 name UPLINK rule 2", # "delete firewall ipv4 name INBOUND rule 102", # "delete firewall ipv4 name INBOUND rule 103", # "set firewall ipv4 name INBOUND rule 104 action 'reject'", # "set firewall ipv4 name INBOUND rule 104 description 'Rule 104 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 104", # "set firewall ipv4 name INBOUND rule 104 ipsec 'match-none'" # ] # # "after": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK" # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 104 is configured by Ansible", # "ipsec": "match-none", # "number": 104 # } # ] # } # ] # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 104 action 'reject' # set firewall ipv4 name INBOUND rule 104 description 'Rule 104 is configured by Ansible' # set firewall ipv4 name INBOUND rule 104 ipsec 'match-none' # Using overridden # # Before state # -------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 104 action 'reject' # set firewall ipv4 name INBOUND rule 104 description 'Rule 104 is configured by Ansible' # set firewall ipv4 name INBOUND rule 104 ipsec 'match-none' # - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_firewall_rules: config: - afi: ipv4 rule_sets: - name: Downlink description: IPv4 INBOUND rule set default_action: accept rules: - number: 501 action: accept description: Rule 501 is configured by Ansible ipsec: match-ipsec - number: 502 action: reject description: Rule 502 is configured by Ansible ipsec: match-ipsec state: overridden # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK" # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 104 is configured by Ansible", # "ipsec": "match-none", # "number": 104 # } # ] # } # ] # } # ] # # "commands": [ # "delete firewall ipv6 name UPLINK", # "delete firewall ipv4 name INBOUND", # "set firewall ipv4 name Downlink default-action 'accept'", # "set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set'", # "set firewall ipv4 name Downlink rule 501 action 'accept'", # "set firewall ipv4 name Downlink rule 501", # "set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible'", # "set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec'", # "set firewall ipv4 name Downlink rule 502 action 'reject'", # "set firewall ipv4 name Downlink rule 502", # "set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible'", # "set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec'" # # # "after": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # # # After state # ------------ # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv4 name Downlink default-action 'accept' # set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' # set firewall ipv4 name Downlink rule 501 action 'accept' # set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' # set firewall ipv4 name Downlink rule 502 action 'reject' # set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec' # Using gathered # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 102 action 'reject' # set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 103 action 'accept' # set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall ipv4 name INBOUND rule 103 destination group address-group 'inbound' # set firewall ipv4 name INBOUND rule 103 source address '192.0.2.0' # set firewall ipv4 name INBOUND rule 103 state established # set firewall ipv4 name INBOUND rule 103 state related # - name: Gather listed firewall rules with provided configurations vyos.vyos.vyos_firewall_rules: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 102 action 'reject' # set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 103 action 'accept' # set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall ipv4 name INBOUND rule 103 destination group address-group 'inbound' # set firewall ipv4 name INBOUND rule 103 source address '192.0.2.0' # set firewall ipv4 name INBOUND rule 103 state established # set firewall ipv4 name INBOUND rule 103 state related # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_firewall_rules: config: - afi: ipv6 rule_sets: - name: UPLINK description: This is ipv6 specific rule-set default_action: accept - afi: ipv4 rule_sets: - name: INBOUND description: IPv4 INBOUND rule set default_action: accept rules: - number: 101 action: accept description: Rule 101 is configured by Ansible ipsec: match-ipsec - number: 102 action: reject description: Rule 102 is configured by Ansible ipsec: match-ipsec - number: 103 action: accept description: Rule 103 is configured by Ansible destination: group: address_group: inbound source: address: 192.0.2.0 state: established: true new: false invalid: false related: true state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set firewall ipv6 name UPLINK default-action 'accept'", # "set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set'", # "set firewall ipv4 name INBOUND default-action 'accept'", # "set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set'", # "set firewall ipv4 name INBOUND rule 101 action 'accept'", # "set firewall ipv4 name INBOUND rule 101", # "set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND rule 102 action 'reject'", # "set firewall ipv4 name INBOUND rule 102", # "set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 103 destination group address-group inbound", # "set firewall ipv4 name INBOUND rule 103", # "set firewall ipv4 name INBOUND rule 103 source address 192.0.2.0", # "set firewall ipv4 name INBOUND rule 103 state established", # "set firewall ipv4 name INBOUND rule 103 state related", # "set firewall ipv4 name INBOUND rule 103 action 'accept'" # ] # Using parsed # # - name: Parse the commands for provided configuration vyos.vyos.vyos_firewall_rules: running_config: "set firewall group address-group 'inbound' set firewall ipv4 name Downlink default-action 'accept' set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' set firewall ipv4 name Downlink rule 501 action 'accept' set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' set firewall ipv4 name Downlink rule 502 action 'reject' set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - "set firewall ipv4 name Downlink default-action 'accept'" - "set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set'" - "set firewall ipv4 name Downlink rule 501 action 'accept'" - "set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible'" - "set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec'" rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - "set firewall ipv4 name Downlink default-action 'accept'" - "set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set'" - "set firewall ipv4 name Downlink rule 501 action 'accept'" - "set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible'" - "set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec'" gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import ( Firewall_rulesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_rules.firewall_rules import ( Firewall_rules, ) 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", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Firewall_rulesArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Firewall_rules(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_hostname.py b/plugins/modules/vyos_hostname.py index 480b011f..27f2081f 100644 --- a/plugins/modules/vyos_hostname.py +++ b/plugins/modules/vyos_hostname.py @@ -1,293 +1,292 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The module file for vyos_hostname """ from __future__ import absolute_import, division, print_function - __metaclass__ = type DOCUMENTATION = """ module: vyos_hostname version_added: 2.8.0 short_description: Manages hostname resource module description: This module manages the hostname attribute of Vyos network devices author: Gomathi Selvi Srinivasan (@GomathiselviS) notes: - Tested against vyos 1.1.8 - This module works with connection C(network_cli). - The Configuration defaults of the Vyos network devices are supposed to hinder idempotent behavior of plays options: config: description: Hostname configuration. type: dict suboptions: hostname: description: set hostname for VYOS. 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 host-name"). - 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: choices: - merged - replaced - overridden - deleted - gathered - parsed - rendered default: merged description: - The state the configuration should be left in - The states I(rendered), I(gathered) and I(parsed) does not perform any change on the device. - The state I(rendered) will transform the configuration in C(config) option to platform specific CLI commands which will be returned in the I(rendered) key within the result. For state I(rendered) active connection to remote host is not required. - The states I(merged), I(replaced) and I(overridden) have identical behaviour for this module. - The state I(gathered) will fetch the running configuration from device and transform it into structured data in the format as per the resource module argspec and the value is returned in the I(gathered) key within the result. - The state I(parsed) reads the configuration from C(running_config) option and transforms it into JSON format as per the resource module parameters and the value is returned in the I(parsed) key within the result. The value of C(running_config) option should be the same format as the output of command I(show configuration commands | grep host-name) executed on device. For state I(parsed) active connection to remote host is not required. type: str """ EXAMPLES = """ # Using merged # # Before state: # ------------- # test#show configuration commands | grep host-name # set system host-name 'vyostest' - name: Apply the provided configuration vyos.vyos.vyos_hostname: config: hostname: vyos state: merged # Commands Fired: # --------------- # "commands": [ # "hostname vyos", # ], # # After state: # ------------ # test#show configuration commands | grep host-name # set system host-name 'vyos' # Using deleted # # Before state: # ------------- # test#show configuration commands | grep host-name # set system host-name 'vyos' # - name: Remove all existing configuration vyos.vyos.vyos_hostname: state: deleted # Commands Fired: # --------------- # "commands": [ # "no hostname vyosTest", # ], # # After state: # ------------ # test#show configuration commands | grep host-name # Using overridden # # Before state: # ------------- # test#show configuration commands | grep host-name # set system host-name 'vyos' - name: Override commands with provided configuration vyos.vyos.vyos_hostname: config: hostname: vyosTest state: overridden # Commands Fired: # --------------- # "commands": [ # "hostname vyosTest", # ], # # After state: # ------------ # test#show configuration commands | grep host-name # set system host-name 'vyosTest' # Using replaced # # Before state: # ------------- # test#show configuration commands | grep host-name # set system host-name 'vyosTest' - name: Replace commands with provided configuration vyos.vyos.vyos_hostname: config: hostname: vyos state: replaced # After state: # ------------ # test#show configuration commands | grep host-name # set system host-name 'vyos' # Using gathered # # Before state: # ------------- # test#show configuration commands | grep host-name # set system host-name 'vyos' - name: Gather listed hostname config vyos.vyos.vyos_hostname: state: gathered # Module Execution Result: # ------------------------ # "gathered": { # "hostname": "vyos" # }, # Using state: rendered # Rendered play: # -------------- - name: Render the commands for provided configuration vyos.vyos.vyos_hostname: config: hostname: vyosTest state: rendered # Module Execution Result: # ------------------------ # "rendered": [ # "set system host-name vyosTest", # ] # Using state: parsed # File: parsed.cfg # ---------------- # set system host-name 'vyos' # Parsed play: # ------------ - name: Parse the provided configuration with the existing running configuration vyos.vyos.vyos_hostname: running_config: "{{ lookup('file', 'parsed.cfg') }}" state: parsed # Module Execution Result: # ------------------------ # "parsed": { # "hostname": "vyos" # } """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: list sample: - sample command 1 - sample command 2 - sample command 3 rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - sample command 1 - sample command 2 - sample command 3 gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.hostname.hostname import ( HostnameArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.hostname.hostname import ( Hostname, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ module = AnsibleModule( argument_spec=HostnameArgs.argument_spec, mutually_exclusive=[["config", "running_config"]], required_if=[ ["state", "merged", ["config"]], ["state", "replaced", ["config"]], ["state", "overridden", ["config"]], ["state", "rendered", ["config"]], ["state", "parsed", ["running_config"]], ], supports_check_mode=True, ) result = Hostname(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_interfaces.py b/plugins/modules/vyos_interfaces.py index 82b4fd70..937ce2bf 100644 --- a/plugins/modules/vyos_interfaces.py +++ b/plugins/modules/vyos_interfaces.py @@ -1,909 +1,908 @@ #!/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_interfaces """ from __future__ import absolute_import, division, print_function - __metaclass__ = type ANSIBLE_METADATA = { "metadata_version": "1.1", "status": ["preview"], "supported_by": "network", } DOCUMENTATION = """ --- module: vyos_interfaces version_added: '2.9.0' short_description: Manages interface attributes of VyOS network devices. description: - This module manages the interface attributes on VyOS network devices. - This module supports managing base attributes of Ethernet, Bonding, VXLAN, Loopback and Virtual Tunnel Interfaces. notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025 - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Nilashish Chakraborty (@nilashishc) - Rohit Thakur (@rohitthakur2590) options: config: description: The provided interface configuration. type: list elements: dict suboptions: name: description: - Full name of the interface, e.g. eth0, eth1, bond0, vti1, vxlan2. type: str required: true description: description: - Interface description. type: str duplex: description: - Interface duplex mode. - Applicable for Ethernet interfaces only. choices: - full - half - auto type: str enabled: default: true description: - Administrative state of the interface. - Set the value to C(true) to administratively enable the interface or C(false) to disable it. type: bool aliases: ['enable'] mtu: description: - MTU for a specific interface. Refer to vendor documentation for valid values. - Applicable for Ethernet, Bonding, VXLAN and Virtual Tunnel interfaces. type: int speed: description: - Interface link speed. - Applicable for Ethernet interfaces only. type: str choices: - auto - '10' - '100' - '1000' - '2500' - '10000' vifs: description: - Virtual sub-interfaces related configuration. - 802.1Q VLAN interfaces are represented as virtual sub-interfaces in VyOS. type: list elements: dict suboptions: vlan_id: description: - Identifier for the virtual sub-interface. type: int description: description: - Virtual sub-interface description. type: str enabled: description: - Administrative state of the virtual sub-interface. - Set the value to C(true) to administratively enable the interface or C(false) to disable it. type: bool default: true aliases: ['enable'] mtu: description: - MTU for the virtual sub-interface. - Refer to vendor documentation for valid values. type: int 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 interfaces). - 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 of the configuration after module completion. type: str choices: - merged - replaced - overridden - deleted - rendered - gathered - parsed default: merged """ EXAMPLES = """ # Using merged # Before state: # ------------- # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces bonding 'bond0' # set interfaces bonding 'bond1' # set interfaces bonding bond2 'ip' # set interfaces bonding bond2 'ipv6' # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 'ip' # set interfaces ethernet eth0 'ipv6' # set interfaces ethernet eth0 smp_affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet 'eth1' # set interfaces ethernet 'eth2' - name: Merge provided configuration with device configuration vyos.vyos.vyos_interfaces: config: - name: eth2 description: Configured by Ansible enabled: true vifs: - vlan_id: 200 description: VIF 200 - ETH2 - name: eth3 description: Configured by Ansible mtu: 1500 - name: bond1 description: Bond - 1 mtu: 1200 - name: vti2 description: VTI - 2 enabled: false state: merged # Task Output # ----------- # before: # - enabled: true # name: lo # - enabled: true # name: eth3 # - enabled: true # name: eth2 # - enabled: true # name: eth1 # - duplex: auto # enabled: true # name: eth0 # speed: auto # commands: # - set interfaces ethernet eth2 description 'Configured by Ansible' # - set interfaces ethernet eth2 vif 200 # - set interfaces ethernet eth2 vif 200 description 'VIF 200 - ETH2' # - set interfaces ethernet eth3 description 'Configured by Ansible' # - set interfaces ethernet eth3 mtu '1500' # - set interfaces bonding bond1 # - set interfaces bonding bond1 description 'Bond - 1' # - set interfaces bonding bond1 mtu '1200' # - set interfaces vti vti2 # - set interfaces vti vti2 description 'VTI - 2' # - set interfaces vti vti2 disable # after: # - description: Bond - 1 # enabled: true # mtu: 1200 # name: bond1 # - enabled: true # name: lo # - description: VTI - 2 # enabled: false # name: vti2 # - description: Configured by Ansible # enabled: true # mtu: 1500 # name: eth3 # - description: Configured by Ansible # enabled: true # name: eth2 # vifs: # - description: VIF 200 - ETH2 # enabled: true # vlan_id: '200' # - enabled: true # name: eth1 # - duplex: auto # enabled: true # name: eth0 # speed: auto # After state: # ------------ # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces bonding bond1 description 'Bond - 1' # set interfaces bonding bond1 mtu '1200' # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth2 description 'Configured by Ansible' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth2 vif 200 description 'VIF 200 - ETH2' # set interfaces ethernet eth3 description 'Configured by Ansible' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 mtu '1500' # set interfaces loopback lo # set interfaces vti vti2 description 'VTI - 2' # set interfaces vti vti2 disable # Using replaced # Before state: # ------------- # vyos:~$ show configuration commands | grep eth # set interfaces bonding bond1 description 'Bond - 1' # set interfaces bonding bond1 mtu '1400' # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 description 'Management Interface for the Appliance' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:f3:6c:b5' # set interfaces ethernet eth0 smp_affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 description 'Configured by Ansible Eng Team' # set interfaces ethernet eth1 duplex 'full' # set interfaces ethernet eth1 hw-id '08:00:27:ad:ef:65' # set interfaces ethernet eth1 smp_affinity 'auto' # set interfaces ethernet eth1 speed '100' # set interfaces ethernet eth2 description 'Configured by Ansible' # set interfaces ethernet eth2 duplex 'full' # set interfaces ethernet eth2 hw-id '08:00:27:ab:4e:79' # set interfaces ethernet eth2 mtu '500' # set interfaces ethernet eth2 smp_affinity 'auto' # set interfaces ethernet eth2 speed '100' # set interfaces ethernet eth2 vif 200 description 'Configured by Ansible' # set interfaces ethernet eth3 description 'Configured by Ansible' # set interfaces ethernet eth3 duplex 'full' # set interfaces ethernet eth3 hw-id '08:00:27:17:3c:85' # set interfaces ethernet eth3 mtu '1500' # set interfaces ethernet eth3 smp_affinity 'auto' # set interfaces ethernet eth3 speed '100' # set interfaces loopback lo - name: Replace device configurations of listed interfaces with provided configurations vyos.vyos.vyos_interfaces: config: - name: eth2 description: Replaced by Ansible - name: eth3 description: Replaced by Ansible - name: eth1 description: Replaced by Ansible state: replaced # Task Output # ----------- # before: # - description: Bond - 1 # enabled: true # mtu: 1400 # name: bond1 # - enabled: true # name: lo # - description: Configured by Ansible # duplex: full # enabled: true # mtu: 1500 # name: eth3 # speed: '100' # - description: Configured by Ansible # duplex: full # enabled: true # mtu: 500 # name: eth2 # speed: '100' # vifs: # - description: VIF 200 - ETH2 # enabled: true # vlan_id: '200' # - description: Configured by Ansible Eng Team # duplex: full # enabled: true # name: eth1 # speed: '100' # - description: Management Interface for the Appliance # duplex: auto # enabled: true # name: eth0 # speed: auto # commands: # - delete interfaces ethernet eth2 speed # - delete interfaces ethernet eth2 duplex # - delete interfaces ethernet eth2 mtu # - delete interfaces ethernet eth2 vif 200 # - set interfaces ethernet eth2 description 'Replaced by Ansible' # - delete interfaces ethernet eth3 speed # - delete interfaces ethernet eth3 duplex # - delete interfaces ethernet eth3 mtu # - set interfaces ethernet eth3 description 'Replaced by Ansible' # - delete interfaces ethernet eth1 speed # - delete interfaces ethernet eth1 duplex # - set interfaces ethernet eth1 description 'Replaced by Ansible' # after: # - description: Bond - 1 # enabled: true # mtu: 1400 # name: bond1 # - enabled: true # name: lo # - description: Replaced by Ansible # enabled: true # name: eth3 # - description: Replaced by Ansible # enabled: true # name: eth2 # - description: Replaced by Ansible # enabled: true # name: eth1 # - description: Management Interface for the Appliance # duplex: auto # enabled: true # name: eth0 # speed: auto # After state: # ------------ # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces bonding bond1 description 'Bond - 1' # set interfaces bonding bond1 mtu '1400' # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 description 'Management Interface for the Appliance' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 description 'Replaced by Ansible' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth2 description 'Replaced by Ansible' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth3 description 'Replaced by Ansible' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces loopback lo # Using overridden # Before state: # ------------- # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 description 'Ethernet Interface - 0' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 mtu '1200' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 description 'Configured by Ansible Eng Team' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 mtu '100' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth1 vif 100 description 'VIF 100 - ETH1' # set interfaces ethernet eth1 vif 100 disable # set interfaces ethernet eth2 description 'Configured by Ansible Team (Admin Down)' # set interfaces ethernet eth2 disable # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 mtu '600' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth3 description 'Configured by Ansible Network' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces loopback lo # set interfaces vti vti1 description 'Virtual Tunnel Interface - 1' # set interfaces vti vti1 mtu '68' - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_interfaces: config: - name: eth0 description: Outbound Interface For The Appliance speed: auto duplex: auto - name: eth2 speed: auto duplex: auto - name: eth3 mtu: 1200 state: overridden # Task Output # ----------- # before: # - enabled: true # name: lo # - description: Virtual Tunnel Interface - 1 # enabled: true # mtu: 68 # name: vti1 # - description: Configured by Ansible Network # enabled: true # name: eth3 # - description: Configured by Ansible Team (Admin Down) # enabled: false # mtu: 600 # name: eth2 # - description: Configured by Ansible Eng Team # enabled: true # mtu: 100 # name: eth1 # vifs: # - description: VIF 100 - ETH1 # enabled: false # vlan_id: '100' # - description: Ethernet Interface - 0 # duplex: auto # enabled: true # mtu: 1200 # name: eth0 # speed: auto # commands: # - delete interfaces vti vti1 description # - delete interfaces vti vti1 mtu # - delete interfaces ethernet eth1 description # - delete interfaces ethernet eth1 mtu # - delete interfaces ethernet eth1 vif 100 # - delete interfaces ethernet eth0 mtu # - set interfaces ethernet eth0 description 'Outbound Interface For The Appliance' # - delete interfaces ethernet eth2 description # - delete interfaces ethernet eth2 mtu # - set interfaces ethernet eth2 duplex 'auto' # - delete interfaces ethernet eth2 disable # - set interfaces ethernet eth2 speed 'auto' # - delete interfaces ethernet eth3 description # - set interfaces ethernet eth3 mtu '1200' # after: # - enabled: true # name: lo # - enabled: true # name: vti1 # - enabled: true # mtu: 1200 # name: eth3 # - duplex: auto # enabled: true # name: eth2 # speed: auto # - enabled: true # name: eth1 # - description: Outbound Interface For The Appliance # duplex: auto # enabled: true # name: eth0 # speed: auto # After state: # ------------ # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 description 'Outbound Interface For The Appliance' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth2 duplex 'auto' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth2 speed 'auto' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 mtu '1200' # set interfaces loopback lo # set interfaces vti vti1 # Using deleted # Before state: # ------------- # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces bonding bond0 mtu '1300' # set interfaces bonding bond1 description 'LAG - 1' # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 description 'Outbound Interface for this appliance' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 description 'Configured by Ansible Network' # set interfaces ethernet eth1 duplex 'full' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth1 speed '100' # set interfaces ethernet eth2 description 'Configured by Ansible' # set interfaces ethernet eth2 disable # set interfaces ethernet eth2 duplex 'full' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 mtu '600' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth2 speed '100' # set interfaces ethernet eth3 description 'Configured by Ansible Network' # set interfaces ethernet eth3 duplex 'full' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 speed '100' # set interfaces loopback lo - name: Delete attributes of given interfaces (Note - This won't delete the interfaces themselves) vyos.vyos.vyos_interfaces: config: - name: bond1 - name: eth1 - name: eth2 - name: eth3 state: deleted # Task Output # ----------- # before: # - enabled: true # mtu: 1300 # name: bond0 # - description: LAG - 1 # enabled: true # name: bond1 # - enabled: true # name: lo # - description: Configured by Ansible Network # duplex: full # enabled: true # name: eth3 # speed: '100' # - description: Configured by Ansible # duplex: full # enabled: false # mtu: 600 # name: eth2 # speed: '100' # - description: Configured by Ansible Network # duplex: full # enabled: true # name: eth1 # speed: '100' # - description: Outbound Interface for this appliance # duplex: auto # enabled: true # name: eth0 # speed: auto # commands: # - delete interfaces bonding bond1 description # - delete interfaces ethernet eth1 speed # - delete interfaces ethernet eth1 duplex # - delete interfaces ethernet eth1 description # - delete interfaces ethernet eth2 speed # - delete interfaces ethernet eth2 disable # - delete interfaces ethernet eth2 duplex # - delete interfaces ethernet eth2 disable # - delete interfaces ethernet eth2 description # - delete interfaces ethernet eth2 disable # - delete interfaces ethernet eth2 mtu # - delete interfaces ethernet eth2 disable # - delete interfaces ethernet eth3 speed # - delete interfaces ethernet eth3 duplex # - delete interfaces ethernet eth3 description # after: # - enabled: true # mtu: 1300 # name: bond0 # - enabled: true # name: bond1 # - enabled: true # name: lo # - enabled: true # name: eth3 # - enabled: true # name: eth2 # - enabled: true # name: eth1 # - description: Outbound Interface for this appliance # duplex: auto # enabled: true # name: eth0 # speed: auto # After state: # ------------ # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces bonding bond0 mtu '1300' # set interfaces bonding bond1 # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 description 'Outbound Interface for this appliance' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces loopback lo # Using gathered # Before state: # ------------- # vyos@192# run show configuration commands | grep interfaces # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19' # set interfaces ethernet eth0 smp_affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 description 'Configured by Ansible' # set interfaces ethernet eth1 duplex 'auto' # set interfaces ethernet eth1 mtu '1500' # set interfaces ethernet eth1 speed 'auto' # set interfaces ethernet eth1 vif 200 description 'VIF - 200' # set interfaces ethernet eth2 description 'Configured by Ansible' # set interfaces ethernet eth2 duplex 'auto' # set interfaces ethernet eth2 mtu '1500' # set interfaces ethernet eth2 speed 'auto' # set interfaces ethernet eth2 vif 200 description 'VIF - 200' # - name: Gather listed interfaces with provided configurations vyos.vyos.vyos_interfaces: state: gathered # Task output # ----------- # gathered: # - description: Configured by Ansible # duplex: auto # enabled: true # mtu: 1500 # name: eth2 # speed: auto # vifs: # - description: VIF - 200 # enabled: true # vlan_id: 200 # - description: Configured by Ansible # duplex: auto # enabled: true # mtu: 1500 # name: eth1 # speed: auto # vifs: # - description: VIF - 200 # enabled: true # vlan_id: 200 # - duplex: auto # enabled: true # name: eth0 # speed: auto # Using rendered - name: Render the commands for provided configuration vyos.vyos.vyos_interfaces: config: - name: eth0 enabled: true duplex: auto speed: auto - name: eth1 description: Configured by Ansible - Interface 1 mtu: 1500 speed: auto duplex: auto enabled: true vifs: - vlan_id: 100 description: Eth1 - VIF 100 mtu: 400 enabled: true - vlan_id: 101 description: Eth1 - VIF 101 enabled: true - name: eth2 description: Configured by Ansible - Interface 2 (ADMIN DOWN) mtu: 600 enabled: false state: rendered # Task Output # ----------- # rendered: # - set interfaces ethernet eth0 duplex 'auto' # - set interfaces ethernet eth0 speed 'auto' # - delete interfaces ethernet eth0 disable # - set interfaces ethernet eth1 duplex 'auto' # - delete interfaces ethernet eth1 disable # - set interfaces ethernet eth1 speed 'auto' # - set interfaces ethernet eth1 description 'Configured by Ansible - Interface 1' # - set interfaces ethernet eth1 mtu '1500' # - set interfaces ethernet eth1 vif 100 description 'Eth1 - VIF 100' # - set interfaces ethernet eth1 vif 100 mtu '400' # - set interfaces ethernet eth1 vif 101 description 'Eth1 - VIF 101' # - set interfaces ethernet eth2 disable # - set interfaces ethernet eth2 description 'Configured by Ansible - Interface 2 (ADMIN # DOWN)' # - set interfaces ethernet eth2 mtu '600' # Using parsed - name: Parse the configuration. vyos.vyos.vyos_interfaces: running_config: "set interfaces ethernet eth0 address 'dhcp' set interfaces ethernet eth0 duplex 'auto' set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19' set interfaces ethernet eth0 smp_affinity 'auto' set interfaces ethernet eth0 speed 'auto' set interfaces ethernet eth1 description 'Configured by Ansible' set interfaces ethernet eth1 duplex 'auto' set interfaces ethernet eth1 mtu '1500' set interfaces ethernet eth1 speed 'auto' set interfaces ethernet eth1 vif 200 description 'VIF - 200' set interfaces ethernet eth2 description 'Configured by Ansible' set interfaces ethernet eth2 duplex 'auto' set interfaces ethernet eth2 mtu '1500' set interfaces ethernet eth2 speed 'auto' set interfaces ethernet eth2 vif 200 description 'VIF - 200'" state: parsed # Task output # ----------- # parsed: # - description: Configured by Ansible # duplex: auto # enabled: true # mtu: 1500 # name: eth2 # speed: auto # vifs: # - description: VIF - 200 # enabled: true # vlan_id: 200 # - description: Configured by Ansible # duplex: auto # enabled: true # mtu: 1500 # name: eth1 # speed: auto # vifs: # - description: VIF - 200 # enabled: true # vlan_id: 200 # - duplex: auto # enabled: true # name: eth0 # speed: auto """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: list sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: list sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - 'set interfaces ethernet eth1 mtu 1200' - 'set interfaces ethernet eth2 vif 100 description VIF 100' rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - 'set interfaces ethernet eth1 mtu 1200' - 'set interfaces ethernet eth2 vif 100 description VIF 100' gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.interfaces.interfaces import ( InterfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.interfaces.interfaces import ( Interfaces, ) 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", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=InterfacesArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Interfaces(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_l3_interfaces.py b/plugins/modules/vyos_l3_interfaces.py index 0d2a5dae..1d3dd20c 100644 --- a/plugins/modules/vyos_l3_interfaces.py +++ b/plugins/modules/vyos_l3_interfaces.py @@ -1,585 +1,584 @@ #!/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_l3_interfaces """ from __future__ import absolute_import, division, print_function - __metaclass__ = type ANSIBLE_METADATA = { "metadata_version": "1.1", "status": ["preview"], "supported_by": "network", } DOCUMENTATION = """ --- module: vyos_l3_interfaces version_added: '1.0.0' short_description: Layer 3 interfaces resource module. description: This module manages the L3 interface attributes on VyOS network devices. notes: - Tested against VyOS 1.3. - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Rohit Thakur (@rohitthakur2590) - Nilashish Chakraborty (@nilashishc) options: config: description: - The provided L3 interfaces configuration. type: list elements: dict suboptions: name: description: - Full name of the interface, e.g. eth0, eth1. type: str required: true ipv4: description: - List of IPv4 addresses of the interface. type: list elements: dict suboptions: address: description: - IPv4 address of the interface. type: str ipv6: description: - List of IPv6 addresses of the interface. type: list elements: dict suboptions: address: description: - IPv6 address of the interface. - C(auto-config) to use SLAAC to chose an address. type: str vifs: description: - List of virtual sub-interfaces (VIFs) of the interface. elements: dict type: list suboptions: vlan_id: description: - Identifier for the VIF. type: int ipv4: description: - List of IPv4 addresses of the VIF. type: list elements: dict suboptions: address: description: - IPv4 address of the VIF. type: str ipv6: description: - List of IPv6 addresses of the VIF. type: list elements: dict suboptions: address: description: - IPv6 address of the virtual VIF - C(auto-config) to use SLAAC to chose an address. 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 -e eth[2,3]). - 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 of the configuration after module completion. type: str choices: - merged - replaced - overridden - deleted - rendered - gathered - parsed default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos:~$ show configuration commands | grep -e eth[2,3] # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 vif 101 # set interfaces ethernet eth3 vif 102 - name: Merge provided configuration with device configuration vyos.vyos.vyos_l3_interfaces: config: - name: eth2 ipv4: - address: 192.0.2.10/28 - address: 198.51.100.40/27 ipv6: - address: '2001:db8:100::2/32' - address: '2001:db8:400::10/32' - name: eth3 ipv4: - address: 203.0.113.65/26 vifs: - vlan_id: 101 ipv4: - address: 192.0.2.71/28 - address: 198.51.100.131/25 - vlan_id: 102 ipv6: - address: '2001:db8:1000::5/38' - address: '2001:db8:1400::3/38' state: merged # After state: # ------------- # # vyos:~$ show configuration commands | grep -e eth[2,3] # set interfaces ethernet eth2 address '192.0.2.10/28' # set interfaces ethernet eth2 address '198.51.100.40/27' # set interfaces ethernet eth2 address '2001:db8:100::2/32' # set interfaces ethernet eth2 address '2001:db8:400::10/32' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 address '203.0.113.65/26' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 vif 101 address '192.0.2.71/28' # set interfaces ethernet eth3 vif 101 address '198.51.100.131/25' # set interfaces ethernet eth3 vif 102 address '2001:db8:1000::5/38' # set interfaces ethernet eth3 vif 102 address '2001:db8:1400::3/38' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34' # Using replaced # # Before state: # ------------- # # vyos:~$ show configuration commands | grep eth # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9' # set interfaces ethernet eth1 address '192.0.2.14/24' # set interfaces ethernet eth2 address '192.0.2.10/24' # set interfaces ethernet eth2 address '192.0.2.11/24' # set interfaces ethernet eth2 address '2001:db8::10/32' # set interfaces ethernet eth2 address '2001:db8::11/32' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 address '198.51.100.10/24' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 vif 101 address '198.51.100.130/25' # set interfaces ethernet eth3 vif 101 address '198.51.100.131/25' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::3/34' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34' # - name: Replace device configurations of listed interfaces with provided configurations vyos.vyos.vyos_l3_interfaces: config: - name: eth2 ipv4: - address: 192.0.2.10/24 - name: eth3 ipv6: - address: '2001:db8::11/32' state: replaced # After state: # ------------- # # vyos:~$ show configuration commands | grep eth # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9' # set interfaces ethernet eth1 address '192.0.2.14/24' # set interfaces ethernet eth2 address '192.0.2.10/24' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 address '2001:db8::11/32' # set interfaces ethernet eth3 vif 101 # set interfaces ethernet eth3 vif 102 # Using overridden # # Before state # -------------- # # vyos@vyos-appliance:~$ show configuration commands | grep eth # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9' # set interfaces ethernet eth1 address '192.0.2.14/24' # set interfaces ethernet eth2 address '192.0.2.10/24' # set interfaces ethernet eth2 address '192.0.2.11/24' # set interfaces ethernet eth2 address '2001:db8::10/32' # set interfaces ethernet eth2 address '2001:db8::11/32' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 address '198.51.100.10/24' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 vif 101 address '198.51.100.130/25' # set interfaces ethernet eth3 vif 101 address '198.51.100.131/25' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::3/34' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34' - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_l3_interfaces: config: - name: eth0 ipv4: - address: dhcp ipv6: - address: dhcpv6 state: overridden # After state # ------------ # # vyos@vyos-appliance:~$ show configuration commands | grep eth # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 vif 101 # set interfaces ethernet eth3 vif 102 # Using deleted # # Before state # ------------- # vyos@vyos-appliance:~$ show configuration commands | grep eth # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9' # set interfaces ethernet eth1 address '192.0.2.14/24' # set interfaces ethernet eth2 address '192.0.2.10/24' # set interfaces ethernet eth2 address '192.0.2.11/24' # set interfaces ethernet eth2 address '2001:db8::10/32' # set interfaces ethernet eth2 address '2001:db8::11/32' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 address '198.51.100.10/24' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 vif 101 address '198.51.100.130/25' # set interfaces ethernet eth3 vif 101 address '198.51.100.131/25' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::3/34' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34' - name: Delete L3 attributes of given interfaces (Note - This won't delete the interface itself) vyos.vyos.vyos_l3_interfaces: config: - name: eth1 - name: eth2 - name: eth3 state: deleted # After state # ------------ # vyos@vyos-appliance:~$ show configuration commands | grep eth # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:f3:6c:b5' # set interfaces ethernet eth0 smp_affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:ad:ef:65' # set interfaces ethernet eth1 smp_affinity 'auto' # set interfaces ethernet eth2 hw-id '08:00:27:ab:4e:79' # set interfaces ethernet eth2 smp_affinity 'auto' # set interfaces ethernet eth3 hw-id '08:00:27:17:3c:85' # set interfaces ethernet eth3 smp_affinity 'auto' # Using gathered # # Before state: # ------------- # # vyos:~$ show configuration commands | grep -e eth[2,3,0] # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19' # set interfaces ethernet eth0 smp_affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 address '192.0.2.14/24' # set interfaces ethernet eth2 address '192.0.2.11/24' # set interfaces ethernet eth2 address '192.0.2.10/24' # set interfaces ethernet eth2 address '2001:db8::10/32' # set interfaces ethernet eth2 address '2001:db8::12/32' # - name: Gather listed l3 interfaces with provided configurations vyos.vyos.vyos_l3_interfaces: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "ipv4": [ # { # "address": "192.0.2.11/24" # }, # { # "address": "192.0.2.10/24" # } # ], # "ipv6": [ # { # "address": "2001:db8::10/32" # }, # { # "address": "2001:db8::12/32" # } # ], # "name": "eth2" # }, # { # "ipv4": [ # { # "address": "192.0.2.14/24" # } # ], # "name": "eth1" # }, # { # "ipv4": [ # { # "address": "dhcp" # } # ], # "name": "eth0" # } # ] # # # After state: # ------------- # # vyos:~$ show configuration commands | grep -e eth[2,3] # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19' # set interfaces ethernet eth0 smp_affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 address '192.0.2.14/24' # set interfaces ethernet eth2 address '192.0.2.11/24' # set interfaces ethernet eth2 address '192.0.2.10/24' # set interfaces ethernet eth2 address '2001:db8::10/32' # set interfaces ethernet eth2 address '2001:db8::12/32' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_l3_interfaces: config: - name: eth1 ipv4: - address: 192.0.2.14/24 - name: eth2 ipv4: - address: 192.0.2.10/24 - address: 192.0.2.11/24 ipv6: - address: '2001:db8::10/32' - address: '2001:db8::12/32' state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set interfaces ethernet eth1 address '192.0.2.14/24'", # "set interfaces ethernet eth2 address '192.0.2.11/24'", # "set interfaces ethernet eth2 address '192.0.2.10/24'", # "set interfaces ethernet eth2 address '2001:db8::10/32'", # "set interfaces ethernet eth2 address '2001:db8::12/32'" # ] # Using parsed # # - name: parse the provided running configuration vyos.vyos.vyos_l3_interfaces: running_config: "set interfaces ethernet eth0 address 'dhcp' set interfaces ethernet eth1 address '192.0.2.14/24' set interfaces ethernet eth2 address '192.0.2.10/24' set interfaces ethernet eth2 address '192.0.2.11/24' set interfaces ethernet eth2 address '2001:db8::10/32' set interfaces ethernet eth2 address '2001:db8::12/32'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": [ # { # "ipv4": [ # { # "address": "192.0.2.10/24" # }, # { # "address": "192.0.2.11/24" # } # ], # "ipv6": [ # { # "address": "2001:db8::10/32" # }, # { # "address": "2001:db8::12/32" # } # ], # "name": "eth2" # }, # { # "ipv4": [ # { # "address": "192.0.2.14/24" # } # ], # "name": "eth1" # }, # { # "ipv4": [ # { # "address": "dhcp" # } # ], # "name": "eth0" # } # ] """ RETURN = """ before: description: The configuration prior to the module invocation. returned: always type: list sample: > The configuration returned will always be in the same format of the parameters above. after: description: The resulting configuration module invocation. returned: when changed type: list 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 interfaces ethernet eth1 192.0.2.14/2' - 'set interfaces ethernet eth3 vif 101 address 198.51.100.130/25' """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.l3_interfaces.l3_interfaces import ( L3_interfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.l3_interfaces.l3_interfaces import ( L3_interfaces, ) 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", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=L3_interfacesArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = L3_interfaces(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_lag_interfaces.py b/plugins/modules/vyos_lag_interfaces.py index a90d0098..27dc6d0a 100644 --- a/plugins/modules/vyos_lag_interfaces.py +++ b/plugins/modules/vyos_lag_interfaces.py @@ -1,787 +1,786 @@ #!/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_lag_interfaces """ from __future__ import absolute_import, division, print_function - __metaclass__ = type ANSIBLE_METADATA = { "metadata_version": "1.1", "status": ["preview"], "supported_by": "network", } DOCUMENTATION = """ --- module: vyos_lag_interfaces version_added: '1.0.0' short_description: LAG interfaces resource module description: This module manages attributes of link aggregation groups on VyOS network devices. author: - Rohit Thakur (@rohitthakur2590) notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025. - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). options: config: description: A list of link aggregation group configurations. type: list elements: dict suboptions: name: description: - Name of the link aggregation group (LAG) or bond. type: str required: true mode: description: - LAG or bond mode. type: str choices: - 802.3ad - active-backup - broadcast - round-robin - transmit-load-balance - adaptive-load-balance - xor-hash members: description: - List of member interfaces for the LAG (bond). type: list elements: dict suboptions: member: description: - Name of the member interface. type: str primary: description: - Primary device interfaces for the LAG (bond). type: str hash_policy: description: - LAG or bonding transmit hash policy. type: str choices: - layer2 - layer2+3 - layer3+4 arp_monitor: description: - ARP Link monitoring parameters. type: dict suboptions: interval: description: - ARP link monitoring frequency in milliseconds. type: int target: description: - IP address to use for ARP monitoring. type: list elements: 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 bond). - 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 of the configuration after module completion. type: str choices: - merged - replaced - overridden - deleted - parsed - gathered - rendered default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 # set interfaces bonding bond3 # - name: Merge provided configuration with device configuration vyos.vyos.vyos_lag_interfaces: config: - name: bond2 mode: active-backup members: - member: eth2 - member: eth1 hash_policy: layer2 primary: eth2 - name: bond3 mode: active-backup hash_policy: layer2+3 members: - member: eth3 primary: eth3 state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "name": "bond2" # }, # { # "name": "bond3" # } # ], # # "commands": [ # "set interfaces bonding bond2 hash-policy 'layer2'", # "set interfaces bonding bond2 mode 'active-backup'", # "set interfaces ethernet eth2 bond-group bond2", # "set interfaces ethernet eth1 bond-group bond2", # "set interfaces bonding bond2 primary 'eth2'", # "set interfaces bonding bond3 hash-policy 'layer2+3'", # "set interfaces bonding bond3 mode 'active-backup'", # "set interfaces ethernet eth3 bond-group bond3", # "set interfaces bonding bond3 primary 'eth3'" # ] # # "after": [ # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # }, # { # "member": "eth2" # } # ], # "mode": "active-backup", # "name": "bond2", # "primary": "eth2" # }, # { # "hash_policy": "layer2+3", # "members": [ # { # "member": "eth3" # } # ], # "mode": "active-backup", # "name": "bond3", # "primary": "eth3" # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 hash-policy 'layer2' # set interfaces bonding bond2 mode 'active-backup' # set interfaces bonding bond2 primary 'eth2' # set interfaces bonding bond3 hash-policy 'layer2+3' # set interfaces bonding bond3 mode 'active-backup' # set interfaces bonding bond3 primary 'eth3' # set interfaces ethernet eth1 bond-group 'bond2' # set interfaces ethernet eth2 bond-group 'bond2' # set interfaces ethernet eth3 bond-group 'bond3' # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 hash-policy 'layer2' # set interfaces bonding bond2 mode 'active-backup' # set interfaces bonding bond2 primary 'eth2' # set interfaces bonding bond3 hash-policy 'layer2+3' # set interfaces bonding bond3 mode 'active-backup' # set interfaces bonding bond3 primary 'eth3' # set interfaces ethernet eth1 bond-group 'bond2' # set interfaces ethernet eth2 bond-group 'bond2' # set interfaces ethernet eth3 bond-group 'bond3' # - name: Replace device configurations of listed LAGs with provided configurations vyos.vyos.vyos_lag_interfaces: config: - name: bond3 mode: 802.3ad hash_policy: layer2 members: - member: eth3 state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # }, # { # "member": "eth2" # } # ], # "mode": "active-backup", # "name": "bond2", # "primary": "eth2" # }, # { # "hash_policy": "layer2+3", # "members": [ # { # "member": "eth3" # } # ], # "mode": "active-backup", # "name": "bond3", # "primary": "eth3" # } # ], # # "commands": [ # "delete interfaces bonding bond3 primary", # "set interfaces bonding bond3 hash-policy 'layer2'", # "set interfaces bonding bond3 mode '802.3ad'" # ], # # "after": [ # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # }, # { # "member": "eth2" # } # ], # "mode": "active-backup", # "name": "bond2", # "primary": "eth2" # }, # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth3" # } # ], # "mode": "802.3ad", # "name": "bond3" # } # ], # # After state: # ------------- # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 hash-policy 'layer2' # set interfaces bonding bond2 mode 'active-backup' # set interfaces bonding bond2 primary 'eth2' # set interfaces bonding bond3 hash-policy 'layer2' # set interfaces bonding bond3 mode '802.3ad' # set interfaces ethernet eth1 bond-group 'bond2' # set interfaces ethernet eth2 bond-group 'bond2' # set interfaces ethernet eth3 bond-group 'bond3' # Using overridden # # Before state # -------------- # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 hash-policy 'layer2' # set interfaces bonding bond2 mode 'active-backup' # set interfaces bonding bond2 primary 'eth2' # set interfaces bonding bond3 hash-policy 'layer2' # set interfaces bonding bond3 mode '802.3ad' # set interfaces ethernet eth1 bond-group 'bond2' # set interfaces ethernet eth2 bond-group 'bond2' # set interfaces ethernet eth3 bond-group 'bond3' # - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_lag_interfaces: config: - name: bond3 mode: active-backup members: - member: eth1 - member: eth2 - member: eth3 primary: eth3 hash_policy: layer2 state: overridden # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # }, # { # "member": "eth2" # } # ], # "mode": "active-backup", # "name": "bond2", # "primary": "eth2" # }, # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth3" # } # ], # "mode": "802.3ad", # "name": "bond3" # } # ], # # "commands": [ # "delete interfaces bonding bond2 hash-policy", # "delete interfaces ethernet eth1 bond-group bond2", # "delete interfaces ethernet eth2 bond-group bond2", # "delete interfaces bonding bond2 mode", # "delete interfaces bonding bond2 primary", # "set interfaces bonding bond3 mode 'active-backup'", # "set interfaces ethernet eth1 bond-group bond3", # "set interfaces ethernet eth2 bond-group bond3", # "set interfaces bonding bond3 primary 'eth3'" # ], # # "after": [ # { # "name": "bond2" # }, # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # }, # { # "member": "eth2" # }, # { # "member": "eth3" # } # ], # "mode": "active-backup", # "name": "bond3", # "primary": "eth3" # } # ], # # # After state # ------------ # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 # set interfaces bonding bond3 hash-policy 'layer2' # set interfaces bonding bond3 mode 'active-backup' # set interfaces bonding bond3 primary 'eth3' # set interfaces ethernet eth1 bond-group 'bond3' # set interfaces ethernet eth2 bond-group 'bond3' # set interfaces ethernet eth3 bond-group 'bond3' # Using deleted # # Before state # ------------- # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 hash-policy 'layer2' # set interfaces bonding bond2 mode 'active-backup' # set interfaces bonding bond2 primary 'eth2' # set interfaces bonding bond3 hash-policy 'layer2+3' # set interfaces bonding bond3 mode 'active-backup' # set interfaces bonding bond3 primary 'eth3' # set interfaces ethernet eth1 bond-group 'bond2' # set interfaces ethernet eth2 bond-group 'bond2' # set interfaces ethernet eth3 bond-group 'bond3' # - name: Delete LAG attributes of given interfaces (Note - This won't delete the interface itself) vyos.vyos.vyos_lag_interfaces: config: - name: bond2 - name: bond3 state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # }, # { # "member": "eth2" # } # ], # "mode": "active-backup", # "name": "bond2", # "primary": "eth2" # }, # { # "hash_policy": "layer2+3", # "members": [ # { # "member": "eth3" # } # ], # "mode": "active-backup", # "name": "bond3", # "primary": "eth3" # } # ], # "commands": [ # "delete interfaces bonding bond2 hash-policy", # "delete interfaces ethernet eth1 bond-group bond2", # "delete interfaces ethernet eth2 bond-group bond2", # "delete interfaces bonding bond2 mode", # "delete interfaces bonding bond2 primary", # "delete interfaces bonding bond3 hash-policy", # "delete interfaces ethernet eth3 bond-group bond3", # "delete interfaces bonding bond3 mode", # "delete interfaces bonding bond3 primary" # ], # # "after": [ # { # "name": "bond2" # }, # { # "name": "bond3" # } # ], # # After state # ------------ # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 # set interfaces bonding bond3 # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep bond # set interfaces bonding bond0 hash-policy 'layer2' # set interfaces bonding bond0 mode 'active-backup' # set interfaces bonding bond0 primary 'eth1' # set interfaces bonding bond1 hash-policy 'layer2+3' # set interfaces bonding bond1 mode 'active-backup' # set interfaces bonding bond1 primary 'eth2' # set interfaces ethernet eth1 bond-group 'bond0' # set interfaces ethernet eth2 bond-group 'bond1' # - name: Gather listed lag interfaces with provided configurations vyos.vyos.vyos_lag_interfaces: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # # After state: # ------------- # # vyos@192# run show configuration commands | grep bond # set interfaces bonding bond0 hash-policy 'layer2' # set interfaces bonding bond0 mode 'active-backup' # set interfaces bonding bond0 primary 'eth1' # set interfaces bonding bond1 hash-policy 'layer2+3' # set interfaces bonding bond1 mode 'active-backup' # set interfaces bonding bond1 primary 'eth2' # set interfaces ethernet eth1 bond-group 'bond0' # set interfaces ethernet eth2 bond-group 'bond1' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_lag_interfaces: config: - name: bond0 hash_policy: layer2 members: - member: eth1 mode: active-backup primary: eth1 - name: bond1 hash_policy: layer2+3 members: - member: eth2 mode: active-backup primary: eth2 state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set interfaces bonding bond0 hash-policy 'layer2'", # "set interfaces ethernet eth1 bond-group 'bond0'", # "set interfaces bonding bond0 mode 'active-backup'", # "set interfaces bonding bond0 primary 'eth1'", # "set interfaces bonding bond1 hash-policy 'layer2+3'", # "set interfaces ethernet eth2 bond-group 'bond1'", # "set interfaces bonding bond1 mode 'active-backup'", # "set interfaces bonding bond1 primary 'eth2'" # ] # Using parsed # # - name: Parsed the commands for provided configuration vyos.vyos.vyos_l3_interfaces: running_config: "set interfaces bonding bond0 hash-policy 'layer2' set interfaces bonding bond0 mode 'active-backup' set interfaces bonding bond0 primary 'eth1' set interfaces bonding bond1 hash-policy 'layer2+3' set interfaces bonding bond1 mode 'active-backup' set interfaces bonding bond1 primary 'eth2' set interfaces ethernet eth1 bond-group 'bond0' set interfaces ethernet eth2 bond-group 'bond1'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": [ # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # } # ], # "mode": "active-backup", # "name": "bond0", # "primary": "eth1" # }, # { # "hash_policy": "layer2+3", # "members": [ # { # "member": "eth2" # } # ], # "mode": "active-backup", # "name": "bond1", # "primary": "eth2" # } # ] """ RETURN = """ before: description: The configuration prior to the module invocation. returned: always type: list sample: > The configuration returned will always be in the same format of the parameters above. after: description: The resulting configuration module invocation. returned: when changed type: list 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 interfaces bonding bond2' - 'set interfaces bonding bond2 hash-policy layer2' """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lag_interfaces.lag_interfaces import ( Lag_interfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.lag_interfaces.lag_interfaces import ( Lag_interfaces, ) 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", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Lag_interfacesArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Lag_interfaces(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_lldp_global.py b/plugins/modules/vyos_lldp_global.py index d1ac618a..a1c01e23 100644 --- a/plugins/modules/vyos_lldp_global.py +++ b/plugins/modules/vyos_lldp_global.py @@ -1,502 +1,501 @@ #!/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_lldp_global """ from __future__ import absolute_import, division, print_function - __metaclass__ = type ANSIBLE_METADATA = { "metadata_version": "1.1", "status": ["preview"], "supported_by": "network", } DOCUMENTATION = """ --- module: vyos_lldp_global version_added: '1.0.0' short_description: LLDP global resource module description: This module manages link layer discovery protocol (LLDP) attributes on VyOS devices. notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025 - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Rohit Thakur (@rohitthakur2590) options: config: description: The provided link layer discovery protocol (LLDP) configuration. type: dict suboptions: enable: description: - This argument is a boolean value to enable or disable LLDP. type: bool address: description: - Exactly one management address (exclusive with addresses). Deprecated in favor of addresses. To be removed in 7.0.0. type: str addresses: description: - One or more management addresses. The management address is used to identify the management interface of the system. Only addresses connected to the system will be transmitted. type: list elements: str snmp: description: - This argument enable the SNMP queries to LLDP database. type: str legacy_protocols: description: - List of the supported legacy protocols. type: list elements: str choices: - cdp - edp - fdp - sonmp 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 lldp). - 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 of the configuration after module completion. type: str choices: - merged - replaced - deleted - gathered - rendered - parsed default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands|grep lldp # - name: Merge provided configuration with device configuration vyos.vyos.vyos_lldp_global: config: legacy_protocols: - fdp - cdp snmp: enable addresses: - 192.0.2.11 state: merged # # # ------------------------ # Module Execution Results # ------------------------ # # before": {} # # commands": [ # "set service lldp legacy-protocols fdp", # "set service lldp legacy-protocols cdp", # "set service lldp snmp enable", # "set service lldp management-address '192.0.2.11'" # ] # # after": { # "snmp": "enable" # "addresses": [ # "192.0.2.11" # ] # "legacy_protocols": [ # "cdp", # "fdp" # ] # "enable": true # } # # After state: # ------------- # # set service lldp legacy-protocols cdp # set service lldp legacy-protocols fdp # set service lldp management-address '192.0.2.11' # set service lldp snmp enable # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp legacy-protocols cdp # set service lldp legacy-protocols fdp # set service lldp management-address '192.0.2.11' # set service lldp snmp enable # - name: Replace device configurations with provided configurations vyos.vyos.vyos_lldp_global: config: legacy_protocols: - edp - sonmp - cdp addresses: - 192.0.2.14 state: replaced # # # ------------------------ # Module Execution Results # ------------------------ # # # "before": { # "snmp": "enable" # "addresses": [ # "192.0.2.11" # ] # "legacy_protocols": [ # "cdp", # "fdp" # ] # "enable": true # } # # "commands": [ # "delete service lldp snmp", # "delete service lldp legacy-protocols fdp", # "delete service lldp management-address '192.0.2.11'", # "set service lldp management-address '192.0.2.14'", # "set service lldp legacy-protocols edp", # "set service lldp legacy-protocols sonmp" # ] # # "after": { # "addresses": [ # "192.0.2.14" # ] # "legacy_protocols": [ # "cdp", # "edp", # "sonmp" # ] # "enable": true # } # # After state: # ------------- # # vyos@vyos:~$ show configuration commands|grep lldp # set service lldp legacy-protocols cdp # set service lldp legacy-protocols edp # set service lldp legacy-protocols sonmp # set service lldp management-address '192.0.2.14' # Using deleted # # Before state # ------------- # vyos@vyos:~$ show configuration commands|grep lldp # set service lldp legacy-protocols cdp # set service lldp legacy-protocols edp # set service lldp legacy-protocols sonmp # set service lldp management-address '192.0.2.14' # - name: Delete attributes of given lldp service (This won't delete the LLDP service itself) vyos.vyos.vyos_lldp_global: config: state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": { # "addresses": [ # "192.0.2.14" # ] # "legacy_protocols": [ # "cdp", # "edp", # "sonmp" # ] # "enable": true # } # # "commands": [ # "delete service lldp management-address", # "delete service lldp legacy-protocols" # ] # # "after": { # "enable": true # } # # After state # ------------ # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep lldp # set service lldp legacy-protocols 'cdp' # set service lldp management-address '192.0.2.17' # - name: Gather lldp global config with provided configurations vyos.vyos.vyos_lldp_global: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.3.1" # }, # { # "address": "192.0.3.2" # } # ], # "name": "ENG-HOSTS" # }, # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.2.1" # }, # { # "address": "192.0.2.2" # }, # { # "address": "192.0.2.3" # } # ], # "name": "SALES-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep lldp # set service lldp legacy-protocols 'cdp' # set service lldp management-address '192.0.2.17' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_lldp_global: config: addresses: - 192.0.2.17 enable: true legacy_protocols: - cdp state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set service lldp legacy-protocols 'cdp'", # "set service lldp", # "set service lldp management-address '192.0.2.17'" # ] # # Using parsed # # - name: Parse the provided commands to provide structured configuration vyos.vyos.vyos_lldp_global: running_config: "set service lldp legacy-protocols 'cdp' set service lldp legacy-protocols 'fdp' set service lldp management-address '192.0.2.11'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": { # "addresses": [ # "192.0.2.11" # ] # "enable": true, # "legacy_protocols": [ # "cdp", # "fdp" # ] # } """ RETURN = """ before: description: The configuration prior to the module 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 after module 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 service lldp legacy-protocols sonmp - set service lldp management-address '192.0.2.14' """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_global.lldp_global import ( Lldp_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.lldp_global.lldp_global import ( Lldp_global, ) 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", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Lldp_globalArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Lldp_global(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_lldp_interfaces.py b/plugins/modules/vyos_lldp_interfaces.py index ce1c9643..48cb171f 100644 --- a/plugins/modules/vyos_lldp_interfaces.py +++ b/plugins/modules/vyos_lldp_interfaces.py @@ -1,594 +1,593 @@ #!/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_lldp_interfaces """ from __future__ import absolute_import, division, print_function - __metaclass__ = type ANSIBLE_METADATA = { "metadata_version": "1.1", "status": ["preview"], "supported_by": "network", } DOCUMENTATION = """ --- module: vyos_lldp_interfaces version_added: '1.0.0' short_description: LLDP interfaces resource module description: This module manages attributes of lldp interfaces on VyOS network devices. notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025 - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Rohit Thakur (@rohitthakur2590) options: config: description: A list of LLDP interfaces configurations. type: list elements: dict suboptions: name: description: - Name of the LLDP interface. type: str required: true enable: default: true description: - Disable LLDP on the interfaces. type: bool location: description: - LLDP-MED location data. type: dict suboptions: coordinate_based: description: - Coordinate-based location. type: dict suboptions: altitude: description: Altitude in meters. type: int datum: description: Coordinate datum type. type: str choices: - WGS84 - NAD83 - MLLW latitude: description: Latitude. type: str required: true longitude: description: Longitude. type: str required: true elin: description: Emergency Call Service ELIN number (between 10-25 numbers). 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 lldp). - 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 of the configuration after module completion. type: str choices: - merged - replaced - overridden - deleted - rendered - parsed - gathered default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands | grep lldp # - name: Merge provided configuration with device configuration vyos.vyos.vyos_lldp_interfaces: config: - name: eth1 location: elin: 0000000911 - name: eth2 location: coordinate_based: altitude: 2200 datum: WGS84 longitude: 222.267255W latitude: 33.524449N state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": {} # # "commands": [ # "set service lldp interface eth1 location elin '0000000911'", # "set service lldp interface eth1", # "set service lldp interface eth2 location coordinate-based latitude '33.524449N'", # "set service lldp interface eth2 location coordinate-based altitude '2200'", # "set service lldp interface eth2 location coordinate-based datum 'WGS84'", # "set service lldp interface eth2 location coordinate-based longitude '222.267255W'", # "set service lldp interface eth2 location coordinate-based latitude '33.524449N'", # "set service lldp interface eth2 location coordinate-based altitude '2200'", # "set service lldp interface eth2 location coordinate-based datum 'WGS84'", # "set service lldp interface eth2 location coordinate-based longitude '222.267255W'", # "set service lldp interface eth2" # ] # # "after": { # "location": { # "coordinate_based": { # "altitude": 2200, # "datum": "WGS84", # "latitude": "33.524449N", # "longitude": "222.267255W" # } # }, # "name": "eth2" # }, # { # "location": { # "elin": "0000000911" # } # }, # "name": "eth1" # } # # After state: # ------------- # # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp interface eth1 location elin '0000000911' # set service lldp interface eth2 location coordinate-based altitude '2200' # set service lldp interface eth2 location coordinate-based datum 'WGS84' # set service lldp interface eth2 location coordinate-based latitude '33.524449N' # set service lldp interface eth2 location coordinate-based longitude '222.267255W' # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp interface eth1 location elin '0000000911' # set service lldp interface eth2 location coordinate-based altitude '2200' # set service lldp interface eth2 location coordinate-based datum 'WGS84' # set service lldp interface eth2 location coordinate-based latitude '33.524449N' # set service lldp interface eth2 location coordinate-based longitude '222.267255W' # - name: >- Replace device configurations of listed LLDP interfaces with provided configurations vyos.vyos.vyos_lldp_interfaces: config: - name: eth2 location: elin: 0000000911 - name: eth1 location: coordinate_based: altitude: 2200 datum: WGS84 longitude: 222.267255W latitude: 33.524449N state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "location": { # "coordinate_based": { # "altitude": 2200, # "datum": "WGS84", # "latitude": "33.524449N", # "longitude": "222.267255W" # } # }, # "name": "eth2" # }, # { # "location": { # "elin": "0000000911" # }, # "name": "eth1" # } # ] # # "commands": [ # "delete service lldp interface eth2 location", # "set service lldp interface eth2 'disable'", # "set service lldp interface eth2 location elin '0000000911'", # "delete service lldp interface eth1 location", # "set service lldp interface eth1 'disable'", # "set service lldp interface eth1 location coordinate-based latitude '33.524449N'", # "set service lldp interface eth1 location coordinate-based altitude '2200'", # "set service lldp interface eth1 location coordinate-based datum 'WGS84'", # "set service lldp interface eth1 location coordinate-based longitude '222.267255W'" # ] # # "after": { # "location": { # "elin": "0000000911" # }, # "name": "eth2" # }, # { # "location": { # "coordinate_based": { # "altitude": 2200, # "datum": "WGS84", # "latitude": "33.524449N", # "longitude": "222.267255W" # } # }, # "name": "eth1" # } # # After state: # ------------- # # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp interface eth1 'disable' # set service lldp interface eth1 location coordinate-based altitude '2200' # set service lldp interface eth1 location coordinate-based datum 'WGS84' # set service lldp interface eth1 location coordinate-based latitude '33.524449N' # set service lldp interface eth1 location coordinate-based longitude '222.267255W' # set service lldp interface eth2 'disable' # set service lldp interface eth2 location elin '0000000911' # Using overridden # # Before state # -------------- # # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp interface eth1 'disable' # set service lldp interface eth1 location coordinate-based altitude '2200' # set service lldp interface eth1 location coordinate-based datum 'WGS84' # set service lldp interface eth1 location coordinate-based latitude '33.524449N' # set service lldp interface eth1 location coordinate-based longitude '222.267255W' # set service lldp interface eth2 'disable' # set service lldp interface eth2 location elin '0000000911' # - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_lldp_interfaces: config: - name: eth2 location: elin: 0000000911 state: overridden # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "enable": false, # "elin": "0000000911", # "name": "eth2" # }, # { # "enable": false, # "location": { # "coordinate_based": { # "altitude": 2200, # "datum": "WGS84", # "latitude": "33.524449N", # "longitude": "222.267255W" # } # }, # "name": "eth1" # } # ] # # "commands": [ # "delete service lldp interface eth2 location", # "delete service lldp interface eth2 disable", # "set service lldp interface eth2 location elin 0000000911" # ] # # "after": [ # { # "location": { # "elin": 0000000911 # }, # "name": "eth2" # } # ] # # # After state # ------------ # # vyos@vyos# run show configuration commands | grep lldp # set service lldp interface eth2 location elin '0000000911' # Using deleted # # Before state # ------------- # # vyos@vyos# run show configuration commands | grep lldp # set service lldp interface eth2 location elin '0000000911' # - name: Delete LLDP interface attributes of given interfaces. vyos.vyos.vyos_lldp_interfaces: config: - name: eth2 state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "location": { # "elin": 0000000911 # }, # "name": "eth2" # } # ] # "commands": [ # "delete service lldp interface eth2" # ] # # "after": [] # After state # ------------ # vyos@vyos# run show configuration commands | grep lldp # set service 'lldp' # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep lldp # set service lldp interface eth1 location elin '0000000911' # set service lldp interface eth2 location coordinate-based altitude '2200' # set service lldp interface eth2 location coordinate-based datum 'WGS84' # set service lldp interface eth2 location coordinate-based latitude '33.524449N' # set service lldp interface eth2 location coordinate-based longitude '222.267255W' # - name: Gather listed lldp interfaces from running configuration vyos.vyos.vyos_lldp_interfaces: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "location": { # "coordinate_based": { # "altitude": 2200, # "datum": "WGS84", # "latitude": "33.524449N", # "longitude": "222.267255W" # } # }, # "name": "eth2" # }, # { # "location": { # "elin": "0000000911" # }, # "name": "eth1" # } # ] # # # After state: # ------------- # # vyos@192# run show configuration commands | grep lldp # set service lldp interface eth1 location elin '0000000911' # set service lldp interface eth2 location coordinate-based altitude '2200' # set service lldp interface eth2 location coordinate-based datum 'WGS84' # set service lldp interface eth2 location coordinate-based latitude '33.524449N' # set service lldp interface eth2 location coordinate-based longitude '222.267255W' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_lldp_interfaces: config: - name: eth1 location: elin: 0000000911 - name: eth2 location: coordinate_based: altitude: 2200 datum: WGS84 longitude: 222.267255W latitude: 33.524449N state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set service lldp interface eth1 location elin '0000000911'", # "set service lldp interface eth1", # "set service lldp interface eth2 location coordinate-based latitude '33.524449N'", # "set service lldp interface eth2 location coordinate-based altitude '2200'", # "set service lldp interface eth2 location coordinate-based datum 'WGS84'", # "set service lldp interface eth2 location coordinate-based longitude '222.267255W'", # "set service lldp interface eth2" # ] # Using parsed # # - name: Parsed the commands to provide structured configuration. vyos.vyos.vyos_lldp_interfaces: running_config: "set service lldp interface eth1 location elin '0000000911' set service lldp interface eth2 location coordinate-based altitude '2200' set service lldp interface eth2 location coordinate-based datum 'WGS84' set service lldp interface eth2 location coordinate-based latitude '33.524449N' set service lldp interface eth2 location coordinate-based longitude '222.267255W'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": [ # { # "location": { # "coordinate_based": { # "altitude": 2200, # "datum": "WGS84", # "latitude": "33.524449N", # "longitude": "222.267255W" # } # }, # "name": "eth2" # }, # { # "location": { # "elin": "0000000911" # }, # "name": "eth1" # } # ] """ RETURN = """ before: description: The configuration as structured data prior to module invocation. returned: always type: list sample: > The configuration returned will always be in the same format of the parameters above. after: description: The configuration as structured data after module completion. returned: when changed type: list 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 service lldp interface eth2 'disable'" - "delete service lldp interface eth1 location" """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_interfaces.lldp_interfaces import ( Lldp_interfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.lldp_interfaces.lldp_interfaces import ( Lldp_interfaces, ) 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", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Lldp_interfacesArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Lldp_interfaces(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_logging_global.py b/plugins/modules/vyos_logging_global.py index 3380b9ea..a3844f5c 100644 --- a/plugins/modules/vyos_logging_global.py +++ b/plugins/modules/vyos_logging_global.py @@ -1,785 +1,784 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The module file for vyos_logging_global """ from __future__ import absolute_import, division, print_function - __metaclass__ = type DOCUMENTATION = """ module: vyos_logging_global version_added: 2.4.0 short_description: Logging resource module description: This module manages the logging attributes of Vyos network devices author: Sagar Paul (@KB-perByte) notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025 - This module works with connection C(network_cli). - The Configuration defaults of the Vyos network devices are supposed to hinder idempotent behavior of plays options: config: description: A list containing dictionary of logging options type: dict suboptions: console: description: logging to serial console type: dict suboptions: state: &state_config description: enable or disable the command type: str choices: - enabled - disabled facilities: description: facility configurations for console type: list elements: dict suboptions: facility: &facility description: Facility for logging type: str choices: - all - auth - authpriv - cron - daemon - kern - lpr - mail - mark - news - protocols - security - syslog - user - uucp - local0 - local1 - local2 - local3 - local4 - local5 - local6 - local7 severity: &severity description: logging level type: str choices: - emerg - alert - crit - err - warning - notice - info - debug - all files: description: logging to file type: list elements: dict suboptions: path: description: file name or path type: str archive: &archive description: Log file size and rotation characteristics type: dict suboptions: state: *state_config file_num: description: Number of saved files (default is 5) type: int size: description: Size of log files (in kilobytes, default is 256) type: int facilities: ¶ms description: facility configurations type: list elements: dict suboptions: facility: *facility severity: *severity global_params: description: logging to serial console type: dict suboptions: state: *state_config archive: *archive facilities: *params marker_interval: description: time interval how often a mark message is being sent in seconds (default is 1200) type: int preserve_fqdn: description: uses FQDN for logging type: bool hosts: description: logging to serial console type: list elements: dict suboptions: port: description: Destination port (1-65535) type: int facilities: description: facility configurations for host type: list elements: dict suboptions: facility: *facility severity: *severity protocol: description: syslog communication protocol. Version 1.3 and below. type: str choices: - udp - tcp hostname: description: Remote host name or IP address type: str protocol: description: syslog communication protocol. Version 1.4+ type: str choices: - udp - tcp syslog: description: logging syslog type: dict suboptions: state: *state_config users: description: logging to file type: list elements: dict suboptions: username: description: user login name type: str facilities: *params 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 syslog). - 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: choices: - merged - replaced - overridden - deleted - gathered - parsed - rendered default: merged description: - The state the configuration should be left in - The states I(replaced) and I(overridden) have identical behaviour for this module. - Refer to examples for more details. type: str """ EXAMPLES = """ # Using state: merged # Before state: # ------------- # vyos:~$show configuration commands | grep syslog - name: Apply the provided configuration vyos.vyos.vyos_logging_global: config: console: facilities: - facility: local7 severity: err files: - path: logFile archive: file_num: 2 facilities: - facility: local6 severity: emerg hosts: - hostname: 172.16.0.1 facilities: - facility: local7 severity: all - facility: all protocol: udp port: 223 users: - username: vyos facilities: - facility: local7 severity: debug global_params: archive: file_num: 2 size: 111 facilities: - facility: cron severity: debug marker_interval: 111 preserve_fqdn: true state: merged # Commands Fired: # --------------- # "commands": [ # "set system syslog console facility local7 level err", # "set system syslog file logFile archive file 2", # "set system syslog host 172.16.0.1 facility local7 level all", # "set system syslog file logFile facility local6 level emerg", # "set system syslog host 172.16.0.1 facility all protocol udp", # "set system syslog user vyos facility local7 level debug", # "set system syslog host 172.16.0.1 port 223", # "set system syslog global facility cron level debug", # "set system syslog global archive file 2", # "set system syslog global archive size 111", # "set system syslog global marker interval 111", # "set system syslog global preserve-fqdn" # ], # After state: # ------------ # vyos:~$ show configuration commands | grep syslog # set system syslog console facility local7 level 'err' # set system syslog file logFile archive file '2' # set system syslog file logFile facility local6 level 'emerg' # set system syslog global archive file '2' # set system syslog global archive size '111' # set system syslog global facility cron level 'debug' # set system syslog global marker interval '111' # set system syslog global preserve-fqdn # set system syslog host 172.16.0.1 facility all protocol 'udp' # set system syslog host 172.16.0.1 facility local7 level 'all' # set system syslog host 172.16.0.1 port '223' # set system syslog user vyos facility local7 level 'debug' # Using state: deleted # Before state: # ------------- # vyos:~$show configuration commands | grep syslog # set system syslog console facility local7 level 'err' # set system syslog file logFile archive file '2' # set system syslog file logFile facility local6 level 'emerg' # set system syslog global archive file '2' # set system syslog global archive size '111' # set system syslog global facility cron level 'debug' # set system syslog global marker interval '111' # set system syslog global preserve-fqdn # set system syslog host 172.16.0.1 facility all protocol 'udp' # set system syslog host 172.16.0.1 facility local7 level 'all' # set system syslog host 172.16.0.1 port '223' # set system syslog user vyos facility local7 level 'debug' - name: delete the existing configuration vyos.vyos.vyos_logging_global: state: deleted # Commands Fired: # --------------- # "commands": [ # "delete system syslog" # ], # After state: # ------------ # vyos:~$show configuration commands | grep syslog # Using state: overridden # Before state: # ------------- # vyos:~$show configuration commands | grep syslog # set system syslog console facility local7 level 'err' # set system syslog file logFile archive file '2' # set system syslog file logFile facility local6 level 'emerg' # set system syslog global archive file '2' # set system syslog global archive size '111' # set system syslog global facility cron level 'debug' # set system syslog global marker interval '111' # set system syslog global preserve-fqdn # set system syslog host 172.16.0.1 facility all protocol 'udp' # set system syslog host 172.16.0.1 facility local7 level 'all' # set system syslog host 172.16.0.1 port '223' # set system syslog user vyos facility local7 level 'debug' - name: Override the current configuration vyos.vyos.vyos_logging_global: config: console: facilities: - facility: all - facility: local7 severity: err - facility: news severity: debug files: - path: logFileNew hosts: - hostname: 172.16.0.2 facilities: - facility: local5 severity: all global_params: archive: file_num: 10 state: overridden # Commands Fired: # --------------- # "commands": [ # "delete system syslog file logFile", # "delete system syslog global facility cron", # "delete system syslog host 172.16.0.1", # "delete system syslog user vyos", # "set system syslog console facility all", # "set system syslog console facility news level debug", # "set system syslog file logFileNew", # "set system syslog host 172.16.0.2 facility local5 level all", # "set system syslog global archive file 10", # "delete system syslog global archive size 111", # "delete system syslog global marker", # "delete system syslog global preserve-fqdn" # ], # After state: # ------------ # vyos:~$show configuration commands | grep syslog # set system syslog console facility all # set system syslog console facility local7 level 'err' # set system syslog console facility news level 'debug' # set system syslog file logFileNew # set system syslog global archive file '10' # set system syslog host 172.16.0.2 facility local5 level 'all' # Using state: replaced # Before state: # ------------- # vyos:~$show configuration commands | grep syslog # set system syslog console facility all # set system syslog console facility local7 level 'err' # set system syslog console facility news level 'debug' # set system syslog file logFileNew # set system syslog global archive file '10' # set system syslog host 172.16.0.2 facility local5 level 'all' - name: Replace with the provided configuration register: result vyos.vyos.vyos_logging_global: config: console: facilities: - facility: local6 users: - username: paul facilities: - facility: local7 severity: err state: replaced # Commands Fired: # --------------- # "commands": [ # "delete system syslog console facility all", # "delete system syslog console facility local7", # "delete system syslog console facility news", # "delete system syslog file logFileNew", # "delete system syslog global archive file 10", # "delete system syslog host 172.16.0.2", # "set system syslog console facility local6", # "set system syslog user paul facility local7 level err" # ], # After state: # ------------ # vyos:~$show configuration commands | grep syslog # set system syslog console facility local6 # set system syslog user paul facility local7 level 'err' # Using state: gathered - name: Gather logging config vyos.vyos.vyos_logging_global: state: gathered # Module Execution Result: # ------------------------ # "gathered": { # "console": { # "facilities": [ # { # "facility": "local6" # }, # { # "facility": "local7", # "severity": "err" # } # ] # }, # "files": [ # { # "archive": { # "file_num": 2 # }, # "facilities": [ # { # "facility": "local6", # "severity": "emerg" # } # ], # "path": "logFile" # } # ], # "global_params": { # "archive": { # "file_num": 2, # "size": 111 # }, # "facilities": [ # { # "facility": "cron", # "severity": "debug" # } # ], # "marker_interval": 111, # "preserve_fqdn": true # }, # "hosts": [ # { # "facilities": [ # { # "facility": "all", # "protocol": "udp" # }, # { # "facility": "local7", # "severity": "all" # } # ], # "hostname": "172.16.0.1", # "port": 223 # } # ], # "users": [ # { # "facilities": [ # { # "facility": "local7", # "severity": "err" # } # ], # "username": "paul" # }, # { # "facilities": [ # { # "facility": "local7", # "severity": "debug" # } # ], # "username": "vyos" # } # ] # }, # After state: # ------------ # vyos:~$show configuration commands | grep syslog # set system syslog console facility local6 # set system syslog console facility local7 level 'err' # set system syslog file logFile archive file '2' # set system syslog file logFile facility local6 level 'emerg' # set system syslog global archive file '2' # set system syslog global archive size '111' # set system syslog global facility cron level 'debug' # set system syslog global marker interval '111' # set system syslog global preserve-fqdn # set system syslog host 172.16.0.1 facility all protocol 'udp' # set system syslog host 172.16.0.1 facility local7 level 'all' # set system syslog host 172.16.0.1 port '223' # set system syslog user paul facility local7 level 'err' # set system syslog user vyos facility local7 level 'debug' # Using state: rendered - name: Render the provided configuration vyos.vyos.vyos_logging_global: config: console: facilities: - facility: local7 severity: err files: - path: logFile archive: file_num: 2 facilities: - facility: local6 severity: emerg hosts: - hostname: 172.16.0.1 facilities: - facility: local7 severity: all - facility: all protocol: udp port: 223 users: - username: vyos facilities: - facility: local7 severity: debug global_params: archive: file_num: 2 size: 111 facilities: - facility: cron severity: debug marker_interval: 111 preserve_fqdn: true state: rendered # Module Execution Result: # ------------------------ # "rendered": [ # "set system syslog console facility local7 level err", # "set system syslog file logFile facility local6 level emerg", # "set system syslog file logFile archive file 2", # "set system syslog host 172.16.0.1 facility local7 level all", # "set system syslog host 172.16.0.1 facility all protocol udp", # "set system syslog host 172.16.0.1 port 223", # "set system syslog user vyos facility local7 level debug", # "set system syslog global facility cron level debug", # "set system syslog global archive file 2", # "set system syslog global archive size 111", # "set system syslog global marker interval 111", # "set system syslog global preserve-fqdn" # ] # Using state: parsed # File: parsed.cfg # ---------------- # set system syslog console facility local6 # set system syslog console facility local7 level 'err' # set system syslog file logFile archive file '2' # set system syslog file logFile facility local6 level 'emerg' # set system syslog global archive file '2' # set system syslog global archive size '111' # set system syslog global facility cron level 'debug' # set system syslog global marker interval '111' # set system syslog global preserve-fqdn # set system syslog host 172.16.0.1 facility all protocol 'udp' # set system syslog host 172.16.0.1 facility local7 level 'all' # set system syslog host 172.16.0.1 port '223' # set system syslog user paul facility local7 level 'err' # set system syslog user vyos facility local7 level 'debug' - name: Parse the provided configuration vyos.vyos.vyos_logging_global: running_config: "{{ lookup('file', 'parsed_vyos.cfg') }}" state: parsed # Module Execution Result: # ------------------------ # "parsed": { # "console": { # "facilities": [ # { # "facility": "local6" # }, # { # "facility": "local7", # "severity": "err" # } # ] # }, # "files": [ # { # "archive": { # "file_num": 2 # }, # "facilities": [ # { # "facility": "local6", # "severity": "emerg" # } # ], # "path": "logFile" # } # ], # "global_params": { # "archive": { # "file_num": 2, # "size": 111 # }, # "facilities": [ # { # "facility": "cron", # "severity": "debug" # } # ], # "marker_interval": 111, # "preserve_fqdn": true # }, # "hosts": [ # { # "facilities": [ # { # "facility": "all", # "protocol": "udp" # }, # { # "facility": "local7", # "severity": "all" # } # ], # "hostname": "172.16.0.1", # "port": 223 # } # ], # "users": [ # { # "facilities": [ # { # "facility": "local7", # "severity": "err" # } # ], # "username": "paul" # }, # { # "facilities": [ # { # "facility": "local7", # "severity": "debug" # } # ], # "username": "vyos" # } # ] # } # } """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: list sample: - set system syslog console facility local7 level err - set system syslog host 172.16.0.1 port 223 - set system syslog global archive size 111 rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - set system syslog host 172.16.0.1 port 223 - set system syslog user vyos facility local7 level debug - set system syslog global facility cron level debu gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.logging_global.logging_global import ( Logging_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.logging_global.logging_global import ( Logging_global, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ module = AnsibleModule( argument_spec=Logging_globalArgs.argument_spec, mutually_exclusive=[["config", "running_config"]], required_if=[ ["state", "merged", ["config"]], ["state", "replaced", ["config"]], ["state", "overridden", ["config"]], ["state", "rendered", ["config"]], ["state", "parsed", ["running_config"]], ], supports_check_mode=True, ) result = Logging_global(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_ntp_global.py b/plugins/modules/vyos_ntp_global.py index 6461a32b..ae1330ff 100644 --- a/plugins/modules/vyos_ntp_global.py +++ b/plugins/modules/vyos_ntp_global.py @@ -1,812 +1,811 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The module file for vyos_ntp_global """ from __future__ import absolute_import, division, print_function - __metaclass__ = type DOCUMENTATION = """ module: vyos_ntp_global version_added: 1.0.0 short_description: NTP global resource module description: - This module manages ntp configuration on devices running Vyos author: - Varshitha Yataluru (@YVarshitha) notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025 - This module works with connection C(network_cli). - "VyOS v.1.4+ uses chronyd, and path changes from `system` to `service`" options: config: description: List of configurations for ntp module type: dict suboptions: allow_clients: description: Network Time Protocol (NTP) server options type: list elements: str listen_addresses: description: local IP addresses for service to listen on type: list elements: str servers: description: Network Time Protocol (NTP) server type: list elements: dict suboptions: server: description: server name or address for NTP type: str options: description: - "server options for NTP" - "`pool` replaces `dynamic` in Vyos 1.3" - "`preempt` is only available in Vyos 1.3 and earlier" - "`nts` was added in Vyos 1.4" - "`ptp` and `interleave` were added in Vyos 1.5" type: list elements: str choices: - dynamic - noselect - pool - preempt - prefer - nts - ptp - interleave 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 ntp). - The states I(replaced) and I(overridden) have identical behaviour for this module. - The state I(parsed) reads the configuration from C(show configuration commands | grep ntp) 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: - deleted - merged - overridden - replaced - gathered - rendered - parsed default: merged """ EXAMPLES = """ # # ------------------- # # 1. Using merged # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set service ntp server time1.vyos.net # set service ntp server time2.vyos.net # set service ntp server time3.vyos.net # vyos@vyos:~$ # # Task # # ------------- - name: Replace the existing ntp config with the new config vyos.vyos.vyos_ntp_global: config: allow_clients: - 10.6.6.0/24 listen_addresses: - 10.1.3.1 servers: - server: 203.0.113.0 options: - prefer # Task output: # ------------- # "after": { # "allow_clients": [ # "10.6.6.0/24" # ], # "listen_addresses": [ # "10.1.3.1" # ], # "servers": [ # { # "server": "ser", # "options": [ # "prefer" # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "before": { # }, # "changed": true, # "commands": [ # "set service ntp allow-clients address 10.6.6.0/24", # "set service ntp listen-address 10.1.3.1", # "set service ntp server 203.0.113.0 prefer" # ] # After state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set service ntp allow-clients address '10.6.6.0/24' # set service ntp listen-address '10.1.3.1' # set service ntp server 203.0.113.0 prefer, # set service ntp server time1.vyos.net # set service ntp server time2.vyos.net # set service ntp server time3.vyos.net # vyos@vyos:~$ # # ------------------- # # 2. Using replaced # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set service ntp allow-clients address '10.4.9.0/24' # set service ntp allow-clients address '10.4.7.0/24' # set service ntp allow-clients address '10.1.2.0/24' # set service ntp allow-clients address '10.2.3.0/24' # set service ntp listen-address '10.1.9.16' # set service ntp listen-address '10.5.3.2' # set service ntp listen-address '10.7.9.21' # set service ntp listen-address '10.8.9.4' # set service ntp listen-address '10.4.5.1' # set service ntp server 10.3.6.5 noselect # set service ntp server 10.3.6.5 dynamic # set service ntp server 10.3.6.5 preempt # set service ntp server 10.3.6.5 prefer # set service ntp server server4 noselect # set service ntp server server4 dynamic # set service ntp server server5 # set service ntp server time1.vyos.net # set service ntp server time2.vyos.net # set service ntp server time3.vyos.net # vyos@vyos:~$ # # Task # # ------------- - name: Replace the existing ntp config with the new config vyos.vyos.vyos_ntp_global: config: allow_clients: - 10.6.6.0/24 listen_addresses: - 10.1.3.1 servers: - server: 203.0.113.0 options: - prefer state: replaced # # Task output: # # ------------- # "after": { # "allow_clients": [ # "10.6.6.0/24" # ], # "listen_addresses": [ # "10.1.3.1" # ], # "servers": [ # { # "server": "ser", # "options": [ # "prefer" # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "before": { # "allow_clients": [ # "10.4.7.0/24", # "10.2.3.0/24", # "10.1.2.0/24", # "10.4.9.0/24" # ], # "listen_addresses": [ # "10.7.9.21", # "10.4.5.1", # "10.5.3.2", # "10.8.9.4", # "10.1.9.16" # ], # "servers": [ # { # "server": "10.3.6.5", # "options": [ # "noselect", # "dynamic", # "preempt", # "prefer" # ] # }, # { # "server": "server4", # "options": [ # "noselect", # "dynamic" # ] # }, # { # "server": "server5" # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "changed": true, # "commands": [ # "delete service ntp allow-clients address 10.4.7.0/24", # "delete service ntp allow-clients address 10.2.3.0/24", # "delete service ntp allow-clients address 10.1.2.0/24", # "delete service ntp allow-clients address 10.4.9.0/24", # "delete service ntp listen-address 10.7.9.21", # "delete service ntp listen-address 10.4.5.1", # "delete service ntp listen-address 10.5.3.2", # "delete service ntp listen-address 10.8.9.4", # "delete service ntp listen-address 10.1.9.16", # "delete service ntp server 10.3.6.5", # "delete service ntp server server4", # "delete service ntp server server5", # "set service ntp allow-clients address 10.6.6.0/24", # "set service ntp listen-address 10.1.3.1", # "set service ntp server 203.0.113.0 prefer" # ] # After state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set service ntp allow-clients address '10.6.6.0/24' # set service ntp listen-address '10.1.3.1' # set service ntp server 203.0.113.0 prefer, # set service ntp server time1.vyos.net # set service ntp server time2.vyos.net # set service ntp server time3.vyos.net # vyos@vyos:~$ # # ------------------- # # 3. Using overridden # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set service ntp allow-clients address '10.6.6.0/24' # set service ntp listen-address '10.1.3.1' # set service ntp server 203.0.113.0 prefer, # set service ntp server time1.vyos.net # set service ntp server time2.vyos.net # set service ntp server time3.vyos.net # vyos@vyos:~$ # Task # ------------- - name: Override ntp config vyos.vyos.vyos_ntp_global: config: allow_clients: - 10.3.3.0/24 listen_addresses: - 10.7.8.1 servers: - server: server1 options: - dynamic - prefer - server: server2 options: - noselect - preempt - server: serv state: overridden # # Task output: # # ------------- # "after": { # "allow_clients": [ # "10.3.3.0/24" # ], # "listen_addresses": [ # "10.7.8.1" # ], # "servers": [ # { # "server": "serv" # }, # { # "server": "server1", # "options": [ # "dynamic", # "prefer" # ] # }, # { # "server": "server2", # "options": [ # "noselect", # "preempt" # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "before": { # "allow_clients": [ # "10.6.6.0/24" # ], # "listen_addresses": [ # "10.1.3.1" # ], # "servers": [ # { # "server": "ser", # "options": [ # "prefer" # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "changed": true, # "commands": [ # "delete service ntp allow-clients address 10.6.6.0/24", # "delete service ntp listen-address 10.1.3.1", # "delete service ntp server ser", # "set service ntp allow-clients address 10.3.3.0/24", # "set service ntp listen-address 10.7.8.1", # "set service ntp server server1 dynamic", # "set service ntp server server1 prefer", # "set service ntp server server2 noselect", # "set service ntp server server2 preempt", # "set service ntp server serv" # ] # After state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set service ntp allow-clients address '10.3.3.0/24' # set service ntp listen-address '10.7.8.1' # set service ntp server serv # set service ntp server server1 dynamic # set service ntp server server1 prefer # set service ntp server server2 noselect # set service ntp server server2 preempt # set service ntp server time1.vyos.net # set service ntp server time2.vyos.net # set service ntp server time3.vyos.net # vyos@vyos:~$ # 4. Using gathered # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set service ntp allow-clients address '10.3.3.0/24' # set service ntp listen-address '10.7.8.1' # set service ntp server serv # set service ntp server server1 dynamic # set service ntp server server1 prefer # set service ntp server server2 noselect # set service ntp server server2 preempt # set service ntp server time1.vyos.net # set service ntp server time2.vyos.net # set service ntp server time3.vyos.net # vyos@vyos:~$ # Task # ------------- - name: Gather ntp config vyos.vyos.vyos_ntp_global: state: gathered # # Task output: # # ------------- # "gathered": { # "allow_clients": [ # "10.3.3.0/24" # ], # "listen_addresses": [ # "10.7.8.1" # ], # "servers": [ # { # "server": "serv" # }, # { # "server": "server1", # "options": [ # "dynamic", # "prefer" # ] # }, # { # "server": "server2", # "options": [ # "noselect", # "preempt" # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # } # After state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set service ntp allow-clients address '10.3.3.0/24' # set service ntp listen-address '10.7.8.1' # set service ntp server serv # set service ntp server server1 dynamic # set service ntp server server1 prefer # set service ntp server server2 noselect # set service ntp server server2 preempt # set service ntp server time1.vyos.net # set service ntp server time2.vyos.net # set service ntp server time3.vyos.net # vyos@vyos:~$ # # ------------------- # # 5. Using deleted # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set service ntp allow-clients address '10.3.3.0/24' # set service ntp listen-address '10.7.8.1' # set service ntp server serv # set service ntp server server1 dynamic # set service ntp server server1 prefer # set service ntp server server2 noselect # set service ntp server server2 preempt # set service ntp server time1.vyos.net # set service ntp server time2.vyos.net # set service ntp server time3.vyos.net # vyos@vyos:~$ # # Task # # ------------- - name: Delete ntp config vyos.vyos.vyos_ntp_global: state: deleted # # Task output: # # ------------- # "after": { # "servers": [ # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "before": { # "allow_clients": [ # "10.3.3.0/24" # ], # "listen_addresses": [ # "10.7.8.1" # ], # "servers": [ # { # "server": "serv" # }, # { # "server": "server1", # "options": [ # "dynamic", # "prefer" # ] # }, # { # "server": "server2", # "options": [ # "noselect", # "preempt" # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "changed": true, # "commands": [ # "delete service ntp allow-clients", # "delete service ntp listen-address", # "delete service ntp server serv", # "delete service ntp server server1", # "delete service ntp server server2" # # ] # After state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set service ntp server time1.vyos.net # set service ntp server time2.vyos.net # set service ntp server time3.vyos.net # vyos@vyos:~$ # # ------------------- # # 6. Using rendered # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set service ntp server time1.vyos.net # set service ntp server time2.vyos.net # set service ntp server time3.vyos.net # vyos@vyos:~$ # Task # ------------- - name: Render ntp config vyos.vyos.vyos_ntp_global: config: allow_clients: - 10.7.7.0/24 - 10.8.8.0/24 listen_addresses: - 10.7.9.1 servers: - server: server7 - server: server45 options: - noselect - prefer - pool - server: time1.vyos.net - server: time2.vyos.net - server: time3.vyos.net state: rendered # # Task output: # # ------------- # "rendered": [ # "set service ntp allow-clients address 10.7.7.0/24", # "set service ntp allow-clients address 10.8.8.0/24", # "set service ntp listen-address 10.7.9.1", # "set service ntp server server7", # "set service ntp server server45 noselect", # "set service ntp server server45 prefer", # "set service ntp server server45 pool", # "set service ntp server time1.vyos.net", # "set service ntp server time2.vyos.net", # "set service ntp server time3.vyos.net" # ] # # ------------------- # # 7. Using parsed # # ------------------- # # sample_config.cfg: # # ------------- # "set service ntp allow-clients address 10.7.7.0/24", # "set service ntp listen-address 10.7.9.1", # "set service ntp server server45 noselect", # "set service ntp allow-clients addres 10.8.6.0/24", # "set service ntp listen-address 10.5.4.1", # "set service ntp server server45 dynamic", # "set service ntp server time1.vyos.net", # "set service ntp server time2.vyos.net", # "set service ntp server time3.vyos.net" # Task: # ------------- - name: Parse externally provided ntp configuration vyos.vyos.vyos_ntp_global: running_config: "{{ lookup('file', './sample_config.cfg') }}" state: parsed # # Task output: # # ------------- # parsed = { # "allow_clients": [ # "10.7.7.0/24", # "10.8.6.0/24 # ], # "listen_addresses": [ # "10.5.4.1", # "10.7.9.1" # ], # "servers": [ # { # "server": "server45", # "options": [ # "noselect", # "dynamic" # # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # # ] # } """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: list sample: - set system ntp server server1 dynamic - set system ntp server server1 prefer - set system ntp server server2 noselect - set system ntp server server2 preempt - set system ntp server server_add preempt rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - set system ntp server server1 dynamic - set system ntp server server1 prefer - set system ntp server server2 noselect - set system ntp server server2 preempt - set system ntp server server_add preempt gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ntp_global.ntp_global import ( Ntp_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ntp_global.ntp_global import ( Ntp_global, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ module = AnsibleModule( argument_spec=Ntp_globalArgs.argument_spec, mutually_exclusive=[["config", "running_config"]], required_if=[ ["state", "merged", ["config"]], ["state", "replaced", ["config"]], ["state", "overridden", ["config"]], ["state", "rendered", ["config"]], ["state", "parsed", ["running_config"]], ], supports_check_mode=True, ) result = Ntp_global(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_ospf_interfaces.py b/plugins/modules/vyos_ospf_interfaces.py index 6a065f17..d49e9d35 100644 --- a/plugins/modules/vyos_ospf_interfaces.py +++ b/plugins/modules/vyos_ospf_interfaces.py @@ -1,949 +1,948 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2024 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The module file for vyos_ospf_interfaces """ from __future__ import absolute_import, division, print_function - __metaclass__ = type DOCUMENTATION = """ module: vyos_ospf_interfaces version_added: 1.2.0 short_description: OSPF Interfaces Resource Module. description: - This module manages OSPF configuration of interfaces on devices running VYOS. - The provided examples of commands are valid for VyOS 1.4+ author: Gomathi Selvi Srinivasan (@GomathiselviS) options: config: description: A list of OSPF configuration for interfaces. type: list elements: dict suboptions: name: description: - Name/Identifier of the interface. type: str address_family: description: - OSPF settings on the interfaces in address-family context. type: list elements: dict suboptions: afi: description: - Address Family Identifier (AFI) for OSPF settings on the interfaces. type: str choices: ['ipv4', 'ipv6'] required: true authentication: description: - Authentication settings on the interface. type: dict suboptions: plaintext_password: description: - Plain Text password. type: str md5_key: description: - md5 parameters. type: dict suboptions: key_id: description: - key id. type: int key: description: - md5 key. type: str bandwidth: description: - Bandwidth of interface (kilobits/sec) type: int cost: description: - metric associated with interface. type: int dead_interval: description: - Time interval to detect a dead router. type: int hello_interval: description: - Timer interval between transmission of hello packets. type: int mtu_ignore: description: - if True, Disable MTU check for Database Description packets. type: bool network: description: - Interface type. type: str priority: description: - Interface priority. type: int retransmit_interval: description: - LSA retransmission interval. type: int transmit_delay: description: - LSA transmission delay. type: int ifmtu: description: - interface MTU. type: int instance: description: - Instance ID. type: str passive: description: - If True, disables forming adjacency. type: bool running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VYOS device by executing the command B(show configuration commands | match "set interfaces"). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state the configuration should be left in. type: str choices: - merged - replaced - overridden - deleted - gathered - parsed - rendered default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # @vyos:~$ show configuration commands | match "ospf" - name: Merge provided configuration with device configuration vyos.vyos.vyos_ospf_interfaces: config: - name: "eth1" address_family: - afi: "ipv4" transmit_delay: 50 priority: 26 network: "point-to-point" - afi: "ipv6" dead_interval: 39 - name: "bond2" address_family: - afi: "ipv4" transmit_delay: 45 bandwidth: 70 authentication: md5_key: key_id: 10 key: "1111111111232345" - afi: "ipv6" passive: true state: merged # After State: # -------------- # vyos@vyos:~$ show configuration commands | match "ospf" # set protocols ospf interface bond2 authentication md5 key-id 10 md5-key '1111111111232345' # set protocols ospf interface bond2 bandwidth '70' # set protocols ospf interface bond2 transmit-delay '45' # set protocols ospfv3 interface bond2 'passive' # set protocols ospf interface eth1 network 'point-to-point' # set protocols ospf interface eth1 priority '26' # set protocols ospf interface eth1 transmit-delay '50' # set protocols ospfv3 interface eth1 dead-interval '39' # "after": [ # " # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "before": [ # { # "name": "eth0" # }, # { # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "changed": true, # "commands": [ # "set protocols ospf interface eth1 transmit-delay 50", # "set protocols ospf interface eth1 priority 26", # "set protocols ospf interface eth1 network point-to-point", # "set protocols ospfv3 interface eth1 dead-interval 39", # "set protocols ospf interface bond2 transmit-delay 45", # "set protocols ospf interface bond2 bandwidth 70", # "set protocols ospf interface bond2 authentication md5 key-id 10 md5-key 1111111111232345", # "set protocols ospfv3 interface bond2 passive" # ], # Using replaced: # Before State: # ------------ # vyos@vyos:~$ show configuration commands | match "ospf" # set protocols ospf interface bond2 authentication md5 key-id 10 md5-key '1111111111232345' # set protocols ospf interface bond2 bandwidth '70' # set protocols ospf interface bond2 transmit-delay '45' # set protocols ospfv3 interface bond2 'passive' # set protocols ospf interface eth1 network 'point-to-point' # set protocols ospf interface eth1 priority '26' # set protocols ospf interface eth1 transmit-delay '50' # set protocols ospfv3 interface eth1 dead-interval '39' - name: Replace provided configuration with device configuration vyos.vyos.vyos_ospf_interfaces: config: - name: "eth1" address_family: - afi: "ipv4" cost: 100 - afi: "ipv6" ifmtu: 33 - name: "bond2" address_family: - afi: "ipv4" transmit_delay: 45 - afi: "ipv6" passive: true state: replaced # After State: # ----------- # vyos@vyos:~$ show configuration commands | match "ospf" # set protocols ospf interface bond2 transmit-delay '45' # set protocols ospfv3 interface bond2 'passive' # set protocols ospf interface eth1 cost '100' # set protocols ospfv3 interface eth1 ifmtu '33' # vyos@vyos:~$ # Module Execution # ---------------- # "after": [ # { # "address_family": [ # { # "afi": "ipv4", # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "cost": 100 # }, # { # "afi": "ipv6", # "ifmtu": 33 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "before": [ # { # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "changed": true, # "commands": [ # "set protocols ospf interface eth1 cost 100", # "set protocols ospfv3 interface eth1 ifmtu 33", # "delete protocols ospf interface eth1 network point-to-point", # "delete protocols ospf interface eth1 priority 26", # "delete protocols ospf interface eth1 transmit-delay 50", # "delete protocols ospfv3 interface eth1 dead-interval 39", # "delete protocols ospf interface bond2 authentication", # "delete protocols ospf interface bond2 bandwidth 70" # ], # # Using Overridden: # ----------------- # Before State: # ------------ # vyos@vyos:~$ show configuration commands | match "ospf" # set protocols ospf interface bond2 authentication md5 key-id 10 md5-key '1111111111232345' # set protocols ospf interface bond2 bandwidth '70' # set protocols ospf interface bond2 transmit-delay '45' # set protocols ospfv3 interface bond2 'passive' # set protocols ospf interface eth1 cost '100' # set protocols ospf interface eth1 network 'point-to-point' # set protocols ospf interface eth1 priority '26' # set protocols ospf interface eth1 transmit-delay '50' # set protocols ospfv3 interface eth1 dead-interval '39' # set protocols ospfv3 interface eth1 ifmtu '33' # vyos@vyos:~$ - name: Override device configuration with provided configuration vyos.vyos.vyos_ospf_interfaces: config: - name: "eth0" address_family: - afi: "ipv4" cost: 100 - afi: "ipv6" ifmtu: 33 passive: true state: overridden # After State: # ----------- # 200~vyos@vyos:~$ show configuration commands | match "ospf" # set protocols ospf interface eth0 cost '100' # set protocols ospfv3 interface eth0 ifmtu '33' # set protocols ospfv3 interface eth0 'passive' # vyos@vyos:~$ # # # "after": [ # { # "name": "bond2" # }, # { # "address_family": [ # { # "afi": "ipv4", # "cost": 100 # }, # { # "afi": "ipv6", # "ifmtu": 33, # "passive": true # } # ], # "name": "eth0" # }, # { # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "before": [ # { # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "cost": 100, # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39, # "ifmtu": 33 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "changed": true, # "commands": [ # "delete protocols ospf interface bond2", # "delete protocols ospfv3 interface bond2", # "delete protocols ospf interface eth1", # "delete protocols ospfv3 interface eth1", # "set protocols ospf interface eth0 cost 100", # "set protocols ospfv3 interface eth0 ifmtu 33", # "set protocols ospfv3 interface eth0 passive" # ], # # Using deleted: # ------------- # before state: # ------------- # vyos@vyos:~$ show configuration commands | match "ospf" # set protocols ospf interface bond2 authentication md5 key-id 10 md5-key '1111111111232345' # set protocols ospf interface bond2 bandwidth '70' # set protocols ospf interface bond2 transmit-delay '45' # set protocols ospfv3 interface bond2 'passive' # set protocols ospf interface eth0 cost '100' # set protocols ospfv3 interface eth0 ifmtu '33' # set protocols ospfv3 interface eth0 'passive' # set protocols ospf interface eth1 network 'point-to-point' # set protocols ospf interface eth1 priority '26' # set protocols ospf interface eth1 transmit-delay '50' # set protocols ospfv3 interface eth1 dead-interval '39' # vyos@vyos:~$ - name: Delete device configuration vyos.vyos.vyos_ospf_interfaces: config: - name: "eth0" state: deleted # After State: # ----------- # vyos@vyos:~$ show configuration commands | match "ospf" # set protocols ospf interface bond2 authentication md5 key-id 10 md5-key '1111111111232345' # set protocols ospf interface bond2 bandwidth '70' # set protocols ospf interface bond2 transmit-delay '45' # set protocols ospfv3 interface bond2 'passive' # set protocols ospf interface eth1 network 'point-to-point' # set protocols ospf interface eth1 priority '26' # set protocols ospf interface eth1 transmit-delay '50' # set protocols ospfv3 interface eth1 dead-interval '39' # vyos@vyos:~$ # # # "after": [ # { # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "before": [ # { # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "address_family": [ # { # "afi": "ipv4", # "cost": 100 # }, # { # "afi": "ipv6", # "ifmtu": 33, # "passive": true # } # ], # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "changed": true, # "commands": [ # "delete protocols ospf interface eth0", # "delete protocols ospfv3 interface eth0" # ], # # Using parsed: # parsed.cfg: # set protocols ospf interface bond2 authentication md5 key-id 10 md5-key '1111111111232345' # set protocols ospf interface bond2 bandwidth '70' # set protocols ospf interface bond2 transmit-delay '45' # set protocols ospfv3 interface bond2 'passive' # set protocols ospf interface eth0 cost '50' # set protocols ospf interface eth0 priority '26' # set protocols ospfv3 interface eth0 instance-id '33' # set protocols ospfv3 interface eth0 'mtu-ignore' # set protocols ospf interface eth1 network 'point-to-point' # set protocols ospf interface eth1 priority '26' # set protocols ospf interface eth1 transmit-delay '50' # set protocols ospfv3 interface eth1 dead-interval '39' # - name: parse configs vyos.vyos.vyos_ospf_interfaces: running_config: "{{ lookup('file', './parsed.cfg') }}" state: parsed # Module Execution: # ---------------- # "parsed": [ # { # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "address_family": [ # { # "afi": "ipv4", # "cost": 50, # "priority": 26 # }, # { # "afi": "ipv6", # "instance": "33", # "mtu_ignore": true # } # ], # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39 # } # ], # "name": "eth1" # } # ] # Using rendered: # -------------- - name: Render vyos.vyos.vyos_ospf_interfaces: config: - name: "eth1" address_family: - afi: "ipv4" transmit_delay: 50 priority: 26 network: "point-to-point" - afi: "ipv6" dead_interval: 39 - name: "bond2" address_family: - afi: "ipv4" transmit_delay: 45 bandwidth: 70 authentication: md5_key: key_id: 10 key: "1111111111232345" - afi: "ipv6" passive: true state: rendered # Module Execution: # ---------------- # "rendered": [ # "set protocols ospf interface eth1 transmit-delay 50", # "set protocols ospf interface eth1 priority 26", # "set protocols ospf interface eth1 network point-to-point", # "set protocols ospfv3 interface eth1 dead-interval 39", # "set protocols ospf interface bond2 transmit-delay 45", # "set protocols ospf interface bond2 bandwidth 70", # "set protocols ospf interface bond2 authentication md5 key-id 10 md5-key 1111111111232345", # "set protocols ospfv3 interface bond2 passive" # ] # # Using Gathered: # -------------- # Native Config: # vyos@vyos:~$ show configuration commands | match "ospf" # set protocols ospf interface bond2 authentication md5 key-id 10 md5-key '1111111111232345' # set protocols ospf interface bond2 bandwidth '70' # set protocols ospf interface bond2 transmit-delay '45' # set protocols ospfv3 interface bond2 'passive' # set protocols ospf interface eth1 network 'point-to-point' # set protocols ospf interface eth1 priority '26' # set protocols ospf interface eth1 transmit-delay '50' # set protocols ospfv3 interface eth1 dead-interval '39' # vyos@vyos:~$ - name: gather configs vyos.vyos.vyos_ospf_interfaces: state: gathered # Module Execution: # ----------------- # "gathered": [ # { # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: list sample: - "set protocols ospf interface bond2 authentication md5 key-id 10 md5-key '1111111111232345'" - "set protocols ospf interface bond2 bandwidth '70'" - "set protocols ospf interface bond2 transmit-delay '45'" rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - "set protocols ospf interface bond2 authentication md5 key-id 10 md5-key '1111111111232345'" - "set protocols ospf interface bond2 bandwidth '70'" - "set protocols ospf interface bond2 transmit-delay '45'" gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospf_interfaces.ospf_interfaces import ( Ospf_interfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospf_interfaces.ospf_interfaces import ( Ospf_interfaces, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ module = AnsibleModule( argument_spec=Ospf_interfacesArgs.argument_spec, mutually_exclusive=[["config", "running_config"]], required_if=[ ["state", "merged", ["config"]], ["state", "replaced", ["config"]], ["state", "overridden", ["config"]], ["state", "rendered", ["config"]], ["state", "parsed", ["running_config"]], ], supports_check_mode=True, ) result = Ospf_interfaces(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_ospfv2.py b/plugins/modules/vyos_ospfv2.py index 000e4d39..85822e89 100644 --- a/plugins/modules/vyos_ospfv2.py +++ b/plugins/modules/vyos_ospfv2.py @@ -1,1827 +1,1826 @@ #!/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: '1.0.0' short_description: OSPFv2 resource module description: This resource module configures and manages attributes of OSPFv2 routes on VyOS network devices. notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025 - The provided examples of commands are valid for VyOS 1.4+ - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Rohit Thakur (@rohitthakur2590) options: config: description: A provided OSPFv2 route configuration. type: dict suboptions: areas: description: OSPFv2 area. type: list elements: dict suboptions: area_id: description: OSPFv2 area identity. type: str area_type: description: Area type. type: dict suboptions: normal: description: Normal OSPFv2 area. type: bool nssa: 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 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: OSPFv2 area authentication type. type: str choices: [plaintext-password, md5] network: description: OSPFv2 network. type: list elements: dict suboptions: address: required: true 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: OSPFv2 area authentication type. type: dict suboptions: md5: description: MD5 key id based authentication. 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: 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 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: OSPFv2 default metric. type: int metric_type: 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 OSPFv2 administrative distance. type: int ospf: 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: description: OSPFv2 specific parameters. type: dict suboptions: 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 elements: str passive_interface_exclude: description: Interface to exclude when using passive-interface default. type: list elements: str 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: 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 elements: str 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: 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: - This option is used only with state I(parsed). - The value of this option should be the output received from the VyOS device by executing the command B(show configuration commands | grep ospf). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - 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 ospf # # - name: Merge the provided configuration with the existing running configuration vyos.vyos.vyos_ospfv2: config: log_adjacency_changes: detail max_metric: router_lsa: administrative: true on_shutdown: 10 on_startup: 10 default_information: originate: always: true metric: 10 metric_type: 2 route_map: ingress mpls_te: enabled: true router_address: 192.0.11.11 auto_cost: reference_bandwidth: 2 neighbor: - neighbor_id: 192.0.11.12 poll_interval: 10 priority: 2 redistribute: - route_type: bgp metric: 10 metric_type: 2 passive_interface: - eth1 - eth2 parameters: router_id: 192.0.1.1 opaque_lsa: true rfc1583_compatibility: true abr_type: cisco areas: - area_id: 2 area_type: normal: true authentication: plaintext-password shortcut: enable - area_id: 3 area_type: nssa: set: true - area_id: 4 area_type: stub: default_cost: 20 network: - address: 192.0.2.0/24 range: - address: 192.0.3.0/24 cost: 10 - address: 192.0.4.0/24 cost: 12 state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": {} # # "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 interface 'eth1' passive", # "set protocols ospf interface 'eth2' passive", # "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" # } # ] # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep ospf # set protocols ospf area 2 area-type 'normal' # set protocols ospf area 2 authentication 'plaintext-password' # set protocols ospf area 2 shortcut 'enable' # set protocols ospf area 3 area-type 'nssa' # set protocols ospf area 4 area-type stub default-cost '20' # set protocols ospf area 4 network '192.0.2.0/24' # set protocols ospf area 4 range 192.0.3.0/24 cost '10' # set protocols ospf area 4 range 192.0.4.0/24 cost '12' # set protocols ospf auto-cost reference-bandwidth '2' # set protocols ospf default-information originate 'always' # set protocols ospf default-information originate metric '10' # set protocols ospf default-information originate metric-type '2' # set protocols ospf default-information originate route-map 'ingress' # set protocols ospf log-adjacency-changes 'detail' # set protocols ospf max-metric router-lsa 'administrative' # set protocols ospf max-metric router-lsa on-shutdown '10' # set protocols ospf max-metric router-lsa on-startup '10' # set protocols ospf mpls-te 'enable' # set protocols ospf mpls-te router-address '192.0.11.11' # set protocols ospf neighbor 192.0.11.12 poll-interval '10' # set protocols ospf neighbor 192.0.11.12 priority '2' # set protocols ospf parameters abr-type 'cisco' # set protocols ospf parameters 'opaque-lsa' # set protocols ospf parameters 'rfc1583-compatibility' # set protocols ospf parameters router-id '192.0.1.1' # set protocols ospf interface 'eth1' passive # set protocols ospf interface 'eth2' passive # set protocols ospf redistribute bgp metric '10' # set protocols ospf redistribute bgp metric-type '2' # Using merged # # Before state: # ------------- # # vyos@vyos# run show configuration commands | grep ospf # # - name: Merge the provided configuration to update existing running configuration vyos.vyos.vyos_ospfv2: config: areas: - area_id: '2' area_type: normal: true authentication: plaintext-password shortcut: enable - area_id: '3' area_type: nssa: set: false - area_id: '4' area_type: stub: default_cost: 20 network: - address: 192.0.2.0/24 - address: 192.0.22.0/24 - address: 192.0.32.0/24 state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # "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 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": { # "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@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 interface 'eth1' passive # set protocols ospf interface 'eth2' passive # 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 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 interface 'eth1' passive # set protocols ospf interface 'eth2' passive # 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: 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 # # # ------------------------- # Module Execution Result # ------------------------- # # "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 interface 'eth2' passive", # "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" # } # ] # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep ospf # set protocols ospf area 2 area-type 'normal' # set protocols ospf area 2 authentication 'plaintext-password' # set protocols ospf area 2 shortcut 'enable' # set protocols ospf area 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 1.1.2.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 interface 'eth1' passive # set protocols ospf redistribute bgp metric '10' # set protocols ospf redistribute bgp metric-type '2' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_ospfv2: config: log_adjacency_changes: detail max_metric: router_lsa: administrative: true on_shutdown: 10 on_startup: 10 default_information: originate: always: true metric: 10 metric_type: 2 route_map: ingress mpls_te: enabled: true router_address: 192.0.11.11 auto_cost: reference_bandwidth: 2 neighbor: - neighbor_id: 192.0.11.12 poll_interval: 10 priority: 2 redistribute: - route_type: bgp metric: 10 metric_type: 2 passive_interface: - 'eth1' - 'eth2' parameters: router_id: 192.0.1.1 opaque_lsa: true rfc1583_compatibility: true abr_type: cisco areas: - area_id: 2 area_type: normal: true authentication: "plaintext-password" shortcut: enable - area_id: 3 area_type: nssa: set: true - area_id: 4 area_type: stub: default_cost: 20 network: - address: 192.0.2.0/24 range: - address: 192.0.3.0/24 cost: 10 - address: 192.0.4.0/24 cost: 12 state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "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 interface 'eth1' passive", # "set protocols ospf interface 'eth2' passive", # "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: Parse 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 interface 'eth1' passive set protocols ospf interface 'eth2' passive set protocols ospf redistribute bgp metric '10' set protocols ospf redistribute bgp metric-type '2' state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "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 gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep ospf # set protocols ospf area 2 area-type 'normal' # set protocols ospf area 2 authentication 'plaintext-password' # set protocols ospf area 2 shortcut 'enable' # set protocols ospf area 3 area-type 'nssa' # set protocols ospf area 4 area-type stub default-cost '20' # set protocols ospf area 4 network '192.0.2.0/24' # set protocols ospf area 4 range 192.0.3.0/24 cost '10' # set protocols ospf area 4 range 192.0.4.0/24 cost '12' # set protocols ospf auto-cost reference-bandwidth '2' # set protocols ospf default-information originate 'always' # set protocols ospf default-information originate metric '10' # set protocols ospf default-information originate metric-type '2' # set protocols ospf default-information originate route-map 'ingress' # set protocols ospf log-adjacency-changes 'detail' # set protocols ospf max-metric router-lsa 'administrative' # set protocols ospf max-metric router-lsa on-shutdown '10' # set protocols ospf max-metric router-lsa on-startup '10' # set protocols ospf mpls-te 'enable' # set protocols ospf mpls-te router-address '192.0.11.11' # set protocols ospf neighbor 192.0.11.12 poll-interval '10' # set protocols ospf neighbor 192.0.11.12 priority '2' # set protocols ospf parameters abr-type 'cisco' # set protocols ospf parameters 'opaque-lsa' # set protocols ospf parameters 'rfc1583-compatibility' # set protocols ospf parameters router-id '192.0.1.1' # set protocols ospf interface 'eth1' passive # set protocols ospf interface 'eth2' passive # 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 # ------------------------- # # "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@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 interface 'eth1' passive # set protocols ospf interface 'eth2' passive # set protocols ospf redistribute bgp metric '10' # set protocols ospf redistribute bgp metric-type '2' # Using deleted # # Before state # ------------- # # vyos@192# run show configuration commands | grep ospf # set protocols ospf area 2 area-type 'normal' # set protocols ospf area 2 authentication 'plaintext-password' # set protocols ospf area 2 shortcut 'enable' # set protocols ospf area 3 area-type 'nssa' # set protocols ospf area 4 area-type stub default-cost '20' # set protocols ospf area 4 network '192.0.2.0/24' # set protocols ospf area 4 range 192.0.3.0/24 cost '10' # set protocols ospf area 4 range 192.0.4.0/24 cost '12' # set protocols ospf auto-cost reference-bandwidth '2' # set protocols ospf default-information originate 'always' # set protocols ospf default-information originate metric '10' # set protocols ospf default-information originate metric-type '2' # set protocols ospf default-information originate route-map 'ingress' # set protocols ospf log-adjacency-changes 'detail' # set protocols ospf max-metric router-lsa 'administrative' # set protocols ospf max-metric router-lsa on-shutdown '10' # set protocols ospf max-metric router-lsa on-startup '10' # set protocols ospf mpls-te 'enable' # set protocols ospf mpls-te router-address '192.0.11.11' # set protocols ospf neighbor 192.0.11.12 poll-interval '10' # set protocols ospf neighbor 192.0.11.12 priority '2' # set protocols ospf parameters abr-type 'cisco' # set protocols ospf parameters 'opaque-lsa' # set protocols ospf parameters 'rfc1583-compatibility' # set protocols ospf parameters router-id '192.0.1.1' # set protocols ospf interface 'eth1' passive # set protocols ospf interface 'eth2' passive # 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: - "set protocols ospf parameters router-id 192.0.1.1" - "set protocols ospf interface 'eth1' passive" """ 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", "rendered", ("config",)), ("state", "overridden", ("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/plugins/modules/vyos_ospfv3.py b/plugins/modules/vyos_ospfv3.py index fcb9d288..89a5ab24 100644 --- a/plugins/modules/vyos_ospfv3.py +++ b/plugins/modules/vyos_ospfv3.py @@ -1,692 +1,691 @@ #!/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", "status": ["preview"], "supported_by": "network", } DOCUMENTATION = """ --- module: vyos_ospfv3 version_added: '1.0.0' short_description: OSPFv3 resource module description: This resource module configures and manages attributes of OSPFv3 routes on VyOS network devices. author: - Rohit Thakur (@rohitthakur2590) notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025 - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). 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 interface: description: Enable OSPVv3 on an interface for this area. aliases: ['interfaces'] type: list elements: dict suboptions: name: description: Interface name. 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: description: OSPFv3 specific parameters. type: dict suboptions: router_id: description: Override the default router identifier. type: str 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 route_map: description: Route map references. 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 existing 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 # # # ------------------------- # 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 # # # ------------------------- # 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 # # # ------------------------- # 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 from the provided configuration 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 # # # ------------------------- # 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 # # # ------------------------- # 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 # # # ------------------------ # 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 ospfv3 parameters router-id '192.0.2.10'" - "set protocols ospfv3 redistribute 'bgp'" """ 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", "overridden", ("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/plugins/modules/vyos_ping.py b/plugins/modules/vyos_ping.py index 58ca3459..1e81111e 100644 --- a/plugins/modules/vyos_ping.py +++ b/plugins/modules/vyos_ping.py @@ -1,253 +1,252 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # (c) 2017, Ansible by Red Hat, inc # # This file is part of Ansible by Red Hat # # 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 . # from __future__ import absolute_import, division, print_function - __metaclass__ = type DOCUMENTATION = """ module: vyos_ping short_description: Tests reachability using ping from VyOS network devices description: - Tests reachability using ping from a VyOS device to a remote destination. - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025 - For a general purpose network module, see the M(ansible.netcommon.net_ping) module. - For Windows targets, use the M(ansible.windows.win_ping) module instead. - For targets running Python, use the M(ansible.builtin.ping) module instead. version_added: 1.0.0 author: - Nilashish Chakraborty (@NilashishC) options: dest: description: - The IP Address or hostname (resolvable by the device) of the remote node. required: true type: str count: description: - Number of packets to send to check reachability. type: int default: 5 source: description: - The source interface or IP Address to use while sending the ping packet(s). type: str ttl: description: - The time-to-live value for the ICMP packet(s). type: int size: description: - Determines the size (in bytes) of the ping packet(s). type: int interval: description: - Determines the interval (in seconds) between consecutive pings. type: int state: description: - Determines if the expected result is success or fail. type: str choices: - absent - present default: present notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025. - For a general purpose network module, see the M(ansible.netcommon.net_ping) module. - For Windows targets, use the M(ansible.windows.win_ping) module instead. - For targets running Python, use the M(ansible.builtin.ping) module instead. - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). extends_documentation_fragment: - vyos.vyos.vyos """ EXAMPLES = """ - name: Test reachability to 10.10.10.10 vyos.vyos.vyos_ping: dest: 10.10.10.10 - name: Test reachability to 10.20.20.20 using source and ttl set vyos.vyos.vyos_ping: dest: 10.20.20.20 source: eth0 ttl: 128 - name: Test reachability to 10.30.30.30 using interval vyos.vyos.vyos_ping: dest: 10.30.30.30 interval: 3 state: absent - name: Test reachability to 10.40.40.40 setting count and source vyos.vyos.vyos_ping: dest: 10.40.40.40 source: eth1 count: 20 size: 512 """ RETURN = """ commands: description: List of commands sent. returned: always type: list sample: ["ping 10.8.38.44 count 10 interface eth0 ttl 128"] packet_loss: description: Percentage of packets lost. returned: always type: str sample: "0%" packets_rx: description: Packets successfully received. returned: always type: int sample: 20 packets_tx: description: Packets successfully transmitted. returned: always type: int sample: 20 rtt: description: The round trip time (RTT) stats. returned: when ping succeeds type: dict sample: {"avg": 2, "max": 8, "min": 1, "mdev": 24} """ import re from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import run_commands def main(): """main entry point for module execution""" argument_spec = dict( count=dict(type="int", default=5), dest=dict(type="str", required=True), source=dict(type="str"), ttl=dict(type="int"), size=dict(type="int"), interval=dict(type="int"), state=dict(type="str", choices=["absent", "present"], default="present"), ) module = AnsibleModule(argument_spec=argument_spec) count = module.params["count"] dest = module.params["dest"] source = module.params["source"] size = module.params["size"] ttl = module.params["ttl"] interval = module.params["interval"] warnings = list() results = {} if warnings: results["warnings"] = warnings results["commands"] = [build_ping(dest, count, size, interval, source, ttl)] ping_results = run_commands(module, commands=results["commands"]) ping_results_list = ping_results[0].split("\n") rtt_info, rate_info = None, None for line in ping_results_list: if line.startswith("rtt"): rtt_info = line if line.startswith("%s packets transmitted" % count): rate_info = line if rtt_info: rtt = parse_rtt(rtt_info) for k, v in rtt.items(): if rtt[k] is not None: rtt[k] = int(v) results["rtt"] = rtt pkt_loss, rx, tx = parse_rate(rate_info) results["packet_loss"] = str(pkt_loss) + "%" results["packets_rx"] = int(rx) results["packets_tx"] = int(tx) validate_results(module, pkt_loss, results) module.exit_json(**results) def build_ping(dest, count, size=None, interval=None, source=None, ttl=None): cmd = "ping {0} count {1}".format(dest, str(count)) if source: cmd += " interface {0}".format(source) if ttl: cmd += " ttl {0}".format(str(ttl)) if size: cmd += " size {0}".format(str(size)) if interval: cmd += " interval {0}".format(str(interval)) return cmd def parse_rate(rate_info): rate_re = re.compile( r"(?P\d+) (?:\w+) (?:\w+), (?P\d+) (?:\w+), (?P\d+)% (?:\w+) (?:\w+), (?:\w+) (?P