diff options
Diffstat (limited to 'collections-debian-merged/ansible_collections/cisco/ios/plugins')
167 files changed, 44264 insertions, 0 deletions
diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/action/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/action/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/action/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/action/ios.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/action/ios.py new file mode 100644 index 00000000..6e06c47f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/action/ios.py @@ -0,0 +1,135 @@ +# +# (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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import sys +import copy + +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + load_provider, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_provider_spec, +) +from ansible.utils.display import Display + +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 ["ios_config", "config"] else False + ) + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection == "network_cli": + provider = self._task.args.get("provider", {}) + if any(provider.values()): + display.warning( + "provider is unnecessary when using network_cli and will be ignored" + ) + del self._task.args["provider"] + elif self._play_context.connection == "local": + provider = load_provider(ios_provider_spec, self._task.args) + pc = copy.deepcopy(self._play_context) + pc.connection = "ansible.netcommon.network_cli" + pc.network_os = "cisco.ios.ios" + pc.remote_addr = provider["host"] or self._play_context.remote_addr + pc.port = int(provider["port"] or self._play_context.port or 22) + pc.remote_user = ( + provider["username"] or self._play_context.connection_user + ) + pc.password = provider["password"] or self._play_context.password + pc.private_key_file = ( + provider["ssh_keyfile"] or self._play_context.private_key_file + ) + pc.become = provider["authorize"] or False + if pc.become: + pc.become_method = "enable" + pc.become_pass = provider["auth_pass"] + + connection = self._shared_loader_obj.connection_loader.get( + "ansible.netcommon.persistent", + pc, + sys.stdin, + task_uuid=self._task._uuid, + ) + + # TODO: Remove below code after ansible minimal is cut out + if connection is None: + pc.connection = "network_cli" + pc.network_os = "ios" + connection = self._shared_loader_obj.connection_loader.get( + "persistent", pc, sys.stdin, task_uuid=self._task._uuid + ) + + display.vvv( + "using connection plugin %s (was local)" % pc.connection, + pc.remote_addr, + ) + + command_timeout = ( + int(provider["timeout"]) + if provider["timeout"] + else connection.get_option("persistent_command_timeout") + ) + connection.set_options( + direct={"persistent_command_timeout": command_timeout} + ) + + socket_path = connection.run() + display.vvvv("socket_path: %s" % socket_path, pc.remote_addr) + if not socket_path: + return { + "failed": True, + "msg": "unable to open shell. Please see: " + + "https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell", + } + + task_vars["ansible_socket"] = socket_path + warnings.append( + [ + "connection local support for this module is deprecated and will be removed in version 2.14, use connection %s" + % pc.connection + ] + ) + else: + 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/collections-debian-merged/ansible_collections/cisco/ios/plugins/cliconf/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/cliconf/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/cliconf/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/cliconf/ios.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/cliconf/ios.py new file mode 100644 index 00000000..e816389a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/cliconf/ios.py @@ -0,0 +1,464 @@ +# +# (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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +author: Ansible Networking Team +cliconf: ios +short_description: Use ios cliconf to run command on Cisco IOS platform +description: +- This ios plugin provides low level abstraction apis for sending and receiving CLI + commands from Cisco IOS network devices. +version_added: 1.0.0 +""" + +import re +import time +import json + +from ansible.errors import AnsibleConnectionFailure +from ansible.module_utils._text import to_text +from ansible.module_utils.common._collections_compat import Mapping +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( + NetworkConfig, + dumps, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible.plugins.cliconf import CliconfBase, enable_mode + + +class Cliconf(CliconfBase): + @enable_mode + def get_config(self, source="running", flags=None, format=None): + if source not in ("running", "startup"): + raise ValueError( + "fetching configuration from %s is not supported" % source + ) + + if format: + raise ValueError( + "'format' value %s is not supported for get_config" % format + ) + + if not flags: + flags = [] + if source == "running": + cmd = "show running-config " + else: + cmd = "show startup-config " + + cmd += " ".join(to_list(flags)) + cmd = cmd.strip() + + return self.send_command(cmd) + + def get_diff( + self, + candidate=None, + running=None, + diff_match="line", + diff_ignore_lines=None, + path=None, + diff_replace="line", + ): + """ + Generate diff between candidate and running configuration. If the + remote host supports onbox diff capabilities ie. supports_onbox_diff in that case + candidate and running configurations are not required to be passed as argument. + In case if onbox diff capability is not supported candidate argument is mandatory + and running argument is optional. + :param candidate: The configuration which is expected to be present on remote host. + :param running: The base configuration which is used to generate diff. + :param diff_match: Instructs how to match the candidate configuration with current device configuration + Valid values are 'line', 'strict', 'exact', 'none'. + 'line' - commands are matched line by line + 'strict' - command lines are matched with respect to position + 'exact' - command lines must be an equal match + 'none' - will not compare the candidate configuration with the running configuration + :param diff_ignore_lines: Use this argument to specify one or more lines that should be + ignored during the diff. This is used for lines in the configuration + that are automatically updated by the system. This argument takes + a list of regular expressions or exact line matches. + :param path: The ordered set of parents that uniquely identify the section or hierarchy + the commands should be checked against. If the parents argument + is omitted, the commands are checked against the set of top + level or global commands. + :param diff_replace: Instructs on the way to perform the configuration on the device. + If the replace argument is set to I(line) then the modified lines are + pushed to the device in configuration mode. If the replace argument is + set to I(block) then the entire command block is pushed to the device in + configuration mode if any line is not correct. + :return: Configuration diff in json format. + { + 'config_diff': '', + 'banner_diff': {} + } + + """ + 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 not in option_values["diff_replace"]: + raise ValueError( + "'replace' value %s in invalid, valid values are %s" + % (diff_replace, ", ".join(option_values["diff_replace"])) + ) + + # prepare candidate configuration + candidate_obj = NetworkConfig(indent=1) + want_src, want_banners = self._extract_banners(candidate) + candidate_obj.load(want_src) + + if running and diff_match != "none": + # running configuration + have_src, have_banners = self._extract_banners(running) + running_obj = NetworkConfig( + indent=1, contents=have_src, ignore_lines=diff_ignore_lines + ) + configdiffobjs = candidate_obj.difference( + running_obj, path=path, match=diff_match, replace=diff_replace + ) + + else: + configdiffobjs = candidate_obj.items + have_banners = {} + + diff["config_diff"] = ( + dumps(configdiffobjs, "commands") if configdiffobjs else "" + ) + banners = self._diff_banners(want_banners, have_banners) + diff["banner_diff"] = banners if banners else {} + return diff + + @enable_mode + def edit_config( + self, candidate=None, commit=True, replace=None, comment=None + ): + resp = {} + operations = self.get_device_operations() + self.check_edit_config_capability( + operations, candidate, commit, replace, comment + ) + + results = [] + requests = [] + if commit: + self.send_command("configure terminal") + for line in to_list(candidate): + if not isinstance(line, Mapping): + line = {"command": line} + + cmd = line["command"] + if cmd != "end" and cmd[0] != "!": + results.append(self.send_command(**line)) + requests.append(cmd) + + self.send_command("end") + else: + raise ValueError("check mode is not supported") + + resp["request"] = requests + resp["response"] = results + return resp + + def edit_macro( + self, candidate=None, commit=True, replace=None, comment=None + ): + """ + ios_config: + lines: "{{ macro_lines }}" + parents: "macro name {{ macro_name }}" + after: '@' + match: line + replace: block + """ + resp = {} + operations = self.get_device_operations() + self.check_edit_config_capability( + operations, candidate, commit, replace, comment + ) + + results = [] + requests = [] + if commit: + commands = "" + self.send_command("config terminal") + time.sleep(0.1) + # first item: macro command + commands += candidate.pop(0) + "\n" + multiline_delimiter = candidate.pop(-1) + for line in candidate: + commands += " " + line + "\n" + commands += multiline_delimiter + "\n" + obj = {"command": commands, "sendonly": True} + results.append(self.send_command(**obj)) + requests.append(commands) + + time.sleep(0.1) + self.send_command("end", sendonly=True) + time.sleep(0.1) + results.append(self.send_command("\n")) + requests.append("\n") + + resp["request"] = requests + resp["response"] = results + return resp + + def get( + self, + command=None, + prompt=None, + answer=None, + sendonly=False, + output=None, + newline=True, + 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 get_device_info(self): + device_info = {} + + device_info["network_os"] = "ios" + reply = self.get(command="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).strip(",") + + model_search_strs = [ + r"^[Cc]isco (.+) \(revision", + r"^[Cc]isco (\S+).+bytes of .*memory", + ] + for item in model_search_strs: + match = re.search(item, data, re.M) + if match: + version = match.group(1).split(" ") + device_info["network_os_model"] = version[0] + break + + match = re.search(r"^(.+) uptime", data, re.M) + if match: + device_info["network_os_hostname"] = match.group(1) + + match = re.search(r'image file is "(.+)"', data) + if match: + device_info["network_os_image"] = match.group(1) + + return device_info + + def get_device_operations(self): + return { + "supports_diff_replace": True, + "supports_commit": False, + "supports_rollback": False, + "supports_defaults": True, + "supports_onbox_diff": False, + "supports_commit_comment": False, + "supports_multiline_delimiter": True, + "supports_diff_match": True, + "supports_diff_ignore_lines": True, + "supports_generate_diff": True, + "supports_replace": False, + } + + def get_option_values(self): + return { + "format": ["text"], + "diff_match": ["line", "strict", "exact", "none"], + "diff_replace": ["line", "block"], + "output": [], + } + + def get_capabilities(self): + result = super(Cliconf, self).get_capabilities() + result["rpc"] += [ + "edit_banner", + "get_diff", + "run_commands", + "get_defaults_flag", + ] + result["device_operations"] = self.get_device_operations() + result.update(self.get_option_values()) + return json.dumps(result) + + def edit_banner( + self, candidate=None, multiline_delimiter="@", commit=True + ): + """ + Edit banner on remote device + :param banners: Banners to be loaded in json format + :param multiline_delimiter: Line delimiter for banner + :param commit: Boolean value that indicates if the device candidate + configuration should be pushed in the running configuration or discarded. + :param diff: Boolean flag to indicate if configuration that is applied on remote host should + generated and returned in response or not + :return: Returns response of executing the configuration command received + from remote host + """ + resp = {} + banners_obj = json.loads(candidate) + results = [] + requests = [] + if commit: + for key, value in iteritems(banners_obj): + key += " %s" % multiline_delimiter + self.send_command("config terminal", sendonly=True) + for cmd in [key, value, multiline_delimiter]: + obj = {"command": cmd, "sendonly": True} + results.append(self.send_command(**obj)) + requests.append(cmd) + + self.send_command("end", sendonly=True) + time.sleep(0.1) + results.append(self.send_command("\n")) + requests.append("\n") + + resp["request"] = requests + resp["response"] = results + + return resp + + 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", to_text(e)) + + responses.append(out) + + return responses + + def get_defaults_flag(self): + """ + The method identifies the filter that should be used to fetch running-configuration + with defaults. + :return: valid default filter + """ + out = self.get("show running-config ?") + out = to_text(out, errors="surrogate_then_replace") + + commands = set() + for line in out.splitlines(): + if line.strip(): + commands.add(line.strip().split()[0]) + + if "all" in commands: + return "all" + else: + return "full" + + def set_cli_prompt_context(self): + """ + Make sure we are in the operational cli mode + :return: None + """ + if self._connection.connected: + out = self._connection.get_prompt() + + if out is None: + raise AnsibleConnectionFailure( + message=u"cli prompt is not identified from the last received" + u" response window: %s" + % self._connection._last_recv_window + ) + + if re.search( + r"config.*\)#", + to_text(out, errors="surrogate_then_replace").strip(), + ): + self._connection.queue_message( + "vvvv", "wrong context, sending end to device" + ) + self._connection.send_command("end") + + def _extract_banners(self, config): + banners = {} + banner_cmds = re.findall(r"^banner (\w+)", config, re.M) + for cmd in banner_cmds: + regex = r"banner %s \^C(.+?)(?=\^C)" % cmd + match = re.search(regex, config, re.S) + if match: + key = "banner %s" % cmd + banners[key] = match.group(1).strip() + + for cmd in banner_cmds: + regex = r"banner %s \^C(.+?)(?=\^C)" % cmd + match = re.search(regex, config, re.S) + if match: + config = config.replace(str(match.group(1)), "") + + config = re.sub(r"banner \w+ \^C\^C", "!! banner removed", config) + return config, banners + + def _diff_banners(self, want, have): + candidate = {} + for key, value in iteritems(want): + if value != have.get(key): + candidate[key] = value + return candidate diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/doc_fragments/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/doc_fragments/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/doc_fragments/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/doc_fragments/ios.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/doc_fragments/ios.py new file mode 100644 index 00000000..0bb9cae5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/doc_fragments/ios.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +# Copyright: (c) 2015, Peter Sprygada <psprygada@ansible.com> +# 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: + provider: + description: + - B(Deprecated) + - 'Starting with Ansible 2.5 we recommend using C(connection: network_cli).' + - For more information please see the L(IOS Platform Options guide, ../network/user_guide/platform_ios.html). + - HORIZONTALLINE + - A dict object containing connection details. + type: dict + suboptions: + host: + description: + - Specifies the DNS host name or address for connecting to the remote device + over the specified transport. The value of host is used as the destination + address for the transport. + type: str + port: + description: + - Specifies the port to use when building the connection to the remote device. + type: int + username: + description: + - Configures the username to use to authenticate the connection to the remote + device. This value is used to authenticate the SSH session. If the value + is not specified in the task, the value of environment variable C(ANSIBLE_NET_USERNAME) + will be used instead. + type: str + password: + description: + - Specifies the password to use to authenticate the connection to the remote + device. This value is used to authenticate the SSH session. If the value + is not specified in the task, the value of environment variable C(ANSIBLE_NET_PASSWORD) + will be used instead. + type: str + timeout: + description: + - Specifies the timeout in seconds for communicating with the network device + for either connecting or sending commands. If the timeout is exceeded before + the operation is completed, the module will error. + type: int + ssh_keyfile: + description: + - Specifies the SSH key to use to authenticate the connection to the remote + device. This value is the path to the key used to authenticate the SSH + session. If the value is not specified in the task, the value of environment + variable C(ANSIBLE_NET_SSH_KEYFILE) will be used instead. + type: path + authorize: + description: + - Instructs the module to enter privileged mode on the remote device before + sending any commands. If not specified, the device will attempt to execute + all commands in non-privileged mode. If the value is not specified in the + task, the value of environment variable C(ANSIBLE_NET_AUTHORIZE) will be + used instead. + type: bool + default: false + auth_pass: + description: + - Specifies the password to use if required to enter privileged mode on the + remote device. If I(authorize) is false, then this argument does nothing. + If the value is not specified in the task, the value of environment variable + C(ANSIBLE_NET_AUTH_PASS) will be used instead. + type: str +notes: +- For more information on using Ansible to manage network devices see the :ref:`Ansible + Network Guide <network_guide>` +- For more information on using Ansible to manage Cisco devices see the `Cisco integration + page <https://www.ansible.com/integrations/networks/cisco>`_. +""" diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acl_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acl_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acl_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acl_interfaces/acl_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acl_interfaces/acl_interfaces.py new file mode 100644 index 00000000..24b32d7b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acl_interfaces/acl_interfaces.py @@ -0,0 +1,83 @@ +# +# -*- 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 ios_acl_interfaces module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Acl_InterfacesArgs(object): + """The arg spec for the ios_acl_interfaces module + """ + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "name": {"required": True, "type": "str"}, + "access_groups": { + "type": "list", + "elements": "dict", + "options": { + "afi": { + "required": True, + "choices": ["ipv4", "ipv6"], + "type": "str", + }, + "acls": { + "type": "list", + "elements": "dict", + "options": { + "name": {"required": True, "type": "str"}, + "direction": { + "required": True, + "choices": ["in", "out"], + "type": "str", + }, + }, + }, + }, + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acls/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acls/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acls/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acls/acls.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acls/acls.py new file mode 100644 index 00000000..87faedb0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acls/acls.py @@ -0,0 +1,404 @@ +# +# -*- 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 ios_acls module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class AclsArgs(object): + """The arg spec for the ios_acls module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "afi": { + "required": True, + "choices": ["ipv4", "ipv6"], + "type": "str", + }, + "acls": { + "elements": "dict", + "type": "list", + "options": { + "name": {"required": True, "type": "str"}, + "acl_type": { + "choices": ["extended", "standard"], + "type": "str", + }, + "aces": { + "elements": "dict", + "type": "list", + "options": { + "grant": { + "choices": ["permit", "deny"], + "type": "str", + }, + "sequence": {"type": "int"}, + "evaluate": {"type": "str"}, + "source": { + "type": "dict", + "mutually_exclusive": [ + ["address", "any", "host"], + ["wildcard_bits", "any", "host"], + ], + "options": { + "address": {"type": "str"}, + "wildcard_bits": {"type": "str"}, + "any": {"type": "bool"}, + "host": {"type": "str"}, + "port_protocol": { + "type": "dict", + "options": { + "eq": {"type": "str"}, + "gt": {"type": "str"}, + "lt": {"type": "str"}, + "neq": {"type": "str"}, + "range": { + "type": "dict", + "options": { + "start": { + "type": "int" + }, + "end": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "destination": { + "type": "dict", + "mutually_exclusive": [ + ["address", "any", "host"], + ["wildcard_bits", "any", "host"], + ], + "options": { + "address": {"type": "str"}, + "wildcard_bits": {"type": "str"}, + "any": {"type": "bool"}, + "host": {"type": "str"}, + "port_protocol": { + "type": "dict", + "options": { + "eq": {"type": "str"}, + "gt": {"type": "str"}, + "lt": {"type": "str"}, + "neq": {"type": "str"}, + "range": { + "type": "dict", + "options": { + "start": { + "type": "int" + }, + "end": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "protocol": {"type": "str"}, + "protocol_options": { + "type": "dict", + "options": { + "protocol_number": {"type": "int"}, + "ahp": {"type": "bool"}, + "eigrp": {"type": "bool"}, + "esp": {"type": "bool"}, + "gre": {"type": "bool"}, + "hbh": {"type": "bool"}, + "icmp": { + "type": "dict", + "options": { + "administratively_prohibited": { + "type": "bool" + }, + "alternate_address": { + "type": "bool" + }, + "conversion_error": { + "type": "bool" + }, + "dod_host_prohibited": { + "type": "bool" + }, + "dod_net_prohibited": { + "type": "bool" + }, + "echo": {"type": "bool"}, + "echo_reply": {"type": "bool"}, + "general_parameter_problem": { + "type": "bool" + }, + "host_isolated": { + "type": "bool" + }, + "host_precedence_unreachable": { + "type": "bool" + }, + "host_redirect": { + "type": "bool" + }, + "host_tos_redirect": { + "type": "bool" + }, + "host_tos_unreachable": { + "type": "bool" + }, + "host_unknown": { + "type": "bool" + }, + "host_unreachable": { + "type": "bool" + }, + "information_reply": { + "type": "bool" + }, + "information_request": { + "type": "bool" + }, + "mask_reply": {"type": "bool"}, + "mask_request": { + "type": "bool" + }, + "mobile_redirect": { + "type": "bool" + }, + "net_redirect": { + "type": "bool" + }, + "net_tos_redirect": { + "type": "bool" + }, + "net_tos_unreachable": { + "type": "bool" + }, + "net_unreachable": { + "type": "bool" + }, + "network_unknown": { + "type": "bool" + }, + "no_room_for_option": { + "type": "bool" + }, + "option_missing": { + "type": "bool" + }, + "packet_too_big": { + "type": "bool" + }, + "parameter_problem": { + "type": "bool" + }, + "port_unreachable": { + "type": "bool" + }, + "precedence_unreachable": { + "type": "bool" + }, + "protocol_unreachable": { + "type": "bool" + }, + "reassembly_timeout": { + "type": "bool" + }, + "redirect": {"type": "bool"}, + "router_advertisement": { + "type": "bool" + }, + "router_solicitation": { + "type": "bool" + }, + "source_quench": { + "type": "bool" + }, + "source_route_failed": { + "type": "bool" + }, + "time_exceeded": { + "type": "bool" + }, + "timestamp_reply": { + "type": "bool" + }, + "timestamp_request": { + "type": "bool" + }, + "traceroute": {"type": "bool"}, + "ttl_exceeded": { + "type": "bool" + }, + "unreachable": { + "type": "bool" + }, + }, + }, + "igmp": { + "type": "dict", + "options": { + "dvmrp": {"type": "bool"}, + "host_query": {"type": "bool"}, + "mtrace_resp": { + "type": "bool" + }, + "mtrace_route": { + "type": "bool" + }, + "pim": {"type": "bool"}, + "trace": {"type": "bool"}, + "v1host_report": { + "type": "bool" + }, + "v2host_report": { + "type": "bool" + }, + "v2leave_group": { + "type": "bool" + }, + "v3host_report": { + "type": "bool" + }, + }, + }, + "ip": {"type": "bool"}, + "ipv6": {"type": "bool"}, + "ipinip": {"type": "bool"}, + "nos": {"type": "bool"}, + "ospf": {"type": "bool"}, + "pcp": {"type": "bool"}, + "pim": {"type": "bool"}, + "sctp": {"type": "bool"}, + "tcp": { + "options": { + "ack": {"type": "bool"}, + "established": { + "type": "bool" + }, + "fin": {"type": "bool"}, + "psh": {"type": "bool"}, + "rst": {"type": "bool"}, + "syn": {"type": "bool"}, + "urg": {"type": "bool"}, + }, + "type": "dict", + }, + "udp": {"type": "bool"}, + }, + }, + "dscp": {"type": "str"}, + "fragments": {"type": "str"}, + "log": {"type": "str"}, + "log_input": {"type": "str"}, + "option": { + "type": "dict", + "options": { + "add_ext": {"type": "bool"}, + "any_options": {"type": "bool"}, + "com_security": {"type": "bool"}, + "dps": {"type": "bool"}, + "encode": {"type": "bool"}, + "eool": {"type": "bool"}, + "ext_ip": {"type": "bool"}, + "ext_security": {"type": "bool"}, + "finn": {"type": "bool"}, + "imitd": {"type": "bool"}, + "lsr": {"type": "bool"}, + "mtup": {"type": "bool"}, + "mtur": {"type": "bool"}, + "no_op": {"type": "bool"}, + "nsapa": {"type": "bool"}, + "record_route": {"type": "bool"}, + "router_alert": {"type": "bool"}, + "sdb": {"type": "bool"}, + "security": {"type": "bool"}, + "ssr": {"type": "bool"}, + "stream_id": {"type": "bool"}, + "timestamp": {"type": "bool"}, + "traceroute": {"type": "bool"}, + "ump": {"type": "bool"}, + "visa": {"type": "bool"}, + "zsu": {"type": "bool"}, + }, + }, + "precedence": {"type": "int"}, + "time_range": {"type": "str"}, + "tos": { + "type": "dict", + "options": { + "service_value": {"type": "int"}, + "max_reliability": {"type": "bool"}, + "max_throughput": {"type": "bool"}, + "min_delay": {"type": "bool"}, + "min_monetary_cost": {"type": "bool"}, + "normal": {"type": "bool"}, + }, + }, + "ttl": { + "type": "dict", + "options": { + "eq": {"type": "int"}, + "gt": {"type": "int"}, + "lt": {"type": "int"}, + "neq": {"type": "int"}, + "range": { + "type": "dict", + "options": { + "start": {"type": "int"}, + "end": {"type": "int"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py new file mode 100644 index 00000000..3666a359 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py @@ -0,0 +1,910 @@ +# -*- 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 + +############################################# +# 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 cisco.ios_bgp_global module +""" + + +class Bgp_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the cisco.ios_bgp_global module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "type": "dict", + "options": { + "as_number": {"type": "str", "required": True}, + "aggregate_address": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "netmask": {"type": "str"}, + "advertise_map": {"type": "str"}, + "as_confed_set": {"type": "bool"}, + "as_set": {"type": "bool"}, + "attribute_map": {"type": "str"}, + "summary_only": {"type": "bool"}, + "suppress_map": {"type": "str"}, + }, + }, + "auto_summary": {"type": "bool"}, + "bgp": { + "type": "dict", + "options": { + "additional_paths": { + "type": "dict", + "options": { + "install": {"type": "bool"}, + "receive": {"type": "bool"}, + "select": { + "type": "dict", + "options": { + "all": {"type": "bool"}, + "best": {"type": "int"}, + "best_external": {"type": "bool"}, + "group_best": {"type": "bool"}, + }, + }, + "send": {"type": "bool"}, + }, + }, + "advertise_best_external": {"type": "bool"}, + "aggregate_timer": {"type": "int"}, + "always_compare_med": {"type": "bool"}, + "asnotation": {"type": "bool"}, + "bestpath": { + "type": "list", + "elements": "dict", + "options": { + "aigp": {"type": "bool"}, + "compare_routerid": {"type": "bool"}, + "cost_community": {"type": "bool"}, + "igp_metric": {"type": "bool"}, + "med": { + "type": "dict", + "options": { + "confed": {"type": "bool"}, + "missing_as_worst": {"type": "bool"}, + }, + }, + }, + }, + "client_to_client": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "all": {"type": "bool"}, + "intra_cluster": {"type": "str"}, + }, + }, + "cluster_id": {"type": "bool"}, + "confederation": { + "type": "dict", + "options": { + "identifier": {"type": "str"}, + "peers": {"type": "str"}, + }, + }, + "consistency_checker": { + "type": "dict", + "options": { + "auto_repair": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "interval": {"type": "int"}, + }, + }, + "error_message": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "interval": {"type": "int"}, + }, + }, + }, + }, + "dampening": { + "type": "dict", + "options": { + "penalty_half_time": {"type": "int"}, + "reuse_route_val": {"type": "int"}, + "suppress_route_val": {"type": "int"}, + "max_suppress": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "deterministic_med": {"type": "bool"}, + "dmzlink_bw": {"type": "bool"}, + "enforce_first_as": {"type": "bool"}, + "enhanced_error": {"type": "bool"}, + "fast_external_fallover": {"type": "bool"}, + "graceful_restart": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "extended": {"type": "bool"}, + "restart_time": {"type": "int"}, + "stalepath_time": {"type": "int"}, + }, + }, + "graceful_shutdown": { + "type": "dict", + "options": { + "neighbors": { + "type": "dict", + "options": { + "time": {"type": "int"}, + "activate": {"type": "bool"}, + }, + }, + "vrfs": { + "type": "dict", + "options": { + "time": {"type": "int"}, + "activate": {"type": "bool"}, + }, + }, + "community": {"type": "str"}, + "local_preference": {"type": "int"}, + }, + }, + "inject_map": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "exist_map_name": {"type": "str"}, + "copy_attributes": {"type": "bool"}, + }, + }, + "listen": { + "type": "dict", + "options": { + "limit": {"type": "int"}, + "range": { + "type": "dict", + "options": { + "ipv4_with_subnet": {"type": "str"}, + "ipv6_with_subnet": {"type": "str"}, + "peer_group": {"type": "str"}, + }, + }, + }, + }, + "log_neighbor_changes": {"type": "bool"}, + "maxas_limit": {"type": "int"}, + "maxcommunity_limit": {"type": "int"}, + "maxextcommunity_limit": {"type": "int"}, + "nexthop": { + "type": "dict", + "options": { + "route_map": {"type": "str"}, + "trigger": { + "type": "dict", + "options": { + "delay": {"type": "int"}, + "enable": {"type": "bool"}, + }, + }, + }, + }, + "nopeerup_delay": { + "type": "list", + "elements": "dict", + "options": { + "cold_boot": {"type": "int"}, + "nsf_switchover": {"type": "int"}, + "post_boot": {"type": "int"}, + "user_initiated": {"type": "int"}, + }, + }, + "recursion": {"type": "bool"}, + "redistribute_internal": {"type": "bool"}, + "refresh": { + "type": "dict", + "options": { + "max_eor_time": {"type": "int"}, + "stalepath_time": {"type": "int"}, + }, + }, + "regexp": {"type": "bool"}, + "route_map": {"type": "bool"}, + "router_id": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "interface": {"type": "str"}, + "vrf": {"type": "bool"}, + }, + }, + "scan_time": {"type": "int"}, + "slow_peer": { + "type": "dict", + "options": { + "detection": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "threshold": {"type": "int"}, + }, + }, + "split_update_group": { + "type": "dict", + "options": { + "dynamic": {"type": "bool"}, + "permanent": {"type": "int"}, + }, + }, + }, + }, + "snmp": {"type": "bool"}, + "sso": {"type": "bool"}, + "soft_reconfig_backup": {"type": "bool"}, + "suppress_inactive": {"type": "bool"}, + "transport": {"type": "bool"}, + "update_delay": {"type": "int"}, + "update_group": {"type": "bool"}, + "upgrade_cli": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "af_mode": {"type": "bool"}, + }, + }, + }, + }, + "bmp": { + "type": "dict", + "options": { + "buffer_size": {"type": "int"}, + "initial_refresh": { + "type": "dict", + "options": { + "delay": {"type": "int"}, + "skip": {"type": "bool"}, + }, + }, + "server": {"type": "int"}, + }, + }, + "default_information": {"type": "bool"}, + "default_metric": {"type": "int"}, + "distance": { + "type": "dict", + "options": { + "admin": { + "type": "dict", + "options": { + "distance": {"type": "int"}, + "address": {"type": "str"}, + "wildcard_bit": {"type": "str"}, + "acl": {"type": "str"}, + }, + }, + "bgp": { + "type": "dict", + "options": { + "routes_external": {"type": "int"}, + "routes_internal": {"type": "int"}, + "routes_local": {"type": "int"}, + }, + }, + "mbgp": { + "type": "dict", + "options": { + "routes_external": {"type": "int"}, + "routes_internal": {"type": "int"}, + "routes_local": {"type": "int"}, + }, + }, + }, + }, + "distribute_list": { + "type": "dict", + "options": { + "acl": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + "interface": {"type": "str"}, + }, + }, + "maximum_paths": { + "type": "dict", + "options": { + "paths": {"type": "int"}, + "eibgp": {"type": "int"}, + "ibgp": {"type": "int"}, + }, + }, + "maximum_secondary_paths": { + "type": "dict", + "options": { + "paths": {"type": "int"}, + "eibgp": {"type": "int"}, + "ibgp": {"type": "int"}, + }, + }, + "neighbor": { + "type": "list", + "elements": "dict", + "options": { + "address": {"type": "str"}, + "tag": {"type": "str"}, + "ipv6_adddress": {"type": "str"}, + "activate": {"type": "bool"}, + "additional_paths": { + "type": "dict", + "options": { + "disable": {"type": "bool"}, + "receive": {"type": "bool"}, + "send": {"type": "bool"}, + }, + }, + "advertise": { + "type": "dict", + "options": { + "additional_paths": { + "type": "dict", + "options": { + "all": {"type": "bool"}, + "best": {"type": "int"}, + "group_best": {"type": "bool"}, + }, + }, + "best_external": {"type": "bool"}, + "diverse_path": { + "type": "dict", + "options": { + "backup": {"type": "bool"}, + "mpath": {"type": "bool"}, + }, + }, + }, + }, + "advertise_map": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "exist_map": {"type": "str"}, + "non_exist_map": {"type": "str"}, + }, + }, + "advertisement_interval": {"type": "int"}, + "aigp": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "send": { + "type": "dict", + "options": { + "cost_community": { + "type": "dict", + "options": { + "id": {"type": "int"}, + "poi": { + "type": "dict", + "options": { + "igp_cost": { + "type": "bool" + }, + "pre_bestpath": { + "type": "bool" + }, + "transitive": { + "type": "bool" + }, + }, + }, + }, + }, + "med": {"type": "bool"}, + }, + }, + }, + }, + "allow_policy": {"type": "bool"}, + "allowas_in": {"type": "int"}, + "as_override": {"type": "bool"}, + "bmp_activate": { + "type": "dict", + "options": { + "all": {"type": "bool"}, + "server": {"type": "int"}, + }, + }, + "capability": { + "type": "dict", + "options": { + "both": {"type": "bool"}, + "receive": {"type": "bool"}, + "send": {"type": "bool"}, + }, + }, + "cluster_id": {"type": "str"}, + "default_originate": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "route_map": {"type": "str"}, + }, + }, + "description": {"type": "str"}, + "disable_connected_check": {"type": "bool"}, + "distribute_list": { + "type": "dict", + "options": { + "acl": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + }, + "dmzlink_bw": {"type": "bool"}, + "ebgp_multihop": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "hop_count": {"type": "int"}, + }, + }, + "fall_over": { + "type": "dict", + "options": { + "bfd": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "multi_hop": {"type": "bool"}, + "single_hop": {"type": "bool"}, + }, + }, + "route_map": {"type": "str"}, + }, + }, + "filter_list": { + "type": "dict", + "options": { + "path_acl": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + }, + "ha_mode": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + "inherit": {"type": "str"}, + "local_as": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "number": {"type": "int"}, + "dual_as": {"type": "bool"}, + "no_prepend": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "replace_as": {"type": "bool"}, + }, + }, + }, + }, + "log_neighbor_changes": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + "maximum_prefix": { + "type": "dict", + "options": { + "max_no": {"type": "int"}, + "threshold_val": {"type": "int"}, + "restart": {"type": "int"}, + "warning_only": {"type": "bool"}, + }, + }, + "next_hop_self": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "all": {"type": "bool"}, + }, + }, + "next_hop_unchanged": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "allpaths": {"type": "bool"}, + }, + }, + "password": {"type": "str"}, + "path_attribute": { + "type": "dict", + "options": { + "discard": { + "type": "dict", + "options": { + "type": {"type": "int"}, + "range": { + "type": "dict", + "options": { + "start": {"type": "int"}, + "end": {"type": "int"}, + }, + }, + "in": {"type": "bool"}, + }, + }, + "treat_as_withdraw": { + "type": "dict", + "options": { + "type": {"type": "int"}, + "range": { + "type": "dict", + "options": { + "start": {"type": "int"}, + "end": {"type": "int"}, + }, + }, + "in": {"type": "bool"}, + }, + }, + }, + }, + "peer_group": {"type": "str"}, + "remote_as": {"type": "int"}, + "remove_private_as": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "all": {"type": "bool"}, + "replace_as": {"type": "bool"}, + }, + }, + "route_map": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + }, + "route_reflector_client": {"type": "bool"}, + "route_server_client": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "context": {"type": "str"}, + }, + }, + "send_community": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "both": {"type": "bool"}, + "extended": {"type": "bool"}, + "standard": {"type": "bool"}, + }, + }, + "send_label": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "explicit_null": {"type": "bool"}, + }, + }, + "shutdown": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "graceful": {"type": "int"}, + }, + }, + "slow_peer": { + "type": "dict", + "options": { + "detection": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + "threshold": {"type": "int"}, + }, + }, + "split_update_group": { + "type": "dict", + "options": { + "dynamic": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + "permanent": {"type": "bool"}, + }, + }, + "static": {"type": "bool"}, + }, + }, + }, + }, + "soft_reconfiguration": {"type": "bool"}, + "timers": { + "type": "dict", + "options": { + "interval": {"type": "int"}, + "holdtime": {"type": "int"}, + "min_holdtime": {"type": "int"}, + }, + }, + "translate_update": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "nlri": { + "type": "dict", + "options": { + "multicast": {"type": "bool"}, + "unicast": {"type": "bool"}, + }, + }, + }, + }, + "transport": { + "type": "dict", + "options": { + "connection_mode": { + "type": "dict", + "options": { + "active": {"type": "bool"}, + "passive": {"type": "bool"}, + }, + }, + "multi_session": {"type": "bool"}, + "path_mtu_discovery": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + }, + }, + "ttl_security": {"type": "int"}, + "unsuppress_map": {"type": "str"}, + "version": {"type": "int"}, + "weight": {"type": "int"}, + }, + }, + "redistribute": { + "type": "list", + "elements": "dict", + "options": { + "application": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "bgp": { + "type": "dict", + "options": { + "as_number": {"type": "str"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "connected": { + "type": "dict", + "options": { + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "eigrp": { + "type": "dict", + "options": { + "as_number": {"type": "str"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "isis": { + "type": "dict", + "options": { + "area_tag": {"type": "str"}, + "clns": {"type": "bool"}, + "ip": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "iso_igrp": { + "type": "dict", + "options": { + "area_tag": {"type": "str"}, + "route_map": {"type": "str"}, + }, + }, + "lisp": { + "type": "dict", + "options": { + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "mobile": { + "type": "dict", + "options": { + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "odr": { + "type": "dict", + "options": { + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "ospf": { + "type": "dict", + "options": { + "process_id": {"type": "int"}, + "match": { + "type": "dict", + "options": { + "external": {"type": "bool"}, + "internal": {"type": "bool"}, + "nssa_external": {"type": "bool"}, + "type_1": {"type": "bool"}, + "type_2": {"type": "bool"}, + }, + }, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + "vrf": {"type": "str"}, + }, + }, + "ospfv3": { + "type": "dict", + "options": { + "process_id": {"type": "int"}, + "match": { + "type": "dict", + "options": { + "external": {"type": "bool"}, + "internal": {"type": "bool"}, + "nssa_external": {"type": "bool"}, + "type_1": {"type": "bool"}, + "type_2": {"type": "bool"}, + }, + }, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "rip": { + "type": "dict", + "options": { + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "static": { + "type": "dict", + "options": { + "clns": {"type": "bool"}, + "ip": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "vrf": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "global": {"type": "bool"}, + }, + }, + }, + }, + "route_server_context": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "address_family": { + "type": "dict", + "options": { + "afi": { + "type": "str", + "choices": ["ipv4", "ipv6"], + }, + "modifier": { + "type": "str", + "choices": ["multicast", "unicast"], + }, + "import_map": {"type": "str"}, + }, + }, + "description": {"type": "str"}, + }, + }, + "scope": { + "type": "dict", + "options": { + "global": {"type": "bool"}, + "vrf": {"type": "str"}, + }, + }, + "synchronization": {"type": "bool"}, + "table_map": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "filter": {"type": "bool"}, + }, + }, + "template": { + "type": "dict", + "options": { + "peer_policy": {"type": "str"}, + "peer_session": {"type": "str"}, + }, + }, + "timers": { + "type": "dict", + "options": { + "keepalive": {"type": "int"}, + "holdtime": {"type": "int"}, + "min_holdtime": {"type": "int"}, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "type": "str", + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "purged", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + }, + } # pylint: disable=C0301 diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/facts/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/facts/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/facts/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/facts/facts.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/facts/facts.py new file mode 100644 index 00000000..935fee75 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/facts/facts.py @@ -0,0 +1,27 @@ +# +# -*- 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 arg spec for the ios facts module. +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class FactsArgs(object): + """ The arg spec for the ios facts module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "gather_subset": dict( + default=["!config"], type="list", elements="str" + ), + "gather_network_resources": dict(type="list", elements="str"), + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/interfaces/interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/interfaces/interfaces.py new file mode 100644 index 00000000..5dbdda17 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/interfaces/interfaces.py @@ -0,0 +1,64 @@ +# +# -*- 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 ios_interfaces module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class InterfacesArgs(object): + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "description": {"type": "str"}, + "enabled": {"default": True, "type": "bool"}, + "speed": {"type": "str"}, + "mtu": {"type": "int"}, + "duplex": {"type": "str", "choices": ["full", "half", "auto"]}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "parsed", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l2_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l2_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l2_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l2_interfaces/l2_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l2_interfaces/l2_interfaces.py new file mode 100644 index 00000000..0f21f3a3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l2_interfaces/l2_interfaces.py @@ -0,0 +1,80 @@ +# +# -*- 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 ios_l2_interfaces module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class L2_InterfacesArgs(object): + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "mode": {"type": "str", "choices": ["access", "trunk"]}, + "access": { + "type": "dict", + "options": {"vlan": {"type": "int"}}, + }, + "voice": { + "type": "dict", + "options": {"vlan": {"type": "int"}}, + }, + "trunk": { + "type": "dict", + "options": { + "allowed_vlans": {"type": "list", "elements": "str"}, + "encapsulation": { + "type": "str", + "choices": ["dot1q", "isl", "negotiate"], + }, + "native_vlan": {"type": "int"}, + "pruning_vlans": {"type": "list", "elements": "str"}, + }, + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "parsed", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/l3_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/l3_interfaces.py new file mode 100644 index 00000000..ce8e1396 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/l3_interfaces.py @@ -0,0 +1,75 @@ +# +# -*- 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 ios_l3_interfaces module +""" + + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class L3_InterfacesArgs(object): + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "ipv4": { + "elements": "dict", + "type": "list", + "options": { + "address": {"type": "str"}, + "secondary": {"type": "bool"}, + "dhcp_client": {"type": "int"}, + "dhcp_hostname": {"type": "str"}, + }, + }, + "ipv6": { + "elements": "dict", + "type": "list", + "options": {"address": {"type": "str"}}, + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "parsed", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp/lacp.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp/lacp.py new file mode 100644 index 00000000..09f40b28 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp/lacp.py @@ -0,0 +1,64 @@ +# +# -*- 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 ios_lacp module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class LacpArgs(object): + """The arg spec for the ios_lacp module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "system": { + "options": {"priority": {"required": True, "type": "int"}}, + "type": "dict", + } + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "rendered", + "parsed", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp_interfaces/lacp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 00000000..b0af01a5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,66 @@ +# +# -*- 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 ios_lacp_interfaces module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Lacp_InterfacesArgs(object): + """The arg spec for the ios_lacp_interfaces module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "name": {"required": True, "type": "str"}, + "port_priority": {"type": "int"}, + "fast_switchover": {"type": "bool"}, + "max_bundle": {"type": "int"}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "parsed", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lag_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lag_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lag_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lag_interfaces/lag_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lag_interfaces/lag_interfaces.py new file mode 100644 index 00000000..03021fed --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lag_interfaces/lag_interfaces.py @@ -0,0 +1,79 @@ +# +# -*- 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 ios_lag_interfaces module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Lag_interfacesArgs(object): + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "name": {"required": True, "type": "str"}, + "members": { + "elements": "dict", + "options": { + "member": {"type": "str"}, + "mode": { + "choices": [ + "auto", + "on", + "desirable", + "active", + "passive", + ], + "type": "str", + "required": True, + }, + "link": {"type": "int"}, + }, + "type": "list", + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "parsed", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_global/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_global/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_global/lldp_global.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_global/lldp_global.py new file mode 100644 index 00000000..9133305e --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_global/lldp_global.py @@ -0,0 +1,74 @@ +# +# -*- 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 ios_lldp_global module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Lldp_globalArgs(object): + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "holdtime": {"type": "int"}, + "reinit": {"type": "int"}, + "enabled": {"type": "bool"}, + "timer": {"type": "int"}, + "tlv_select": { + "options": { + "four_wire_power_management": {"type": "bool"}, + "mac_phy_cfg": {"type": "bool"}, + "management_address": {"type": "bool"}, + "port_description": {"type": "bool"}, + "port_vlan": {"type": "bool"}, + "power_management": {"type": "bool"}, + "system_capabilities": {"type": "bool"}, + "system_description": {"type": "bool"}, + "system_name": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "rendered", + "parsed", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_interfaces/lldp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_interfaces/lldp_interfaces.py new file mode 100644 index 00000000..f4ad39f5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_interfaces/lldp_interfaces.py @@ -0,0 +1,73 @@ +# +# -*- 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 ios_lldp_interfaces module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Lldp_InterfacesArgs(object): + """The arg spec for the ios_lldp_interfaces module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "name": {"required": True, "type": "str"}, + "transmit": {"type": "bool"}, + "receive": {"type": "bool"}, + "med_tlv_select": { + "options": {"inventory_management": {"type": "bool"}}, + "type": "dict", + }, + "tlv_select": { + "options": {"power_management": {"type": "bool"}}, + "type": "dict", + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "parsed", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospf_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospf_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospf_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospf_interfaces/ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 00000000..b705c4b5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,200 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the cisco.ios_ospf_interfaces module +""" + + +class Ospf_InterfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the cisco.ios_ospf_interfaces module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "address_family": { + "type": "list", + "elements": "dict", + "options": { + "afi": { + "type": "str", + "choices": ["ipv4", "ipv6"], + "required": True, + }, + "process": { + "type": "dict", + "options": { + "id": {"type": "int"}, + "area_id": {"type": "str"}, + "secondaries": {"type": "bool"}, + "instance_id": {"type": "int"}, + }, + }, + "adjacency": {"type": "bool"}, + "authentication": { + "type": "dict", + "options": { + "key_chain": {"type": "str"}, + "message_digest": {"type": "bool"}, + "null": {"type": "bool"}, + }, + }, + "bfd": {"type": "bool"}, + "cost": { + "type": "dict", + "options": { + "interface_cost": {"type": "int"}, + "dynamic_cost": { + "type": "dict", + "options": { + "default": {"type": "int"}, + "hysteresis": { + "type": "dict", + "options": { + "percent": {"type": "int"}, + "threshold": {"type": "int"}, + }, + }, + "weight": { + "type": "dict", + "options": { + "l2_factor": {"type": "int"}, + "latency": {"type": "int"}, + "oc": {"type": "bool"}, + "resources": {"type": "int"}, + "throughput": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "database_filter": {"type": "bool"}, + "dead_interval": { + "type": "dict", + "options": { + "time": {"type": "int"}, + "minimal": {"type": "int"}, + }, + }, + "demand_circuit": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "ignore": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + "flood_reduction": {"type": "bool"}, + "hello_interval": {"type": "int"}, + "lls": {"type": "bool"}, + "manet": { + "type": "dict", + "options": { + "cost": { + "type": "dict", + "options": { + "percent": {"type": "int"}, + "threshold": {"type": "int"}, + }, + }, + "link_metrics": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "cost_threshold": {"type": "int"}, + }, + }, + }, + }, + "mtu_ignore": {"type": "bool"}, + "multi_area": { + "type": "dict", + "options": { + "id": {"type": "int"}, + "cost": {"type": "int"}, + }, + }, + "neighbor": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "cost": {"type": "int"}, + "database_filter": {"type": "bool"}, + "poll_interval": {"type": "int"}, + "priority": {"type": "int"}, + }, + }, + "network": { + "type": "dict", + "options": { + "broadcast": {"type": "bool"}, + "manet": {"type": "bool"}, + "non_broadcast": {"type": "bool"}, + "point_to_multipoint": {"type": "bool"}, + "point_to_point": {"type": "bool"}, + }, + }, + "prefix_suppression": {"type": "bool"}, + "priority": {"type": "int"}, + "resync_timeout": {"type": "int"}, + "retransmit_interval": {"type": "int"}, + "shutdown": {"type": "bool"}, + "transmit_delay": {"type": "int"}, + "ttl_security": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "hops": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "type": "str", + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + }, + } # pylint: disable=C0301 diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv2/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv2/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv2/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv2/ospfv2.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv2/ospfv2.py new file mode 100644 index 00000000..fb053745 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv2/ospfv2.py @@ -0,0 +1,547 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the ios_ospfv2 module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Ospfv2Args(object): + """The arg spec for the ios_ospfv2 module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "processes": { + "elements": "dict", + "options": { + "address_family": { + "options": { + "default": {"type": "bool"}, + "snmp_context": {"type": "str"}, + "topology": { + "options": { + "base": {"type": "bool"}, + "name": {"type": "str"}, + "tid": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "adjacency": { + "options": { + "max_adjacency": {"type": "int"}, + "min_adjacency": {"type": "int"}, + "none": {"type": "bool"}, + }, + "type": "dict", + }, + "areas": { + "elements": "dict", + "options": { + "area_id": {"type": "str"}, + "authentication": { + "options": { + "enable": {"type": "bool"}, + "message_digest": {"type": "bool"}, + }, + "type": "dict", + }, + "capability": {"type": "bool"}, + "default_cost": {"type": "int"}, + "filter_list": { + "elements": "dict", + "options": { + "direction": { + "choices": ["in", "out"], + "required": True, + "type": "str", + }, + "name": {"type": "str"}, + }, + "type": "list", + }, + "nssa": { + "options": { + "default_information_originate": { + "options": { + "metric": {"type": "int"}, + "metric_type": { + "choices": [1, 2], + "type": "int", + }, + "nssa_only": {"type": "bool"}, + }, + "type": "dict", + }, + "no_ext_capability": {"type": "bool"}, + "no_redistribution": {"type": "bool"}, + "no_summary": {"type": "bool"}, + "set": {"type": "bool"}, + "translate": { + "choices": [ + "always", + "suppress-fa", + ], + "type": "str", + }, + }, + "type": "dict", + }, + "ranges": { + "options": { + "address": {"type": "str"}, + "advertise": {"type": "bool"}, + "cost": {"type": "int"}, + "netmask": {"type": "str"}, + "not_advertise": {"type": "bool"}, + }, + "type": "list", + "elements": "dict", + }, + "sham_link": { + "options": { + "cost": {"type": "int"}, + "destination": {"type": "str"}, + "source": {"type": "str"}, + "ttl_security": {"type": "int"}, + }, + "type": "dict", + }, + "stub": { + "options": { + "no_ext_capability": {"type": "bool"}, + "no_summary": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "list", + }, + "auto_cost": { + "options": { + "reference_bandwidth": {"type": "int"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "bfd": {"type": "bool"}, + "capability": { + "options": { + "lls": {"type": "bool"}, + "opaque": {"type": "bool"}, + "transit": {"type": "bool"}, + "vrf_lite": {"type": "bool"}, + }, + "type": "dict", + }, + "compatible": { + "options": { + "rfc1583": {"type": "bool"}, + "rfc1587": {"type": "bool"}, + "rfc5243": {"type": "bool"}, + }, + "type": "dict", + }, + "default_information": { + "options": { + "always": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "originate": {"type": "bool"}, + "route_map": {"type": "str"}, + }, + "type": "dict", + }, + "default_metric": {"type": "int"}, + "discard_route": { + "options": { + "external": {"type": "int"}, + "internal": {"type": "int"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "distance": { + "options": { + "admin_distance": { + "options": { + "acl": {"type": "str"}, + "address": {"type": "str"}, + "distance": {"type": "int"}, + "wildcard_bits": {"type": "str"}, + }, + "type": "dict", + }, + "ospf": { + "options": { + "external": {"type": "int"}, + "inter_area": {"type": "int"}, + "intra_area": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "distribute_list": { + "options": { + "acls": { + "elements": "dict", + "options": { + "direction": { + "choices": ["in", "out"], + "required": True, + "type": "str", + }, + "interface": {"type": "str"}, + "name": { + "required": True, + "type": "str", + }, + "protocol": {"type": "str"}, + }, + "type": "list", + }, + "prefix": { + "options": { + "direction": { + "choices": ["in", "out"], + "required": True, + "type": "str", + }, + "gateway_name": {"type": "str"}, + "interface": {"type": "str"}, + "name": { + "required": True, + "type": "str", + }, + "protocol": {"type": "str"}, + }, + "type": "dict", + }, + "route_map": { + "options": { + "name": { + "required": True, + "type": "str", + } + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "domain_id": { + "options": { + "ip_address": { + "options": { + "address": {"type": "str"}, + "secondary": {"type": "bool"}, + }, + "type": "dict", + }, + "null": {"type": "bool"}, + }, + "type": "dict", + }, + "domain_tag": {"type": "int"}, + "event_log": { + "options": { + "enable": {"type": "bool"}, + "one_shot": {"type": "bool"}, + "pause": {"type": "bool"}, + "size": {"type": "int"}, + }, + "type": "dict", + }, + "help": {"type": "bool"}, + "ignore": {"type": "bool"}, + "interface_id": {"type": "bool"}, + "ispf": {"type": "bool"}, + "limit": { + "options": { + "dc": { + "options": { + "disable": {"type": "bool"}, + "number": {"type": "int"}, + }, + "type": "dict", + }, + "non_dc": { + "options": { + "disable": {"type": "bool"}, + "number": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "local_rib_criteria": { + "options": { + "enable": {"type": "bool"}, + "forwarding_address": {"type": "bool"}, + "inter_area_summary": {"type": "bool"}, + "nssa_translation": {"type": "bool"}, + }, + "type": "dict", + }, + "log_adjacency_changes": { + "options": { + "detail": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "max_lsa": { + "options": { + "ignore_count": {"type": "int"}, + "ignore_time": {"type": "int"}, + "number": {"type": "int"}, + "reset_time": {"type": "int"}, + "threshold_value": {"type": "int"}, + "warning_only": {"type": "bool"}, + }, + "type": "dict", + }, + "max_metric": { + "options": { + "external_lsa": {"type": "int"}, + "include_stub": {"type": "bool"}, + "on_startup": { + "options": { + "time": {"type": "int"}, + "wait_for_bgp": {"type": "bool"}, + }, + "type": "dict", + }, + "router_lsa": { + "required": True, + "type": "bool", + }, + "summary_lsa": {"type": "int"}, + }, + "type": "dict", + }, + "maximum_paths": {"type": "int"}, + "mpls": { + "options": { + "ldp": { + "options": { + "autoconfig": { + "options": { + "area": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "sync": {"type": "bool"}, + }, + "type": "dict", + }, + "traffic_eng": { + "options": { + "area": {"type": "str"}, + "autoroute_exclude": {"type": "str"}, + "interface": { + "options": { + "area": {"type": "int"}, + "interface_type": { + "type": "str" + }, + }, + "type": "dict", + }, + "mesh_group": { + "options": { + "area": {"type": "str"}, + "id": {"type": "int"}, + "interface": {"type": "str"}, + }, + "type": "dict", + }, + "multicast_intact": {"type": "bool"}, + "router_id_interface": {"type": "str"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "neighbor": { + "options": { + "address": {"type": "str"}, + "cost": {"type": "int"}, + "database_filter": {"type": "bool"}, + "poll_interval": {"type": "int"}, + "priority": {"type": "int"}, + }, + "type": "dict", + }, + "network": { + "options": { + "address": {"type": "str"}, + "area": {"type": "str"}, + "wildcard_bits": {"type": "str"}, + }, + "type": "list", + "elements": "dict", + }, + "nsf": { + "options": { + "cisco": { + "options": { + "disable": {"type": "bool"}, + "helper": {"type": "bool"}, + }, + "type": "dict", + }, + "ietf": { + "options": { + "disable": {"type": "bool"}, + "helper": {"type": "bool"}, + "strict_lsa_checking": { + "type": "bool" + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "passive_interface": {"type": "str"}, + "prefix_suppression": {"type": "bool"}, + "priority": {"type": "int"}, + "process_id": {"required": True, "type": "int"}, + "queue_depth": { + "options": { + "hello": { + "options": { + "max_packets": {"type": "int"}, + "unlimited": {"type": "bool"}, + }, + "type": "dict", + }, + "update": { + "options": { + "max_packets": {"type": "int"}, + "unlimited": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "router_id": {"type": "str"}, + "shutdown": {"type": "bool"}, + "summary_address": { + "options": { + "address": {"type": "str"}, + "mask": {"type": "str"}, + "not_advertise": {"type": "bool"}, + "nssa_only": {"type": "bool"}, + "tag": {"type": "int"}, + }, + "type": "dict", + }, + "timers": { + "options": { + "lsa": {"type": "int"}, + "pacing": { + "options": { + "flood": {"type": "int"}, + "lsa_group": {"type": "int"}, + "retransmission": {"type": "int"}, + }, + "type": "dict", + }, + "throttle": { + "options": { + "lsa": { + "options": { + "first_delay": {"type": "int"}, + "max_delay": {"type": "int"}, + "min_delay": {"type": "int"}, + }, + "type": "dict", + }, + "spf": { + "options": { + "between_delay": { + "type": "int" + }, + "max_delay": {"type": "int"}, + "receive_delay": { + "type": "int" + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "traffic_share": {"type": "bool"}, + "ttl_security": { + "options": { + "hops": {"type": "int"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "vrf": {"type": "str"}, + }, + "type": "list", + } + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv3/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv3/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv3/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv3/ospfv3.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv3/ospfv3.py new file mode 100644 index 00000000..6b31ecd4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv3/ospfv3.py @@ -0,0 +1,807 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the cisco.ios_ospfv3 module +""" + + +class Ospfv3Args(object): # pylint: disable=R0903 + """The arg spec for the cisco.ios_ospfv3 module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "type": "dict", + "options": { + "processes": { + "type": "list", + "elements": "dict", + "options": { + "process_id": {"required": True, "type": "int"}, + "address_family": { + "type": "list", + "elements": "dict", + "options": { + "afi": { + "type": "str", + "choices": ["ipv4", "ipv6"], + }, + "unicast": {"type": "bool"}, + "vrf": {"type": "str"}, + "adjacency": { + "type": "dict", + "options": { + "min_adjacency": {"type": "int"}, + "none": {"type": "bool"}, + "max_adjacency": {"type": "int"}, + "disable": {"type": "bool"}, + }, + }, + "areas": { + "type": "list", + "elements": "dict", + "options": { + "area_id": {"type": "str"}, + "authentication": { + "type": "dict", + "options": { + "key_chain": {"type": "str"}, + "null": {"type": "bool"}, + }, + }, + "default_cost": {"type": "int"}, + "filter_list": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"}, + "direction": { + "type": "str", + "choices": ["in", "out"], + "required": True, + }, + }, + }, + "normal": {"type": "bool"}, + "nssa": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "default_information_originate": { + "type": "dict", + "options": { + "metric": { + "type": "int" + }, + "metric_type": { + "type": "int", + "choices": [1, 2], + }, + "nssa_only": { + "type": "bool" + }, + }, + }, + "no_redistribution": { + "type": "bool" + }, + "no_summary": {"type": "bool"}, + "translate": { + "type": "str", + "choices": [ + "always", + "suppress-fa", + ], + }, + }, + }, + "ranges": { + "type": "list", + "elements": "dict", + "options": { + "address": {"type": "str"}, + "netmask": {"type": "str"}, + "advertise": {"type": "bool"}, + "cost": {"type": "int"}, + "not_advertise": { + "type": "bool" + }, + }, + }, + "sham_link": { + "type": "dict", + "options": { + "source": {"type": "str"}, + "destination": {"type": "str"}, + "authentication": { + "type": "dict", + "options": { + "key_chain": { + "type": "str" + }, + "null": { + "type": "bool" + }, + }, + }, + "cost": {"type": "int"}, + "ttl_security": { + "type": "int" + }, + }, + }, + "stub": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "no_summary": {"type": "bool"}, + }, + }, + }, + }, + "authentication": { + "type": "dict", + "options": { + "deployment": {"type": "bool"}, + "normal": {"type": "bool"}, + }, + }, + "auto_cost": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "reference_bandwidth": {"type": "int"}, + }, + }, + "bfd": { + "type": "dict", + "options": { + "all_interfaces": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + "capability": {"type": "bool"}, + "compatible": { + "type": "dict", + "options": { + "rfc1583": {"type": "bool"}, + "rfc1587": {"type": "bool"}, + "rfc5243": {"type": "bool"}, + }, + }, + "default_information": { + "type": "dict", + "options": { + "originate": {"type": "bool"}, + "always": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "default_metric": {"type": "int"}, + "discard_route": { + "type": "dict", + "options": { + "sham_link": {"type": "bool"}, + "external": {"type": "bool"}, + "internal": {"type": "bool"}, + }, + }, + "distance": {"type": "int"}, + "distribute_list": { + "type": "dict", + "options": { + "acls": { + "type": "list", + "elements": "dict", + "options": { + "name": { + "type": "str", + "required": True, + }, + "direction": { + "type": "str", + "required": True, + "choices": ["in", "out"], + }, + "interface": {"type": "str"}, + "protocol": {"type": "str"}, + }, + }, + "prefix": { + "type": "dict", + "options": { + "name": { + "type": "str", + "required": True, + }, + "gateway_name": { + "type": "str" + }, + "direction": { + "type": "str", + "required": True, + "choices": ["in", "out"], + }, + "interface": {"type": "str"}, + "protocol": {"type": "str"}, + }, + }, + "route_map": { + "type": "dict", + "options": { + "name": { + "type": "str", + "required": True, + } + }, + }, + }, + }, + "event_log": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "one_shot": {"type": "bool"}, + "pause": {"type": "bool"}, + "size": {"type": "int"}, + }, + }, + "graceful_restart": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + "strict_lsa_checking": { + "type": "bool" + }, + }, + }, + "interface_id": { + "type": "dict", + "options": { + "ios_if_index": {"type": "bool"}, + "snmp_if_index": {"type": "bool"}, + }, + }, + "limit": { + "type": "dict", + "options": { + "dc": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "disable": {"type": "bool"}, + }, + }, + "non_dc": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "disable": {"type": "bool"}, + }, + }, + }, + }, + "local_rib_criteria": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "forwarding_address": {"type": "bool"}, + "inter_area_summary": {"type": "bool"}, + "nssa_translation": {"type": "bool"}, + }, + }, + "log_adjacency_changes": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "detail": {"type": "bool"}, + }, + }, + "manet": { + "type": "dict", + "options": { + "cache": { + "type": "dict", + "options": { + "acknowledgement": { + "type": "int" + }, + "update": {"type": "int"}, + }, + }, + "hello": { + "type": "dict", + "options": { + "multicast": {"type": "bool"}, + "unicast": {"type": "bool"}, + }, + }, + "peering": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "disable": {"type": "bool"}, + "per_interface": { + "type": "bool" + }, + "redundancy": {"type": "int"}, + }, + }, + "willingness": {"type": "int"}, + }, + }, + "max_lsa": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "threshold_value": {"type": "int"}, + "ignore_count": {"type": "int"}, + "ignore_time": {"type": "int"}, + "reset_time": {"type": "int"}, + "warning_only": {"type": "bool"}, + }, + }, + "max_metric": { + "type": "dict", + "options": { + "disable": {"type": "bool"}, + "external_lsa": {"type": "int"}, + "inter_area_lsas": {"type": "int"}, + "on_startup": { + "type": "dict", + "options": { + "time": {"type": "int"}, + "wait_for_bgp": { + "type": "bool" + }, + }, + }, + "stub_prefix_lsa": {"type": "bool"}, + }, + }, + "maximum_paths": {"type": "int"}, + "passive_interface": {"type": "str"}, + "prefix_suppression": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + "queue_depth": { + "type": "dict", + "options": { + "hello": { + "type": "dict", + "options": { + "max_packets": {"type": "int"}, + "unlimited": {"type": "bool"}, + }, + }, + "update": { + "type": "dict", + "options": { + "max_packets": {"type": "int"}, + "unlimited": {"type": "bool"}, + }, + }, + }, + }, + "router_id": {"type": "str"}, + "shutdown": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + "summary_prefix": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "mask": {"type": "str"}, + "not_advertise": {"type": "bool"}, + "nssa_only": {"type": "bool"}, + "tag": {"type": "int"}, + }, + }, + "timers": { + "type": "dict", + "options": { + "lsa": {"type": "int"}, + "manet": { + "type": "dict", + "options": { + "cache": { + "type": "dict", + "options": { + "acknowledgement": { + "type": "int" + }, + "redundancy": { + "type": "int" + }, + }, + }, + "hello": {"type": "bool"}, + "peering": { + "type": "dict", + "options": { + "set": { + "type": "bool" + }, + "per_interface": { + "type": "bool" + }, + "redundancy": { + "type": "int" + }, + }, + }, + "willingness": {"type": "int"}, + }, + }, + "pacing": { + "type": "dict", + "options": { + "flood": {"type": "int"}, + "lsa_group": {"type": "int"}, + "retransmission": { + "type": "int" + }, + }, + }, + "throttle": { + "type": "dict", + "options": { + "lsa": { + "type": "dict", + "options": { + "first_delay": { + "type": "int" + }, + "min_delay": { + "type": "int" + }, + "max_delay": { + "type": "int" + }, + }, + }, + "spf": { + "type": "dict", + "options": { + "receive_delay": { + "type": "int" + }, + "between_delay": { + "type": "int" + }, + "max_delay": { + "type": "int" + }, + }, + }, + }, + }, + }, + }, + }, + }, + "adjacency": { + "type": "dict", + "options": { + "min_adjacency": {"type": "int"}, + "max_adjacency": {"type": "int"}, + "none": {"type": "bool"}, + }, + }, + "areas": { + "type": "list", + "elements": "dict", + "options": { + "area_id": {"type": "str"}, + "authentication": { + "type": "dict", + "options": { + "key_chain": {"type": "str"}, + "ipsec": { + "type": "dict", + "options": { + "spi": {"type": "int"}, + "md5": {"type": "int"}, + "sha1": {"type": "int"}, + "hex_string": {"type": "str"}, + }, + }, + }, + }, + "default_cost": {"type": "int"}, + "nssa": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "default_information_originate": { + "type": "dict", + "options": { + "metric": {"type": "int"}, + "metric_type": { + "type": "int", + "choices": [1, 2], + }, + "nssa_only": {"type": "bool"}, + }, + }, + "no_redistribution": {"type": "bool"}, + "no_summary": {"type": "bool"}, + "translate": { + "type": "str", + "choices": [ + "always", + "suppress-fa", + ], + }, + }, + }, + "stub": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "no_summary": {"type": "bool"}, + }, + }, + }, + }, + "authentication": {"type": "bool"}, + "auto_cost": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "reference_bandwidth": {"type": "int"}, + }, + }, + "bfd": {"type": "bool"}, + "compatible": { + "type": "dict", + "options": { + "rfc1583": {"type": "bool"}, + "rfc1587": {"type": "bool"}, + "rfc5243": {"type": "bool"}, + }, + }, + "event_log": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "one_shot": {"type": "bool"}, + "pause": {"type": "bool"}, + "size": {"type": "int"}, + }, + }, + "graceful_restart": { + "type": "dict", + "options": { + "disable": {"type": "bool"}, + "strict_lsa_checking": {"type": "bool"}, + }, + }, + "help": {"type": "bool"}, + "interface_id": {"type": "bool"}, + "limit": { + "type": "dict", + "options": { + "dc": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "disable": {"type": "bool"}, + }, + }, + "non_dc": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "disable": {"type": "bool"}, + }, + }, + }, + }, + "local_rib_criteria": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "forwarding_address": {"type": "bool"}, + "inter_area_summary": {"type": "bool"}, + "nssa_translation": {"type": "bool"}, + }, + }, + "log_adjacency_changes": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "detail": {"type": "bool"}, + }, + }, + "manet": { + "type": "dict", + "options": { + "cache": { + "type": "dict", + "options": { + "acknowledgement": {"type": "int"}, + "redundancy": {"type": "int"}, + }, + }, + "hello": {"type": "bool"}, + "peering": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "per_interface": {"type": "bool"}, + "redundancy": {"type": "int"}, + }, + }, + "willingness": {"type": "int"}, + }, + }, + "max_lsa": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "threshold_value": {"type": "int"}, + "ignore_count": {"type": "int"}, + "ignore_time": {"type": "int"}, + "reset_time": {"type": "int"}, + "warning_only": {"type": "bool"}, + }, + }, + "max_metric": { + "type": "dict", + "options": { + "router_lsa": { + "type": "bool", + "required": True, + }, + "external_lsa": {"type": "int"}, + "include_stub": {"type": "bool"}, + "on_startup": { + "type": "dict", + "options": { + "time": {"type": "int"}, + "wait_for_bgp": {"type": "bool"}, + }, + }, + "summary_lsa": {"type": "int"}, + }, + }, + "passive_interface": {"type": "str"}, + "prefix_suppression": {"type": "bool"}, + "queue_depth": { + "type": "dict", + "options": { + "hello": { + "type": "dict", + "options": { + "max_packets": {"type": "int"}, + "unlimited": {"type": "bool"}, + }, + } + }, + }, + "router_id": {"type": "str"}, + "shutdown": {"type": "bool"}, + "timers": { + "type": "dict", + "options": { + "lsa": {"type": "int"}, + "manet": { + "type": "dict", + "options": { + "cache": { + "type": "dict", + "options": { + "acknowledgement": { + "type": "int" + }, + "redundancy": {"type": "int"}, + }, + }, + "hello": {"type": "bool"}, + "peering": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "per_interface": { + "type": "bool" + }, + "redundancy": {"type": "int"}, + }, + }, + "willingness": {"type": "int"}, + }, + }, + "pacing": { + "type": "dict", + "options": { + "flood": {"type": "int"}, + "lsa_group": {"type": "int"}, + "retransmission": {"type": "int"}, + }, + }, + "throttle": { + "type": "dict", + "options": { + "lsa": { + "type": "dict", + "options": { + "first_delay": {"type": "int"}, + "min_delay": {"type": "int"}, + "max_delay": {"type": "int"}, + }, + }, + "spf": { + "type": "dict", + "options": { + "receive_delay": { + "type": "int" + }, + "between_delay": { + "type": "int" + }, + "max_delay": {"type": "int"}, + }, + }, + }, + }, + }, + }, + }, + } + }, + }, + "running_config": {"type": "str"}, + "state": { + "type": "str", + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + }, + } # pylint: disable=C0301 diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/static_routes/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/static_routes/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/static_routes/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/static_routes/static_routes.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/static_routes/static_routes.py new file mode 100644 index 00000000..e7dc942f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/static_routes/static_routes.py @@ -0,0 +1,100 @@ +# +# -*- 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 ios_static_routes module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Static_RoutesArgs(object): + """The arg spec for the ios_static_routes module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "vrf": {"type": "str"}, + "address_families": { + "elements": "dict", + "type": "list", + "options": { + "afi": { + "required": True, + "choices": ["ipv4", "ipv6"], + "type": "str", + }, + "routes": { + "elements": "dict", + "type": "list", + "options": { + "dest": {"required": True, "type": "str"}, + "topology": {"type": "str"}, + "next_hops": { + "elements": "dict", + "type": "list", + "options": { + "forward_router_address": { + "type": "str" + }, + "interface": {"type": "str"}, + "dhcp": {"type": "bool"}, + "distance_metric": {"type": "int"}, + "global": {"type": "bool"}, + "name": {"type": "str"}, + "multicast": {"type": "bool"}, + "permanent": {"type": "bool"}, + "tag": {"type": "int"}, + "track": {"type": "int"}, + }, + }, + }, + }, + }, + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/vlans.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/vlans.py new file mode 100644 index 00000000..29528962 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/vlans.py @@ -0,0 +1,71 @@ +# +# -*- 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 ios_vlans module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class VlansArgs(object): + """The arg spec for the ios_vlans module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "name": {"type": "str"}, + "vlan_id": {"required": True, "type": "int"}, + "mtu": {"type": "int"}, + "remote_span": {"type": "bool"}, + "state": {"type": "str", "choices": ["active", "suspend"]}, + "shutdown": { + "type": "str", + "choices": ["enabled", "disabled"], + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "parsed", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acl_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acl_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acl_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acl_interfaces/acl_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acl_interfaces/acl_interfaces.py new file mode 100644 index 00000000..a650b295 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acl_interfaces/acl_interfaces.py @@ -0,0 +1,415 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_acl_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 ( + to_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible.module_utils.six import iteritems +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + remove_duplicate_interface, + normalize_interface, +) + + +class Acl_Interfaces(ConfigBase): + """ + The ios_acl_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["acl_interfaces"] + + def __init__(self, module): + super(Acl_Interfaces, self).__init__(module) + + def get_acl_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 + ) + acl_interfaces_facts = facts["ansible_network_resources"].get( + "acl_interfaces" + ) + if not acl_interfaces_facts: + return [] + + return acl_interfaces_facts + + def execute_module(self): + """ Execute the module + :rtype: A dictionary + :returns: The result from moduel execution + """ + result = {"changed": False} + commands = list() + warnings = list() + + if self.state in self.ACTION_STATES: + existing_acl_interfaces_facts = self.get_acl_interfaces_facts() + else: + existing_acl_interfaces_facts = [] + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_acl_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_acl_interfaces_facts = self.get_acl_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_acl_interfaces_facts( + data=running_config + ) + else: + changed_acl_interfaces_facts = [] + + if self.state in self.ACTION_STATES: + result["before"] = existing_acl_interfaces_facts + if result["changed"]: + result["after"] = changed_acl_interfaces_facts + elif self.state == "gathered": + result["gathered"] = changed_acl_interfaces_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_acl_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 deisred configuration + """ + want = self._module.params["config"] + if want: + for item in want: + item["name"] = normalize_interface(item["name"]) + + have = existing_acl_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 deisred configuration + """ + commands = [] + + state = self._module.params["state"] + if ( + state in ("overridden", "merged", "replaced", "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 = self._state_overridden(want, have) + elif state == "deleted": + commands = self._state_deleted(want, have) + elif state == "merged" or state == "rendered": + commands = self._state_merged(want, have) + elif state == "replaced": + commands = self._state_replaced(want, have) + + return commands + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + :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 deisred configuration + """ + commands = [] + + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + continue + commands.extend(self._clear_config(interface, each, "replaced")) + commands.extend(self._set_config(interface, each)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + :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 = [] + + for each in have: + for interface in want: + if each["name"] == interface["name"]: + break + else: + # We didn't find a matching desired state, which means we can + # pretend we recieved an empty desired state. + interface = dict(name=each["name"]) + commands.extend(self._clear_config(interface, each)) + continue + commands.extend(self._clear_config(interface, each, "overridden")) + commands.extend(self._set_config(interface, each)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + :param want: the additive configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + # configuring non-existing interface + commands.extend(self._set_config(interface, dict())) + continue + commands.extend(self._set_config(interface, each)) + + return commands + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + :param want: the objects from which the configuration should be removed + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + + if want: + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + continue + commands.extend(self._clear_config(interface, each)) + else: + for each in have: + commands.extend(self._clear_config(dict(), each)) + + return commands + + def dict_to_set(self, input_dict, test_set, final_set, count=0): + # recursive function to convert input dict to set for comparision + test_dict = dict() + if isinstance(input_dict, dict): + input_dict_len = len(input_dict) + for k, v in sorted(iteritems(input_dict)): + count += 1 + if isinstance(v, list): + for each in v: + if isinstance(each, dict): + input_dict_len = len(each) + if [ + True for i in each.values() if type(i) == list + ]: + self.dict_to_set(each, set(), final_set, count) + else: + self.dict_to_set(each, test_set, final_set, 0) + else: + if v is not None: + test_dict.update({k: v}) + if ( + tuple(iteritems(test_dict)) not in test_set + and count == input_dict_len + ): + test_set.add(tuple(iteritems(test_dict))) + count = 0 + if count == input_dict_len + 1: + test_set.update(tuple(iteritems(test_dict))) + final_set.add(tuple(test_set)) + + def _set_config(self, want, have): + """ Function that sets the acls config based on the want and have config + :param want: want config + :param have: have config + :param acl_want: want acl config + :param afi: acl afi type + :rtype: A list + :returns: the commands generated based on input want/have params + """ + commands = [] + + want_set = set() + have_set = set() + self.dict_to_set(want, set(), want_set) + self.dict_to_set(have, set(), have_set) + + for w in want_set: + want_afi = dict(w).get("afi") + if have_set: + + def common_diff_config_code(diff_list, cmd, commands): + for each in diff_list: + try: + temp = dict(each) + temp_cmd = cmd + " {0} {1}".format( + temp["name"], temp["direction"] + ) + if temp_cmd not in commands: + commands.append(temp_cmd) + except ValueError: + continue + + for h in have_set: + have_afi = dict(h).get("afi") + if have_afi == want_afi: + if want_afi == "ipv4": + diff = set(w) - set(h) + if diff: + cmd = "ip access-group" + common_diff_config_code(diff, cmd, commands) + if want_afi == "ipv6": + diff = set(w) - set(h) + if diff: + cmd = "ipv6 traffic-filter" + common_diff_config_code(diff, cmd, commands) + break + else: + if want_afi == "ipv4": + diff = set(w) - set(h) + if diff: + cmd = "ip access-group" + common_diff_config_code(diff, cmd, commands) + if want_afi == "ipv6": + diff = set(w) - set(h) + if diff: + cmd = "ipv6 traffic-filter" + common_diff_config_code(diff, cmd, commands) + else: + + def common_want_config_code(want, cmd, commands): + for each in want: + if each[0] == "afi": + continue + temp = dict(each) + temp_cmd = cmd + " {0} {1}".format( + temp["name"], temp["direction"] + ) + commands.append(temp_cmd) + + if want_afi == "ipv4": + cmd = "ip access-group" + common_want_config_code(w, cmd, commands) + if want_afi == "ipv6": + cmd = "ipv6 traffic-filter" + common_want_config_code(w, cmd, commands) + commands.sort() + if commands: + interface = want.get("name") + commands.insert(0, "interface {0}".format(interface)) + + return commands + + def _clear_config(self, want, have, state=""): + """ Function that deletes the acl config based on the want and have config + :param acl: acl config + :param config: config + :rtype: A list + :returns: the commands generated based on input acl/config params + """ + commands = [] + + if want.get("name"): + interface = "interface " + want["name"] + else: + interface = "interface " + have["name"] + + w_access_group = want.get("access_groups") + temp_want_acl_name = [] + if w_access_group: + # get the user input afi and acls + for each in w_access_group: + want_acls = each.get("acls") + if want_acls: + for each in want_acls: + temp_want_acl_name.append(each.get("name")) + + h_access_group = have.get("access_groups") + if h_access_group: + for access_grp in h_access_group: + for acl in access_grp.get("acls"): + acl_name = acl.get("name") + acl_direction = acl.get("direction") + if access_grp.get("afi") == "ipv4": + if acl_name in temp_want_acl_name: + continue + cmd = "no ip access-group" + cmd += " {0} {1}".format(acl_name, acl_direction) + commands.append(cmd) + elif access_grp.get("afi") == "ipv6": + if acl_name in temp_want_acl_name: + continue + cmd = "no ipv6 traffic-filter" + cmd += " {0} {1}".format(acl_name, acl_direction) + commands.append(cmd) + if commands: + # inserting the interface at first + commands.insert(0, interface) + + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/acls.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/acls.py new file mode 100644 index 00000000..b5f7e1dc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/acls.py @@ -0,0 +1,301 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_acls 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 + +import copy + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.acls import ( + AclsTemplate, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import ( + ResourceModule, +) + + +class Acls(ResourceModule): + """ + The ios_acls class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["acls"] + + def __init__(self, module): + super(Acls, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="acls", + tmplt=AclsTemplate(), + ) + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from moduel execution + """ + self.gen_config() + self.run_commands() + return self.result + + def gen_config(self): + """ Select the appropriate function based on the state provided + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + + if self.want: + wantd = self.want + else: + wantd = {} + if self.have: + haved = self.have + else: + haved = {} + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + temp_have = copy.deepcopy(haved) + temp = [] + temp_acls = {} + + for each in wantd: + want_in_have = False + for each_have in temp_have: + if each.get("afi") == each_have.get("afi"): + temp_acls["acls"] = [] + for each_acls in each.get("acls"): + want_acls_in_have = False + for each_have_acls in each_have.get("acls"): + if each_acls["name"] == each_have_acls["name"]: + aces = [] + for each_ace in each_acls["aces"]: + each_ace_sequence = each_ace.get( + "sequence" + ) + if each_ace_sequence: + for ( + each_have_ace + ) in each_have_acls["aces"]: + if ( + each_ace_sequence + == each_have_ace.get( + "sequence" + ) + ): + aces.append( + dict( + dict_merge( + each_have_ace, + each_ace, + ) + ) + ) + break + if aces: + temp_acls["acls"].append( + { + "aces": aces, + "name": each_acls["name"], + } + ) + else: + temp_acls["acls"].append( + dict( + dict_merge( + each_have_acls, each_acls + ) + ) + ) + want_acls_in_have = True + if not want_acls_in_have: + temp_acls["acls"].append(each_acls) + temp_acls.update({"afi": each.get("afi")}) + # temp.append(dict(dict_merge(each, each_have))) + temp.append(temp_acls) + want_in_have = True + if not want_in_have: + temp.append(each) + if temp: + wantd = temp + + # if state is deleted, empty out wantd and set haved to wantd + if self.state == "deleted": + if wantd: + for each_have in haved: + count = 0 + for every_have in each_have.get("acls"): + del_want = False + for each_want in wantd: + want_acls = each_want.get("acls") + want_afi = each_want.get("afi") + if want_acls: + for every_want in each_want.get("acls"): + if every_want.get( + "name" + ) == every_have.get("name"): + del_want = True + break + elif want_afi and want_afi == each_have["afi"]: + del_want = True + if not del_want: + del each_have.get("acls")[count] + count += 1 + wantd = {} + for each in haved: + for every_acls in each.get("acls"): + every_acls.update({"afi": each.get("afi")}) + self.addcmd(every_acls, "acls_name", True) + + # remove superfluous config for overridden and deleted + if self.state in ["overridden"] and wantd: + for each_have in haved: + count = 0 + for every_have in each_have.get("acls"): + del_want = False + for each_want in wantd: + for every_want in each_want.get("acls"): + if every_want.get("name") == every_have.get( + "name" + ): + del_want = True + break + if not del_want: + every_have.update({"afi": each_have.get("afi")}) + self.addcmd(every_have, "acls_name", True) + count += 1 + + for w in wantd: + want_in_have = False + if haved: + for h in haved: + if w["afi"] == h["afi"]: + want_in_have = True + for e_w in w["acls"]: + set_want = True + for e_h in h["acls"]: + if e_w["name"] == e_h["name"]: + e_w.update({"afi": w.get("afi")}) + e_h.update({"afi": h.get("afi")}) + self._compare(want=e_w, have=e_h) + set_want = False + break + if set_want: + e_w.update({"afi": w.get("afi")}) + self._compare(want=e_w, have={}) + if not haved or not want_in_have: + for e_w in w["acls"]: + e_w.update({"afi": w["afi"]}) + self._compare(want=e_w, have={}) + + 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. + """ + parsers = ["aces"] + + if want.get("aces"): + cmd_added = True + for each in want.get("aces"): + set_want = True + if have.get("aces"): + for each_have in have.get("aces"): + if each.get("source") == each_have.get( + "source" + ) and each.get("destination") == each_have.get( + "destination" + ): + set_want = False + if each.get("sequence") and not each_have.get( + "sequence" + ): + each_have.update( + {"sequence": each.get("sequence")} + ) + elif not each.get("sequence") and each_have.get( + "sequence" + ): + each.update( + {"sequence": each_have.get("sequence")} + ) + if each.get("protocol") and not each_have.get( + "protocol" + ): + each_have.update( + {"protocol": each.get("protocol")} + ) + elif not each.get("protocol") and each_have.get( + "protocol" + ): + each.update( + {"protocol": each_have.get("protocol")} + ) + if each != each_have: + if cmd_added: + self.addcmd(have, "acls_name", False) + cmd_added = False + self.compare( + parsers=parsers, + want={"aces": each, "afi": want["afi"]}, + have={ + "aces": each_have, + "afi": have["afi"], + }, + ) + elif each.get("sequence") == each_have.get("sequence"): + if cmd_added: + self.addcmd(have, "acls_name", False) + cmd_added = False + self.compare( + parsers=parsers, + want={}, + have={"aces": each_have, "afi": have["afi"]}, + ) + self.compare( + parsers=parsers, + want={"aces": each, "afi": want["afi"]}, + have={}, + ) + set_want = False + else: + if cmd_added: + self.addcmd(want, "acls_name", False) + cmd_added = False + self.compare( + parsers=parsers, + want={"aces": each, "afi": want["afi"]}, + have=dict(), + ) + set_want = False + if set_want: + if cmd_added: + self.addcmd(want, "acls_name", False) + cmd_added = False + self.compare( + parsers=parsers, + want={"aces": each, "afi": want["afi"]}, + have=dict(), + ) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py new file mode 100644 index 00000000..6a20e23b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py @@ -0,0 +1,452 @@ +# +# -*- 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 cisco.ios_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.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import ( + ResourceModule, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.bgp_global import ( + Bgp_globalTemplate, +) + + +class Bgp_global(ResourceModule): + """ + The cisco.ios_bgp_global config class + """ + + parsers = [ + "as_number", + "bgp.additional_paths", + "bgp.dampening", + "bgp.graceful_shutdown", + "route_server_context", + "synchronization", + "table_map", + "timers", + ] + + def __init__(self, module): + super(Bgp_global, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="bgp_global", + tmplt=Bgp_globalTemplate(), + ) + + 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.want["as_number"]: self.want} + else: + wantd = {} + if self.have: + haved = {self.have["as_number"]: self.have} + else: + haved = {} + + wantd, haved = self.list_to_dict(wantd, 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" and self.have: + if ( + not self.want + or self.have.get("as_number") == self.want.get("as_number") + and len(self.have) > 1 + ): + self.addcmd( + {"as_number": haved[list(haved)[0]].pop("as_number")}, + "as_number", + False, + ) + self.compare( + parsers=self.parsers, want={}, have=haved[list(haved)[0]] + ) + self._compare(want={}, have=haved[list(haved)[0]]) + self._list_params_compare(want={}, have=haved[list(haved)[0]]) + wantd = {} + + if self.state == "purged" and self.have: + if ( + not self.want + or (self.have.get("as_number") == self.want.get("as_number")) + and len(self.have) >= 1 + ): + self.addcmd( + {"as_number": haved[list(haved)[0]].pop("as_number")}, + "as_number", + True, + ) + wantd = {} + + for k, want in iteritems(wantd): + 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 Ospf_interfaces network resource. + """ + if want != have and self.state != "deleted": + self.addcmd(have, "as_number", False) + self.compare(parsers=self.parsers, want=want, have=have) + self._bgp_config_compare(want.get("bgp"), have.get("bgp")) + self._list_params_compare(want, have) + elif self.state == "deleted": + self._bgp_config_compare(dict(), have.get("bgp")) + + def _bgp_config_compare(self, want, have): + if want and have and want != have and isinstance(want, dict): + set_have = True + for k, val in iteritems(want): + if isinstance(val, dict): + if k in have: + self.compare( + parsers=["bgp.config"], + want={"bgp": {k: val}}, + have={"bgp": {k: have[k]}}, + ) + if k in have and self.state == "replaced": + if set_have: + self.compare( + parsers=["bgp.config"], + want=dict(), + have={"bgp": {k: have[k]}}, + ) + self.compare( + parsers=["bgp.config"], + want={"bgp": {k: val}}, + have={"bgp": {k: have[k]}}, + ) + else: + self.compare( + parsers=["bgp.config"], + want={"bgp": {k: val}}, + have=dict(), + ) + elif want and not have: + for k, val in iteritems(want): + if not isinstance(val, list): + self.compare( + parsers=["bgp.config"], + want={"bgp": {k: val}}, + have=dict(), + ) + elif not want and have: + for k, val in iteritems(have): + if not isinstance(val, list): + self.compare( + parsers=["bgp.config"], + want=dict(), + have={"bgp": {k: val}}, + ) + + def _list_params_compare(self, want, have): + def multi_compare(parser, want, have): + if want: + dict_iter = want + else: + dict_iter = have + for k, v in iteritems(dict_iter): + if parser == "neighbor": + type = None + if want.get("address") or have.get("address"): + type = "address" + want_type_val = want.get("address") + have_type_val = have.get("address") + if want.get("tag") or have.get("tag"): + type = "tag" + want_type_val = want.get("tag") + have_type_val = have.get("tag") + if want.get("ipv6_adddress") or have.get("ipv6_address"): + type = "ipv6_adddress" + want_type_val = want.get("ipv6_adddress") + have_type_val = have.get("ipv6_adddress") + if want and have: + self.compare( + parsers=[parser], + want={parser: {k: v, type: want_type_val}}, + have={ + parser: { + k: have.get(k, {}), + type: have_type_val, + } + }, + ) + elif not have: + self.compare( + parsers=[parser], + want={parser: {k: v, type: want_type_val}}, + have=dict(), + ) + elif not want: + self.compare( + parsers=[parser], + want=dict(), + have={parser: {k: v, type: have_type_val}}, + ) + if parser == "redistribute": + if want and have: + self.compare( + parsers=[parser], + want={parser: {k: v}}, + have={parser: {k: have.get(k, {})}}, + ) + elif not have: + self.compare( + parsers=[parser], + want={parser: {k: v}}, + have=dict(), + ) + elif not want: + self.compare( + parsers=[parser], + want=dict(), + have={parser: {k: v}}, + ) + + for every in ["bgp", "neighbor", "redistribute"]: + + param_want = want.get(every) + param_have = have.get(every) + if param_want and param_want != param_have: + set_have = True + if every == "bgp": + for each in ["bestpath", "nopeerup_delay"]: + set_have = True + for k, v in iteritems(param_want.get(each)): + if ( + param_have + and k in param_have.get(each) + and self.state == "merged" + ): + if k in param_have.get(each): + self.compare( + parsers=[every + "." + each], + want={"bgp": {each: {k: v}}}, + have={ + "bgp": { + each: { + k: param_have.get( + each, {} + )[k] + } + } + }, + ) + + elif param_have and self.state == "replaced": + if set_have and param_have.get(each): + if isinstance(each, dict): + for key_have, val_have in iteritems( + param_have.get(each) + ): + multi_compare( + parser=every, + want=dict(), + have=val_have, + ) + else: + # q(param_have, param_want) + temp_have = { + each: {i: param_have[each][i]} + for i in list(param_have[each]) + if i not in param_want[each] + } + temp_want = { + each: {i: param_want[each][i]} + for i in list(param_want[each]) + if i not in param_have[each] + } + # q(temp_have) + if temp_have: + self.compare( + parsers=[every + "." + each], + want=dict(), + have={"bgp": temp_have}, + ) + if temp_want: + self.compare( + parsers=[every + "." + each], + want={"bgp": temp_want}, + have=dict(), + ) + set_have = False + else: + self.compare( + parsers=[every + "." + each], + want={"bgp": {each: {k: v}}}, + have=dict(), + ) + if every == "neighbor" or every == "redistribute": + for k, v in iteritems(param_want): + if every == "neighbor": + if param_have and self.state == "merged": + multi_compare( + parser=every, + want=v, + have=param_have.pop(k, {}), + ) + elif param_have and self.state == "replaced": + if set_have: + for key_have, val_have in iteritems( + param_have + ): + multi_compare( + parser=every, + want=dict(), + have=val_have, + ) + set_have = False + multi_compare( + parser=every, want=v, have=dict() + ) + else: + multi_compare( + parser=every, want=v, have=dict() + ) + self.commands = ( + [ + each + for each in self.commands + if "neighbor" not in each + ] + + [ + each + for each in self.commands + if "remote-as" in each + and "neighbor" in each + ] + + [ + each + for each in self.commands + if "remote-as" not in each + and "neighbor" in each + ] + ) + elif every == "redistribute": + if param_have and self.state == "merged": + multi_compare( + parser=every, + want={k: v}, + have={k: param_have.pop(k, {})}, + ) + if param_have and self.state == "replaced": + if set_have: + for key_have, val_have in iteritems( + param_have + ): + multi_compare( + parser=every, + want=dict(), + have=val_have, + ) + set_have = False + multi_compare( + parser=every, want={k: v}, have=dict() + ) + else: + multi_compare( + parser=every, want={k: v}, have=dict() + ) + elif param_have and self.state == "deleted": + del_config_have = True + if param_have: + for k, v in iteritems(param_have): + if every == "bgp" and del_config_have: + for each in ["bestpath", "nopeerup_delay"]: + for k, v in iteritems(param_have.get(each)): + self.compare( + parsers=[every + "." + each], + want=dict(), + have={"bgp": {each: {k: v}}}, + ) + del_config_have = False + elif every == "neighbor": + multi_compare(parser=every, want=dict(), have=v) + elif every == "redistribute": + if param_have: + multi_compare( + parser=every, want=dict(), have={k: v} + ) + + def list_to_dict(self, wantd, haved): + for thing in wantd, haved: + if thing: + for key, val in iteritems(thing): + for every in ["bgp", "neighbor", "redistribute"]: + value = val.get(every) + if value: + if isinstance(value, dict): + for k, v in iteritems(val.get(every)): + if isinstance(v, list): + temp = dict() + temp[k] = {} + for each in v: + temp[k].update(each) + val[every][k] = temp[k] + elif isinstance(value, list): + temp = dict() + temp[every] = {} + for each in value: + if every == "neighbor": + if each.get("address"): + temp[every].update( + {each.get("address"): each} + ) + elif each.get("tag"): + temp[every].update( + {each.get("tag"): each} + ) + elif each.get("ipv6_adddress"): + temp[every].update( + { + each.get( + "ipv6_adddress" + ): each + } + ) + elif every == "redistribute": + temp[every].update(each) + val[every] = temp[every] + return wantd, haved diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/interfaces/interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/interfaces/interfaces.py new file mode 100644 index 00000000..a449f5f0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/interfaces/interfaces.py @@ -0,0 +1,353 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_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 ( + to_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + get_interface_type, + dict_to_set, + normalize_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + remove_command_from_config_list, + add_command_to_config_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + filter_dict_having_none_value, + remove_duplicate_interface, +) + + +class Interfaces(ConfigBase): + """ + The ios_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["interfaces"] + + params = ("description", "mtu", "speed", "duplex") + + 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 moduel 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 deisred configuration + """ + config = self._module.params.get("config") + want = [] + if config: + for each in config: + each.update({"name": normalize_interface(each["name"])}) + want.append(each) + 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 deisred configuration + """ + commands = [] + + if ( + self.state in ("overridden", "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 == "overridden": + commands = self._state_overridden(want, have) + elif self.state == "deleted": + commands = self._state_deleted(want, have) + elif self.state == "merged" or self.state == "rendered": + commands = self._state_merged(want, have) + elif self.state == "replaced": + commands = self._state_replaced(want, have) + + return commands + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param interface_type: interface type + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the deisred configuration + """ + commands = [] + + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + if interface["name"] in each["name"]: + break + else: + # configuring non-existing interface + commands.extend(self._set_config(interface, dict())) + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :param want: the desired configuration as a dictionary + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + for each in have: + for interface in want: + count = 0 + if each["name"] == interface["name"]: + break + if interface["name"] in each["name"]: + break + count += 1 + else: + # We didn't find a matching desired state, which means we can + # pretend we received an empty desired state. + interface = dict(name=each["name"]) + commands.extend(self._clear_config(interface, each)) + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each)) + # as the pre-existing interface are now configured by + # above set_config call, deleting the respective + # interface entry from the want list + del want[count] + + # Iterating through want list which now only have new interfaces to be + # configured + for each in want: + commands.extend(self._set_config(each, dict())) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :param want: the additive configuration as a dictionary + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + # configuring non-existing interface + commands.extend(self._set_config(interface, dict())) + continue + commands.extend(self._set_config(interface, each)) + + return commands + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :param want: the objects from which the configuration should be removed + :param obj_in_have: the current configuration as a dictionary + :param interface_type: interface type + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + + if want: + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + continue + interface = dict(name=interface["name"]) + commands.extend(self._clear_config(interface, each)) + else: + for each in have: + want = dict() + commands.extend(self._clear_config(want, each)) + + return commands + + def _set_config(self, want, have): + # Set the interface config based on the want and have config + commands = [] + interface = "interface " + want["name"] + + # Get the diff b/w want and have + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + diff = want_dict - have_dict + + if diff: + diff = dict(diff) + for item in self.params: + if diff.get(item): + cmd = item + " " + str(want.get(item)) + add_command_to_config_list(interface, cmd, commands) + if diff.get("enabled"): + add_command_to_config_list(interface, "no shutdown", commands) + elif diff.get("enabled") is False: + add_command_to_config_list(interface, "shutdown", commands) + + return commands + + def _clear_config(self, want, have): + # Delete the interface config based on the want and have config + commands = [] + + if want.get("name"): + interface_type = get_interface_type(want["name"]) + interface = "interface " + want["name"] + else: + interface_type = get_interface_type(have["name"]) + interface = "interface " + have["name"] + + if have.get("description") and want.get("description") != have.get( + "description" + ): + remove_command_from_config_list(interface, "description", commands) + if not have.get("enabled") and want.get("enabled") != have.get( + "enabled" + ): + # if enable is False set enable as True which is the default behavior + remove_command_from_config_list(interface, "shutdown", commands) + + if interface_type.lower() == "gigabitethernet": + if ( + have.get("speed") + and have.get("speed") != "auto" + and want.get("speed") != have.get("speed") + ): + remove_command_from_config_list(interface, "speed", commands) + if ( + have.get("duplex") + and have.get("duplex") != "auto" + and want.get("duplex") != have.get("duplex") + ): + remove_command_from_config_list(interface, "duplex", commands) + if have.get("mtu") and want.get("mtu") != have.get("mtu"): + remove_command_from_config_list(interface, "mtu", commands) + + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l2_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l2_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l2_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l2_interfaces/l2_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l2_interfaces/l2_interfaces.py new file mode 100644 index 00000000..b9451fe0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l2_interfaces/l2_interfaces.py @@ -0,0 +1,478 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_l2_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 ( + to_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + dict_to_set, + normalize_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + remove_command_from_config_list, + add_command_to_config_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + filter_dict_having_none_value, + remove_duplicate_interface, +) + + +class L2_Interfaces(ConfigBase): + """ + The ios_l2_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["l2_interfaces"] + + access_cmds = {"access_vlan": "switchport access vlan"} + voice_cmds = {"voice_vlan": "switchport voice vlan"} + trunk_cmds = { + "encapsulation": "switchport trunk encapsulation", + "pruning_vlans": "switchport trunk pruning vlan", + "pruning_vlans_add": "switchport trunk pruning vlan add", + "native_vlan": "switchport trunk native vlan", + "allowed_vlans": "switchport trunk allowed vlan", + "allowed_vlans_add": "switchport trunk allowed vlan add", + } + + def get_l2_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 + ) + l2_interfaces_facts = facts["ansible_network_resources"].get( + "l2_interfaces" + ) + if not l2_interfaces_facts: + return [] + + return l2_interfaces_facts + + def execute_module(self): + """ Execute the module + :rtype: A dictionary + :returns: The result from moduel execution + """ + result = {"changed": False} + commands = [] + warnings = [] + if self.state in self.ACTION_STATES: + existing_l2_interfaces_facts = self.get_l2_interfaces_facts() + else: + existing_l2_interfaces_facts = [] + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_l2_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_l2_interfaces_facts = self.get_l2_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_l2_interfaces_facts( + data=running_config + ) + else: + changed_l2_interfaces_facts = [] + + if self.state in self.ACTION_STATES: + result["before"] = existing_l2_interfaces_facts + if result["changed"]: + result["after"] = changed_l2_interfaces_facts + elif self.state == "gathered": + result["gathered"] = changed_l2_interfaces_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_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 deisred configuration + """ + + config = self._module.params.get("config") + want = [] + if config: + for each in config: + each.update({"name": normalize_interface(each["name"])}) + want.append(each) + have = existing_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 deisred configuration + """ + commands = [] + + if ( + self.state in ("overridden", "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 == "overridden": + commands = self._state_overridden(want, have, self._module) + elif self.state == "deleted": + commands = self._state_deleted(want, have) + elif self.state == "merged" or self.state == "rendered": + commands = self._state_merged(want, have, self._module) + elif self.state == "replaced": + commands = self._state_replaced(want, have, self._module) + + return commands + + def _state_replaced(self, want, have, module): + """ The command generator when state is replaced + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param interface_type: interface type + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the deisred configuration + """ + commands = [] + + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each, module)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_overridden(self, want, have, module): + """ The command generator when state is overridden + :param want: the desired configuration as a dictionary + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + for each in have: + for interface in want: + if each["name"] == interface["name"]: + break + else: + # We didn't find a matching desired state, which means we can + # pretend we received an empty desired state. + interface = dict(name=each["name"]) + kwargs = {"want": interface, "have": each} + commands.extend(self._clear_config(**kwargs)) + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each, module)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_merged(self, want, have, module): + """ The command generator when state is merged + :param want: the additive configuration as a dictionary + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + # configuring non-existing interface + commands.extend(self._set_config(interface, dict(), module)) + continue + commands.extend(self._set_config(interface, each, module)) + + return commands + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + :param want: the objects from which the configuration should be removed + :param obj_in_have: the current configuration as a dictionary + :param interface_type: interface type + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + + if want: + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + continue + interface = dict(name=interface["name"]) + commands.extend(self._clear_config(interface, each)) + else: + for each in have: + want = dict() + commands.extend(self._clear_config(want, each)) + + return commands + + def _check_for_correct_vlan_range(self, vlan, module): + # Function to check if the VLAN range passed is Valid + for each in vlan: + vlan_range = each.split("-") + if len(vlan_range) > 1: + if vlan_range[0] < vlan_range[1]: + return True + else: + module.fail_json( + msg="Command rejected: Bad VLAN list - end of range not larger than the" + " start of range!" + ) + else: + return True + + def _set_config(self, want, have, module): + # Set the interface config based on the want and have config + commands = [] + interface = "interface " + want["name"] + + # Get the diff b/w want and have + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + want_trunk = dict(want_dict).get("trunk") + have_trunk = dict(have_dict).get("trunk") + if want_trunk and have_trunk: + diff = set(tuple(dict(want_dict).get("trunk"))) - set( + tuple(dict(have_dict).get("trunk")) + ) + else: + diff = want_dict - have_dict + + if diff: + diff = dict(diff) + mode = diff.get("mode") + access = diff.get("access") + trunk = diff.get("trunk") + + if access: + cmd = "switchport access vlan {0}".format(access[0][1]) + add_command_to_config_list(interface, cmd, commands) + + if diff.get("voice"): + cmd = "switchport voice vlan {0}".format( + diff.get("voice")[0][1] + ) + add_command_to_config_list(interface, cmd, commands) + + if want_trunk: + if trunk: + diff = dict(trunk) + if diff.get("encapsulation"): + cmd = self.trunk_cmds["encapsulation"] + " {0}".format( + diff.get("encapsulation") + ) + add_command_to_config_list(interface, cmd, commands) + if diff.get("native_vlan"): + cmd = self.trunk_cmds["native_vlan"] + " {0}".format( + diff.get("native_vlan") + ) + add_command_to_config_list(interface, cmd, commands) + allowed_vlans = diff.get("allowed_vlans") + pruning_vlans = diff.get("pruning_vlans") + + if allowed_vlans and self._check_for_correct_vlan_range( + allowed_vlans, module + ): + diff = None + if self.state == "merged": + have_trunk = have.get("trunk") + if have_trunk and have_trunk.get("allowed_vlans"): + have_allowed_vlans = have_trunk.get( + "allowed_vlans" + ) + allowed_vlans = list(allowed_vlans) + diff = set(allowed_vlans).difference( + set(have_allowed_vlans) + ) + allowed_vlans = list(diff) if diff else tuple() + allowed_vlans = ",".join(allowed_vlans) + if allowed_vlans: + trunk_cmd = ( + self.trunk_cmds["allowed_vlans_add"] + if self.state == "merged" and diff + else self.trunk_cmds["allowed_vlans"] + ) + cmd = trunk_cmd + " {0}".format(allowed_vlans) + add_command_to_config_list(interface, cmd, commands) + if pruning_vlans and self._check_for_correct_vlan_range( + pruning_vlans, module + ): + diff = None + if self.state == "merged": + have_trunk = have.get("trunk") + if have_trunk and have_trunk.get("pruning_vlans"): + have_pruning_vlans = have_trunk.get( + "pruning_vlans" + ) + pruning_vlans = list(pruning_vlans) + diff = set(pruning_vlans).difference( + set(have_pruning_vlans) + ) + pruning_vlans = list(diff) if diff else tuple() + pruning_vlans = ",".join(pruning_vlans) + if pruning_vlans: + trunk_cmd = ( + self.trunk_cmds["pruning_vlans_add"] + if self.state == "merged" and diff + else self.trunk_cmds["pruning_vlans"] + ) + cmd = trunk_cmd + " {0}".format(pruning_vlans) + add_command_to_config_list(interface, cmd, commands) + if mode: + cmd = "switchport mode {0}".format(mode) + add_command_to_config_list(interface, cmd, commands) + + return commands + + def _clear_config(self, want, have): + # Delete the interface config based on the want and have config + commands = [] + if want.get("name"): + interface = "interface " + want["name"] + else: + interface = "interface " + have["name"] + + if have.get("mode") or want.get("mode"): + remove_command_from_config_list( + interface, "switchport mode", commands + ) + + if have.get("access") and want.get("access") is None: + remove_command_from_config_list( + interface, L2_Interfaces.access_cmds["access_vlan"], commands + ) + elif have.get("access") and want.get("access"): + if have.get("access").get("vlan") != want.get("access").get( + "vlan" + ): + remove_command_from_config_list( + interface, + L2_Interfaces.access_cmds["access_vlan"], + commands, + ) + + if have.get("voice") and want.get("voice") is None: + remove_command_from_config_list( + interface, L2_Interfaces.voice_cmds["voice_vlan"], commands + ) + elif have.get("voice") and want.get("voice"): + if have.get("voice").get("vlan") != want.get("voice").get("vlan"): + remove_command_from_config_list( + interface, L2_Interfaces.voice_cmds["voice_vlan"], commands + ) + + if have.get("trunk") and want.get("trunk") is None: + # Check when no config is passed + if have.get("trunk").get("encapsulation"): + remove_command_from_config_list( + interface, self.trunk_cmds["encapsulation"], commands + ) + if have.get("trunk").get("native_vlan"): + remove_command_from_config_list( + interface, self.trunk_cmds["native_vlan"], commands + ) + if have.get("trunk").get("allowed_vlans"): + remove_command_from_config_list( + interface, self.trunk_cmds["allowed_vlans"], commands + ) + if have.get("trunk").get("pruning_vlans"): + remove_command_from_config_list( + interface, self.trunk_cmds["pruning_vlans"], commands + ) + elif have.get("trunk") and want.get("trunk"): + # Check when config is passed, also used in replaced and override state + if have.get("trunk").get("encapsulation") and have.get( + "trunk" + ).get("encapsulation") != want.get("trunk").get("encapsulation"): + remove_command_from_config_list( + interface, self.trunk_cmds["encapsulation"], commands + ) + if have.get("trunk").get("native_vlan") and have.get("trunk").get( + "native_vlan" + ) != want.get("trunk").get("native_vlan"): + remove_command_from_config_list( + interface, self.trunk_cmds["native_vlan"], commands + ) + if have.get("trunk").get("allowed_vlans") and have.get( + "trunk" + ).get("allowed_vlans") != want.get("trunk").get("allowed_vlans"): + remove_command_from_config_list( + interface, self.trunk_cmds["allowed_vlans"], commands + ) + if have.get("trunk").get("pruning_vlans") and have.get( + "trunk" + ).get("pruning_vlans") != want.get("trunk").get("pruning_vlans"): + remove_command_from_config_list( + interface, self.trunk_cmds["pruning_vlans"], commands + ) + + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/l3_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/l3_interfaces.py new file mode 100644 index 00000000..3d45588b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/l3_interfaces.py @@ -0,0 +1,435 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_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 ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + remove_empties, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + dict_to_set, + normalize_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + remove_command_from_config_list, + add_command_to_config_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + filter_dict_having_none_value, + remove_duplicate_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + validate_n_expand_ipv4, + validate_ipv6, +) + + +class L3_Interfaces(ConfigBase): + """ + The ios_l3_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["l3_interfaces"] + + 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} + commands = list() + warnings = list() + + if self.state in self.ACTION_STATES: + existing_l3_interfaces_facts = 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.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.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 + """ + config = self._module.params.get("config") + want = [] + if config: + for each in config: + each.update({"name": normalize_interface(each["name"])}) + want.append(each) + 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 = [] + + if ( + self.state in ("overridden", "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 == "overridden": + commands = self._state_overridden(want, have, self._module) + elif self.state == "deleted": + commands = self._state_deleted(want, have) + elif self.state in ("merged", "rendered"): + commands = self._state_merged(want, have, self._module) + elif self.state == "replaced": + commands = self._state_replaced(want, have, self._module) + + return commands + + def _state_replaced(self, want, have, module): + """ The command generator when state is replaced + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + if "." in interface["name"]: + commands.extend( + self._set_config(interface, dict(), module) + ) + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each, module)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_overridden(self, want, have, module): + """ 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 each in have: + for interface in want: + if each["name"] == interface["name"]: + break + else: + # We didn't find a matching desired state, which means we can + # pretend we received an empty desired state. + interface = dict(name=each["name"]) + kwargs = {"want": interface, "have": each} + commands.extend(self._clear_config(**kwargs)) + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each, module)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_merged(self, want, have, module): + """ The command generator when state is merged + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + if "." in interface["name"]: + commands.extend( + self._set_config(interface, dict(), module) + ) + if self.state == "rendered": + commands.extend( + self._set_config(interface, dict(), module) + ) + continue + commands.extend(self._set_config(interface, each, module)) + + 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 interface in want: + for each in have: + if each["name"] == interface["name"]: + break + if interface["name"] in each["name"]: + break + else: + continue + interface = dict(name=interface["name"]) + commands.extend(self._clear_config(interface, each)) + else: + for each in have: + want = dict() + commands.extend(self._clear_config(want, each)) + + return commands + + def verify_diff_again(self, want, have): + """ + Verify the IPV4 difference again as sometimes due to + change in order of set, set difference may result into change, + when there's actually no difference between want and have + :param want: want_dict IPV4 + :param have: have_dict IPV4 + :return: diff + """ + diff = False + for each in want: + each_want = remove_empties(dict(each)) + for every in have: + every_have = dict(every) + if ( + each_want.get("address") != every_have.get("address") + and each_want.get("secondary") + != every_have.get("secondary") + and len(each_want.keys()) == len(every_have.keys()) + ): + diff = True + break + if ( + each_want.get("dhcp_client") + != every_have.get("dhcp_client") + and each_want.get("dhcp_client") is not None + ): + diff = True + break + if ( + each_want.get("dhcp_hostname") + != every_have.get("dhcp_hostname") + and each_want.get("dhcp_hostname") is not None + ): + diff = True + break + if each_want.get("address") != every_have.get( + "address" + ) and len(each_want.keys()) == len(every_have.keys()): + diff = True + break + if diff: + break + + return diff + + def _set_config(self, want, have, module): + # Set the interface config based on the want and have config + commands = [] + interface = "interface " + want["name"] + + # To handle L3 IPV4 configuration + if want.get("ipv4"): + for each in want.get("ipv4"): + if each.get("address") != "dhcp": + ip_addr_want = validate_n_expand_ipv4(module, each) + each["address"] = ip_addr_want + + # Convert the want and have dict to set + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + + # To handle L3 IPV4 configuration + if want.get("ipv4"): + # Get the diff b/w want and have IPV4 + if have.get("ipv4"): + ipv4 = tuple( + set(dict(want_dict).get("ipv4")) + - set(dict(have_dict).get("ipv4")) + ) + if ipv4: + ipv4 = ( + ipv4 + if self.verify_diff_again( + dict(want_dict).get("ipv4"), + dict(have_dict).get("ipv4"), + ) + else () + ) + else: + diff = want_dict - have_dict + ipv4 = dict(diff).get("ipv4") + if ipv4: + for each in ipv4: + ipv4_dict = dict(each) + if ipv4_dict.get("address") != "dhcp": + cmd = "ip address {0}".format(ipv4_dict["address"]) + if ipv4_dict.get("secondary"): + cmd += " secondary" + elif ipv4_dict.get("address") == "dhcp": + cmd = "ip address dhcp" + if "/" in interface: + dhcp_interface = want["name"].split("/")[0] + "/" + elif "gigabitethernet" in interface.lower(): + dhcp_interface = "GigabitEthernet" + if ipv4_dict.get( + "dhcp_client" + ) is not None and ipv4_dict.get("dhcp_hostname"): + cmd = "ip address dhcp client-id {0}{1} hostname {2}".format( + dhcp_interface, + ipv4_dict.get("dhcp_client"), + ipv4_dict.get("dhcp_hostname"), + ) + elif ipv4_dict.get( + "dhcp_client" + ) and not ipv4_dict.get("dhcp_hostname"): + cmd = "ip address dhcp client-id {0}{1}".format( + dhcp_interface, ipv4_dict.get("dhcp_client") + ) + elif not ipv4_dict.get( + "dhcp_client" + ) and ipv4_dict.get("dhcp_hostname"): + cmd = "ip address dhcp hostname {0}".format( + ipv4_dict.get("dhcp_client") + ) + + add_command_to_config_list(interface, cmd, commands) + + # To handle L3 IPV6 configuration + if want.get("ipv6"): + # Get the diff b/w want and have IPV6 + if have.get("ipv6"): + ipv6 = tuple( + set(dict(want_dict).get("ipv6")) + - set(dict(have_dict).get("ipv6")) + ) + else: + diff = want_dict - have_dict + ipv6 = dict(diff).get("ipv6") + if ipv6: + for each in ipv6: + ipv6_dict = dict(each) + validate_ipv6(ipv6_dict.get("address"), module) + cmd = "ipv6 address {0}".format(ipv6_dict.get("address")) + add_command_to_config_list(interface, cmd, commands) + + return commands + + def _clear_config(self, want, have): + # Delete the interface config based on the want and have config + count = 0 + commands = [] + if want.get("name"): + interface = "interface " + want["name"] + else: + interface = "interface " + have["name"] + + if have.get("ipv4") and want.get("ipv4"): + for each in have.get("ipv4"): + if each.get("secondary") and not ( + want.get("ipv4")[count].get("secondary") + ): + cmd = "ipv4 address {0} secondary".format( + each.get("address") + ) + remove_command_from_config_list(interface, cmd, commands) + count += 1 + if have.get("ipv4") and not want.get("ipv4"): + remove_command_from_config_list(interface, "ip address", commands) + if have.get("ipv6") and not want.get("ipv6"): + remove_command_from_config_list( + interface, "ipv6 address", commands + ) + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp/lacp.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp/lacp.py new file mode 100644 index 00000000..6adab3bd --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp/lacp.py @@ -0,0 +1,224 @@ +# +# -*- 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 ios_lacp 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 ( + to_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + dict_to_set, +) + + +class Lacp(ConfigBase): + """ + The ios_lacp class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["lacp"] + + def __init__(self, module): + super(Lacp, self).__init__(module) + + def get_lacp_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 + ) + lacp_facts = facts["ansible_network_resources"].get("lacp") + if not lacp_facts: + return dict() + + return lacp_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_lacp_facts = self.get_lacp_facts() + else: + existing_lacp_facts = dict() + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_lacp_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_lacp_facts = self.get_lacp_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_lacp_facts(data=running_config) + else: + changed_lacp_facts = dict() + + if self.state in self.ACTION_STATES: + result["before"] = existing_lacp_facts + if result["changed"]: + result["after"] = changed_lacp_facts + elif self.state == "gathered": + result["gathered"] = changed_lacp_facts + result["warnings"] = warnings + + return result + + def set_config(self, existing_lacp_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_lacp_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 + """ + 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 = self._state_deleted(want, have) + elif self.state in ("merged", "rendered"): + commands = self._state_merged(want, have) + elif self.state == "replaced": + commands = self._state_replaced(want, 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 = [] + + commands.extend(self._set_config(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._set_config(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: + commands.extend(self._clear_config(have)) + else: + commands.extend(self._clear_config(have)) + + return commands + + def _remove_command_from_config_list(self, cmd, commands): + commands.append("no %s" % cmd) + return commands + + def _add_command_to_config_list(self, cmd, commands): + if cmd not in commands: + commands.append(cmd) + + def _set_config(self, want, have): + # Set the interface config based on the want and have config + commands = [] + + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + diff = want_dict - have_dict + + if diff: + cmd = "lacp system-priority {0}".format( + want.get("system").get("priority") + ) + self._add_command_to_config_list(cmd, commands) + + return commands + + def _clear_config(self, have): + # Delete the interface config based on the want and have config + commands = [] + + if ( + have.get("system").get("priority") + and have.get("system").get("priority") != 32768 + ): + cmd = "lacp system-priority" + self._remove_command_from_config_list(cmd, commands) + + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp_interfaces/lacp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 00000000..3d21b635 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,314 @@ +# +# -*- 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 ios_lacp_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 ( + to_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + dict_to_set, + normalize_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + remove_command_from_config_list, + add_command_to_config_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + filter_dict_having_none_value, + remove_duplicate_interface, +) + + +class Lacp_Interfaces(ConfigBase): + """ + The ios_lacp_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["lacp_interfaces"] + + def __init__(self, module): + super(Lacp_Interfaces, self).__init__(module) + + def get_lacp_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 + ) + lacp_interfaces_facts = facts["ansible_network_resources"].get( + "lacp_interfaces" + ) + + if not lacp_interfaces_facts: + return [] + return lacp_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_lacp_interfaces_facts = self.get_lacp_interfaces_facts() + else: + existing_lacp_interfaces_facts = [] + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_lacp_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_lacp_interfaces_facts = self.get_lacp_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_lacp_interfaces_facts( + data=running_config + ) + else: + changed_lacp_interfaces_facts = [] + + if self.state in self.ACTION_STATES: + result["before"] = existing_lacp_interfaces_facts + if result["changed"]: + result["after"] = changed_lacp_interfaces_facts + elif self.state == "gathered": + result["gathered"] = changed_lacp_interfaces_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_lacp_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 + """ + config = self._module.params.get("config") + want = [] + if config: + for each in config: + each.update({"name": normalize_interface(each["name"])}) + want.append(each) + have = existing_lacp_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 ("overridden", "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 == "overridden": + commands = self._state_overridden(want, have) + elif self.state == "deleted": + commands = self._state_deleted(want, have) + elif self.state in ("merged", "rendered"): + commands = self._state_merged(want, have) + elif self.state == "replaced": + commands = self._state_replaced(want, 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 = [] + + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + 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 each in have: + for interface in want: + if each["name"] == interface["name"]: + break + else: + # We didn't find a matching desired state, which means we can + # pretend we received an empty desired state. + interface = dict(name=each["name"]) + commands.extend(self._clear_config(interface, each)) + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + 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 interface in want: + for each in have: + if interface["name"] == each["name"]: + break + else: + commands.extend(self._set_config(interface, dict())) + continue + commands.extend(self._set_config(interface, each)) + + 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 interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + continue + interface = dict(name=interface["name"]) + commands.extend(self._clear_config(interface, each)) + else: + for each in have: + commands.extend(self._clear_config(dict(), each)) + + return commands + + def _set_config(self, want, have): + # Set the interface config based on the want and have config + commands = [] + interface = "interface " + want["name"] + + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + diff = want_dict - have_dict + + if diff: + port_priotity = dict(diff).get("port_priority") + max_bundle = dict(diff).get("max_bundle") + fast_switchover = dict(diff).get("fast_switchover") + if port_priotity: + cmd = "lacp port-priority {0}".format(port_priotity) + add_command_to_config_list(interface, cmd, commands) + if max_bundle: + cmd = "lacp max-bundle {0}".format(max_bundle) + add_command_to_config_list(interface, cmd, commands) + if fast_switchover: + cmd = "lacp fast-switchover" + add_command_to_config_list(interface, cmd, commands) + + return commands + + def _clear_config(self, want, have): + # Delete the interface config based on the want and have config + commands = [] + if want.get("name"): + interface = "interface " + want["name"] + else: + interface = "interface " + have["name"] + + if have.get("port_priority") and have.get("port_priority") != want.get( + "port_priority" + ): + cmd = "lacp port-priority" + remove_command_from_config_list(interface, cmd, commands) + if have.get("max_bundle") and have.get("max_bundle") != want.get( + "max_bundle" + ): + cmd = "lacp max-bundle" + remove_command_from_config_list(interface, cmd, commands) + if have.get("fast_switchover"): + cmd = "lacp fast-switchover" + remove_command_from_config_list(interface, cmd, commands) + + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lag_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lag_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lag_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lag_interfaces/lag_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lag_interfaces/lag_interfaces.py new file mode 100644 index 00000000..40482fc0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lag_interfaces/lag_interfaces.py @@ -0,0 +1,404 @@ +# +# -*- 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 ios_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 + +__metaclass__ = type + + +import re +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + dict_to_set, + normalize_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + remove_duplicate_interface, +) + + +class Lag_interfaces(ConfigBase): + """ + The ios_lag_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["lag_interfaces"] + + 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} + commands = list() + warnings = 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 + """ + config = self._module.params.get("config") + want = [] + if config: + for each in config: + each.update({"name": normalize_interface(each["name"])}) + want.append(each) + 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 + """ + + if ( + self.state in ("overridden", "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 + ) + ) + + module = self._module + if self.state == "overridden": + commands = self._state_overridden(want, have, module) + elif self.state == "deleted": + commands = self._state_deleted(want, have) + elif self.state in ("merged", "rendered"): + commands = self._state_merged(want, have, module) + elif self.state == "replaced": + commands = self._state_replaced(want, have, module) + return commands + + def _state_replaced(self, want, have, module): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + for interface in want: + for each_interface in interface.get("members"): + for each in have: + if each.get("members"): + for every in each.get("members"): + match = False + if every["member"] == each_interface["member"]: + match = True + break + continue + if match: + have_dict = self.filter_dict_having_none_value( + interface, each + ) + commands.extend( + self._clear_config(dict(), have_dict) + ) + commands.extend( + self._set_config(interface, each, module) + ) + elif each.get("name") == each_interface["member"]: + have_dict = self.filter_dict_having_none_value( + interface, each + ) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend( + self._set_config(interface, each, module) + ) + break + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_overridden(self, want, have, module): + """ 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 interface in want: + if interface.get("members"): + for each_interface in interface.get("members"): + for each in have: + if each.get("members"): + for every in each.get("members"): + match = False + if every["member"] == each_interface["member"]: + match = True + break + commands.extend( + self._clear_config(interface, each) + ) + continue + if match: + have_dict = self.filter_dict_having_none_value( + interface, each + ) + commands.extend( + self._clear_config(dict(), have_dict) + ) + commands.extend( + self._set_config(interface, each, module) + ) + elif each.get("name") == each_interface["member"]: + have_dict = self.filter_dict_having_none_value( + interface, each + ) + commands.extend( + self._clear_config(dict(), have_dict) + ) + commands.extend( + self._set_config(interface, each, module) + ) + break + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_merged(self, want, have, module): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + for interface in want: + for each_interface in interface.get("members"): + for each in have: + if each.get("members"): + for every in each.get("members"): + if every["member"] == each_interface["member"]: + break + elif each.get("name") == each_interface["member"]: + break + else: + if self.state == "rendered": + commands.extend( + self._set_config(interface, dict(), module) + ) + continue + commands.extend(self._set_config(interface, each, module)) + + 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 interface in want: + for each in have: + if each.get("name") == interface["name"]: + break + else: + continue + commands.extend(self._clear_config(interface, each)) + else: + for each in have: + commands.extend(self._clear_config(dict(), each)) + + return commands + + def filter_dict_having_none_value(self, want, have): + # Generate dict with have dict value which is None in want dict + test_dict = dict() + test_key_dict = dict() + test_dict["name"] = want.get("name") + for k, v in iteritems(want): + if isinstance(v, dict): + for key, value in iteritems(v): + if value is None: + dict_val = have.get(k).get(key) + test_key_dict.update({key: dict_val}) + test_dict.update({k: test_key_dict}) + if v is None: + val = have.get(k) + test_dict.update({k: val}) + return test_dict + + def remove_command_from_config_list(self, interface, cmd, commands): + # To delete the passed config + if interface not in commands: + commands.append(interface) + commands.append("no %s" % cmd) + return commands + + def add_command_to_config_list(self, interface, cmd, commands): + # To set the passed config + if interface not in commands: + commands.append(interface) + commands.append(cmd) + return commands + + def _set_config(self, want, have, module): + # Set the interface config based on the want and have config + commands = [] + + # To remove keys with None values from want dict + want = utils.remove_empties(want) + # Get the diff b/w want and have + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + diff = want_dict - have_dict + + # To get the channel-id from lag port-channel name + lag_config = dict(diff).get("members") + channel_name = re.search(r"(\d+)", want.get("name")) + if channel_name: + channel_id = channel_name.group() + else: + module.fail_json(msg="Lag Interface Name is not correct!") + if lag_config: + for each in lag_config: + each = dict(each) + each_interface = "interface {0}".format(each.get("member")) + if have.get("name") == want["members"][0][ + "member" + ] or want.get("name").lower().startswith("po"): + if each.get("mode"): + cmd = "channel-group {0} mode {1}".format( + channel_id, each.get("mode") + ) + self.add_command_to_config_list( + each_interface, cmd, commands + ) + elif each.get("link"): + cmd = "channel-group {0} link {1}".format( + channel_id, each.get("link") + ) + self.add_command_to_config_list( + each_interface, cmd, commands + ) + + return commands + + def _clear_config(self, want, have): + # Delete the interface config based on the want and have config + commands = [] + + if have.get("members"): + for each in have["members"]: + interface = "interface " + each["member"] + if want.get("members"): + if ( + each.get("member") + and each.get("member") != want["members"][0]["member"] + ): + self.remove_command_from_config_list( + interface, "channel-group", commands + ) + elif each.get("member"): + self.remove_command_from_config_list( + interface, "channel-group", commands + ) + + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_global/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_global/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_global/lldp_global.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_global/lldp_global.py new file mode 100644 index 00000000..b4e6a322 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_global/lldp_global.py @@ -0,0 +1,275 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_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 its desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + dict_to_set, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + filter_dict_having_none_value, +) + + +class Lldp_global(ConfigBase): + """ + The ios_lldp_global class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["lldp_global"] + + tlv_select_params = { + "four_wire_power_management": "4-wire-power-management", + "mac_phy_cfg": "mac-phy-cfg", + "management_address": "management-address", + "port_description": "port-description", + "port_vlan": "port-vlan", + "power_management": "power-management", + "system_capabilities": "system-capabilities", + "system_description": "system-description", + "system_name": "system-name", + } + + 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 moduel execution + """ + result = {"changed": False} + commands = list() + warnings = list() + + if self.state in self.ACTION_STATES: + existing_lldp_global_facts = self.get_lldp_global_facts() + else: + existing_lldp_global_facts = dict() + + 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 = dict() + + 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 deisred 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 deisred 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 == "overridden": + commands = self._state_overridden(want, have) + elif self.state == "deleted": + commands = self._state_deleted(want, have) + elif self.state in ("merged", "rendered"): + commands = self._state_merged(want, have) + elif self.state == "replaced": + commands = self._state_replaced(want, have) + + return commands + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :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 deisred configuration + """ + commands = [] + + have_dict = filter_dict_having_none_value(want, have) + commands.extend(self._clear_config(have_dict)) + commands.extend(self._set_config(want, have)) + + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :param want: the additive configuration as a dictionary + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + commands.extend(self._set_config(want, have)) + + return commands + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :param want: the objects from which the configuration should be removed + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + + commands.extend(self._clear_config(have)) + + return commands + + def _remove_command_from_config_list(self, cmd, commands): + if cmd not in commands: + commands.append("no %s" % cmd) + + def add_command_to_config_list(self, cmd, commands): + if cmd not in commands: + commands.append(cmd) + + def _set_config(self, want, have): + # Set the lldp global config based on the want and have config + commands = [] + + # Get the diff b/w want and have + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + diff = want_dict - have_dict + + if diff: + diff = dict(diff) + holdtime = diff.get("holdtime") + enabled = diff.get("enabled") + timer = diff.get("timer") + reinit = diff.get("reinit") + tlv_select = diff.get("tlv_select") + + if holdtime: + cmd = "lldp holdtime {0}".format(holdtime) + self.add_command_to_config_list(cmd, commands) + if enabled: + cmd = "lldp run" + self.add_command_to_config_list(cmd, commands) + if timer: + cmd = "lldp timer {0}".format(timer) + self.add_command_to_config_list(cmd, commands) + if reinit: + cmd = "lldp reinit {0}".format(reinit) + self.add_command_to_config_list(cmd, commands) + if tlv_select: + tlv_selec_dict = dict(tlv_select) + for k, v in iteritems(self.tlv_select_params): + if k in tlv_selec_dict and tlv_selec_dict[k]: + cmd = "lldp tlv-select {0}".format(v) + self.add_command_to_config_list(cmd, commands) + + return commands + + def _clear_config(self, have): + # Delete the lldp global config based on the want and have config + commands = [] + + if have.get("holdtime"): + cmd = "lldp holdtime" + self._remove_command_from_config_list(cmd, commands) + if have.get("enabled"): + cmd = "lldp run" + self._remove_command_from_config_list(cmd, commands) + if have.get("timer"): + cmd = "lldp timer" + self._remove_command_from_config_list(cmd, commands) + if have.get("reinit"): + cmd = "lldp reinit" + self._remove_command_from_config_list(cmd, commands) + + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_interfaces/lldp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_interfaces/lldp_interfaces.py new file mode 100644 index 00000000..479f626c --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_interfaces/lldp_interfaces.py @@ -0,0 +1,328 @@ +# +# -*- 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 ios_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 its 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 ( + to_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + dict_to_set, + normalize_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + remove_command_from_config_list, + add_command_to_config_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + filter_dict_having_none_value, + remove_duplicate_interface, +) + + +class Lldp_Interfaces(ConfigBase): + """ + The ios_lldp_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["lldp_interfaces"] + + 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} + commands = list() + warnings = 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 + """ + config = self._module.params.get("config") + want = [] + if config: + for each in config: + each.update({"name": normalize_interface(each["name"])}) + want.append(each) + 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 + """ + + if ( + self.state in ("overridden", "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 == "overridden": + commands = self._state_overridden(want, have) + elif self.state == "deleted": + commands = self._state_deleted(want, have) + elif self.state in ("merged", "rendered"): + commands = self._state_merged(want, have) + elif self.state == "replaced": + commands = self._state_replaced(want, 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 = [] + + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + 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 each in have: + for interface in want: + if each["name"] == interface["name"]: + break + else: + # We didn't find a matching desired state, which means we can + # pretend we received an empty desired state. + interface = dict(name=each["name"]) + commands.extend(self._clear_config(interface, each)) + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + 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 interface in want: + for each in have: + if interface["name"] == each["name"]: + break + else: + if self.state == "rendered": + commands.extend(self._set_config(interface, dict())) + continue + commands.extend(self._set_config(interface, each)) + + 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 interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + continue + interface = dict(name=interface["name"]) + commands.extend(self._clear_config(interface, each)) + else: + for each in have: + commands.extend(self._clear_config(dict(), each)) + + return commands + + def _set_config(self, want, have): + # Set the interface config based on the want and have config + commands = [] + + interface = "interface " + want["name"] + # Get the diff b/w want and have + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + diff = want_dict - have_dict + + if diff: + diff = dict(diff) + receive = diff.get("receive") + transmit = diff.get("transmit") + med_tlv_select = diff.get("med_tlv_select") + tlv_select = diff.get("tlv_select") + if receive: + cmd = "lldp receive" + add_command_to_config_list(interface, cmd, commands) + elif receive is False: + cmd = "no lldp receive" + add_command_to_config_list(interface, cmd, commands) + if transmit: + cmd = "lldp transmit" + add_command_to_config_list(interface, cmd, commands) + elif transmit is False: + cmd = "no lldp transmit" + add_command_to_config_list(interface, cmd, commands) + + if med_tlv_select: + med_tlv_select = dict(med_tlv_select) + if med_tlv_select.get("inventory_management"): + add_command_to_config_list( + interface, + "lldp med-tlv-select inventory-management", + commands, + ) + if tlv_select: + tlv_select = dict(tlv_select) + if tlv_select.get("power_management"): + add_command_to_config_list( + interface, "lldp tlv-select power-management", commands + ) + + return commands + + def _clear_config(self, want, have): + # Delete the interface config based on the want and have config + commands = [] + if want.get("name"): + interface = "interface " + want["name"] + else: + interface = "interface " + have["name"] + + if have.get("receive") and have.get("receive") != want.get("receive"): + cmd = "lldp receive" + remove_command_from_config_list(interface, cmd, commands) + if have.get("transmit") and have.get("transmit") != want.get( + "transmit" + ): + cmd = "lldp transmit" + remove_command_from_config_list(interface, cmd, commands) + + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospf_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospf_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospf_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospf_interfaces/ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 00000000..3640207d --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,165 @@ +# +# -*- 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 cisco.ios_ospf_interfaces config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import ( + ResourceModule, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ospf_interfaces import ( + Ospf_InterfacesTemplate, +) + + +class Ospf_Interfaces(ResourceModule): + """ + The cisco.ios_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 = [] + + 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 = {} + if self.want: + wantd = {(entry["name"]): entry for entry in self.want} + else: + wantd = {} + if self.have: + haved = {(entry["name"]): entry for entry in self.have} + else: + 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 iteritems(haved) 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 iteritems(haved): + if k not in wantd: + self._compare(want={}, have=have) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _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. + """ + parsers = [ + "name", + "process", + "adjacency", + "authentication", + "bfd", + "cost_ip", + "cost_ipv6_dynamic_cost", + "database_filter", + "dead_interval", + "demand_circuit", + "flood_reduction", + "hello_interval", + "lls", + "manet", + "mtu_ignore", + "multi_area", + "neighbor", + "network", + "prefix_suppression", + "priority", + "resync_timeout", + "retransmit_interval", + "shutdown", + "transmit_delay", + "ttl_security", + ] + + if ( + want != have + ): # and (want.get('address_family') or self.state == 'deleted'): + if have.get("address_family"): + self.addcmd(have, "name", False) + elif want.get("address_family"): + self.addcmd(want, "name", False) + + if want.get("address_family"): + for each in want["address_family"]: + set_want = True + if have.get("address_family"): + have_elements = len(have.get("address_family")) + while have_elements: + if have.get("address_family")[have_elements - 1].get( + "afi" + ) == each.get("afi"): + set_want = False + h_each = have["address_family"].pop( + have_elements - 1 + ) + self.compare( + parsers=parsers, want=each, have=h_each + ) + have_elements -= 1 + else: + h_each = dict() + self.compare(parsers=parsers, want=each, have=h_each) + set_want = False + if set_want: + self.compare(parsers=parsers, want=each, have=dict()) + if self.state in ["overridden", "deleted"]: + if have.get("address_family"): + for each in have["address_family"]: + self.compare(parsers=parsers, want=dict(), have=each) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv2/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv2/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv2/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv2/ospfv2.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv2/ospfv2.py new file mode 100644 index 00000000..08c0af36 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv2/ospfv2.py @@ -0,0 +1,225 @@ +# -*- 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) +""" +The ios_ospfv2 class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible.module_utils.six import iteritems +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ospfv2 import ( + Ospfv2Template, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import ( + ResourceModule, +) + + +class Ospfv2(ResourceModule): + """ + The ios_ospfv2 class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["ospfv2"] + + def __init__(self, module): + super(Ospfv2, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="ospfv2", + tmplt=Ospfv2Template(), + ) + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + self.gen_config() + self.run_commands() + return self.result + + def gen_config(self): + """ Select the appropriate function based on the state provided + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + if self.want: + wantd = { + (entry["process_id"], entry.get("vrf")): entry + for entry in self.want.get("processes", []) + } + else: + wantd = {} + if self.have: + haved = { + (entry["process_id"], entry.get("vrf")): entry + for entry in self.have.get("processes", []) + } + else: + haved = {} + + # turn all lists of dicts into dicts prior to merge + for thing in wantd, haved: + for _pid, proc in iteritems(thing): + for area in proc.get("areas", []): + ranges = { + entry["address"]: entry + for entry in area.get("ranges", []) + } + if bool(ranges): + area["ranges"] = ranges + filter_list = { + entry["direction"]: entry + for entry in area.get("filter_list", []) + } + if bool(filter_list): + area["filter_list"] = filter_list + proc["areas"] = { + entry["area_id"]: entry for entry in proc.get("areas", []) + } + if proc.get("distribute_list"): + if "acls" in proc.get("distribute_list"): + proc["distribute_list"]["acls"] = { + entry["name"]: entry + for entry in proc["distribute_list"].get( + "acls", [] + ) + } + + # if state is merged, merge want onto have + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, limit the have to anything in want + # set want to nothing + if self.state == "deleted": + haved = { + k: v for k, v in iteritems(haved) if k in wantd or not wantd + } + wantd = {} + + # delete processes first so we do run into "more than one" errors + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + if k not in wantd: + self.addcmd(have, "pid", True) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + parsers = [ + "adjacency", + "address_family", + "auto_cost", + "bfd", + "capability", + "compatible", + "default_information", + "default_metric", + "discard_route", + "distance.admin_distance", + "distance.ospf", + "distribute_list.acls", + "distribute_list.prefix", + "distribute_list.route_map", + "domain_id", + "domain_tag", + "event_log", + "help", + "ignore", + "interface_id", + "ispf", + "limit", + "local_rib_criteria", + "log_adjacency_changes", + "max_lsa", + "max_metric", + "maximum_paths", + "mpls.ldp", + "mpls.traffic_eng", + "neighbor", + "network", + "nsf.cisco", + "nsf.ietf", + "passive_interface", + "prefix_suppression", + "priority", + "queue_depth.hello", + "queue_depth.update", + "router_id", + "shutdown", + "summary_address", + "timers.throttle.lsa", + "timers.throttle.spf", + "traffic_share", + "ttl_security", + ] + + if want != have: + self.addcmd(want or have, "pid", False) + self.compare(parsers, want, have) + self._areas_compare(want, have) + + def _areas_compare(self, want, have): + wareas = want.get("areas", {}) + hareas = have.get("areas", {}) + for name, entry in iteritems(wareas): + self._area_compare(want=entry, have=hareas.pop(name, {})) + for name, entry in iteritems(hareas): + self._area_compare(want={}, have=entry) + + def _area_compare(self, want, have): + parsers = [ + "area.authentication", + "area.capability", + "area.default_cost", + "area.nssa", + "area.nssa.translate", + "area.ranges", + "area.sham_link", + "area.stub", + ] + self.compare(parsers=parsers, want=want, have=have) + self._area_compare_filters(want, have) + + def _area_compare_filters(self, wantd, haved): + for name, entry in iteritems(wantd): + h_item = haved.pop(name, {}) + if entry != h_item and name == "filter_list": + filter_list_entry = {} + filter_list_entry["area_id"] = wantd["area_id"] + if h_item: + li_diff = [ + item + for item in entry + h_item + if item not in entry or item not in h_item + ] + else: + li_diff = entry + filter_list_entry["filter_list"] = li_diff + self.addcmd(filter_list_entry, "area.filter_list", False) + for name, entry in iteritems(haved): + if name == "filter_list": + self.addcmd(entry, "area.filter_list", True) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv3/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv3/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv3/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv3/ospfv3.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv3/ospfv3.py new file mode 100644 index 00000000..f3c1e3d2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv3/ospfv3.py @@ -0,0 +1,324 @@ +# -*- 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) +""" +The ios_ospfv3 class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible.module_utils.six import iteritems +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ospfv3 import ( + Ospfv3Template, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import ( + ResourceModule, +) + + +class Ospfv3(ResourceModule): + """ + The ios_ospfv3 class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["ospfv3"] + + def __init__(self, module): + super(Ospfv3, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="ospfv3", + tmplt=Ospfv3Template(), + ) + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + self.gen_config() + self.run_commands() + return self.result + + def gen_config(self): + """ Select the appropriate function based on the state provided + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + if self.want: + wantd = { + (entry["process_id"], entry.get("vrf")): entry + for entry in self.want.get("processes", []) + } + else: + wantd = {} + if self.have: + haved = { + (entry["process_id"], entry.get("vrf")): entry + for entry in self.have.get("processes", []) + } + else: + haved = {} + + # turn all lists of dicts into dicts prior to merge + for thing in wantd, haved: + for _pid, proc in iteritems(thing): + for area in proc.get("areas", []): + ranges = { + entry["address"]: entry + for entry in area.get("ranges", []) + } + if bool(ranges): + area["ranges"] = ranges + filter_list = { + entry["direction"]: entry + for entry in area.get("filter_list", []) + } + if bool(filter_list): + area["filter_list"] = filter_list + proc["areas"] = { + entry["area_id"]: entry for entry in proc.get("areas", []) + } + if proc.get("distribute_list"): + if "acls" in proc.get("distribute_list"): + proc["distribute_list"]["acls"] = { + entry["name"]: entry + for entry in proc["distribute_list"].get( + "acls", [] + ) + } + + # if state is merged, merge want onto have + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, limit the have to anything in want + # set want to nothing + if self.state == "deleted": + haved = { + k: v for k, v in iteritems(haved) if k in wantd or not wantd + } + wantd = {} + + # delete processes first so we do run into "more than one" errors + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + if k not in wantd: + self.addcmd(have, "pid", True) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + parsers = [ + "adjacency", + "auto_cost", + "bfd", + "compatible", + "event_log", + "help", + "interface_id", + "limit", + "local_rib_criteria", + "log_adjacency_changes", + "manet", + "max_lsa", + "max_metric", + "passive_interface", + "prefix_suppression", + "queue_depth.hello", + "queue_depth.update", + "router_id", + "shutdown", + "timers.throttle.lsa", + "timers.throttle.spf", + ] + + if want != have: + self.addcmd(want or have, "pid", False) + self.compare(parsers, want, have) + self._areas_compare(want, have) + self._address_family_compare(want, have) + + if len(self.commands) == 1 and "router" in self.commands[0]: + del self.commands[0] + + def _areas_compare(self, want, have): + wareas = want.get("areas", {}) + hareas = have.get("areas", {}) + for name, entry in iteritems(wareas): + self._area_compare(want=entry, have=hareas.pop(name, {})) + for name, entry in iteritems(hareas): + self._area_compare(want={}, have=entry) + + def _area_compare(self, want, have): + parsers = [ + "area.authentication", + "area.capability", + "area.default_cost", + "area.nssa", + "area.nssa.translate", + "area.ranges", + "area.sham_link", + "area.stub", + ] + self.compare(parsers=parsers, want=want, have=have) + self._area_compare_filters(want, have) + + def _area_compare_filters(self, wantd, haved): + for name, entry in iteritems(wantd): + h_item = haved.pop(name, {}) + if entry != h_item and name == "filter_list": + filter_list_entry = {} + filter_list_entry["area_id"] = wantd["area_id"] + if h_item: + li_diff = [ + item + for item in entry + h_item + if item not in entry or item not in h_item + ] + else: + li_diff = entry + filter_list_entry["filter_list"] = li_diff + self.addcmd(filter_list_entry, "area.filter_list", False) + for name, entry in iteritems(haved): + if name == "filter_list": + self.addcmd(entry, "area.filter_list", True) + + def _address_family_compare(self, want, have): + if want["process_id"] == have.get("process_id") or want["process_id"]: + af_parsers = [ + "address_family.adjacency", + "address_family.auto_cost", + "address_family.bfd", + "address_family.capability", + "address_family.compatible", + "address_family.default_information", + "address_family.default_metric", + "address_family.distance.admin_distance", + "address_family.distance.ospf", + "address_family.distribute_list.acls", + "address_family.distribute_list.prefix", + "address_family.distribute_list.route_map", + "address_family.event_log", + "address_family.graceful_restart", + "address_family.interface_id", + "address_family.limit", + "address_family.local_rib_criteria", + "address_family.log_adjacency_changes", + "address_family.manet", + "address_family.max_lsa", + "address_family.max_metric", + "address_family.maximum_paths", + "address_family.passive_interface", + "address_family.prefix_suppression", + "address_family.queue_depth.hello", + "address_family.queue_depth.update", + "address_family.router_id", + "address_family.shutdown", + "address_family.summary_prefix", + "address_family.timers.throttle.lsa", + "address_family.timers.throttle.spf", + ] + delete_exit_family = False + for each_want_af in want["address_family"]: + if have.get("address_family"): + for each_have_af in have["address_family"]: + if each_have_af.get("vrf") == each_want_af.get( + "vrf" + ) and each_have_af.get("afi") == each_want_af.get( + "afi" + ): + self.compare( + parsers=["address_family"], + want={"address_family": each_want_af}, + have={"address_family": each_have_af}, + ) + self.compare( + parsers=af_parsers, + want=each_want_af, + have=each_have_af, + ) + elif each_have_af.get("afi") == each_want_af.get( + "afi" + ): + self.compare( + parsers=["address_family"], + want={"address_family": each_want_af}, + have={"address_family": each_have_af}, + ) + self.compare( + parsers=af_parsers, + want={"address_family": each_want_af}, + have={"address_family": each_have_af}, + ) + if each_want_af.get("areas"): + af_want_areas = {} + af_have_areas = {} + for each_area in each_want_af["areas"]: + af_want_areas.update( + {each_area["area_id"]: each_area} + ) + if each_have_af.get("areas"): + for each_area in each_have_af["areas"]: + af_have_areas.update( + {each_area["area_id"]: each_area} + ) + + if "exit-address-family" in self.commands: + del self.commands[ + self.commands.index("exit-address-family") + ] + delete_exit_family = True + + if af_have_areas: + self._areas_compare( + {"areas": af_want_areas}, + {"areas": af_have_areas}, + ) + else: + self._areas_compare( + {"areas": af_want_areas}, dict() + ) + if delete_exit_family: + self.commands.append("exit-address-family") + else: + temp_cmd_before = self.commands + self.commands = [] + self.compare( + parsers=["address_family"], + want={"address_family": each_want_af}, + have=dict(), + ) + self.compare( + parsers=af_parsers, want=each_want_af, have=dict() + ) + if each_want_af.get("areas"): + af_areas = {} + for each_area in each_want_af["areas"]: + af_areas.update({each_area["area_id"]: each_area}) + self._areas_compare({"areas": af_areas}, dict()) + del self.commands[ + self.commands.index("exit-address-family") + ] + self.commands.append("exit-address-family") + self.commands[0:0] = temp_cmd_before diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/static_routes/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/static_routes/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/static_routes/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/static_routes/static_routes.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/static_routes/static_routes.py new file mode 100644 index 00000000..15053f44 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/static_routes/static_routes.py @@ -0,0 +1,713 @@ +# +# -*- 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 ios_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 + +import copy +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + new_dict_to_set, + validate_n_expand_ipv4, + filter_dict_having_none_value, +) + + +class Static_Routes(ConfigBase): + """ + The ios_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} + commands = list() + warnings = 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 + """ + state = self._module.params["state"] + if ( + state in ("overridden", "merged", "replaced", "rendered") + and not want + ): + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state + ) + ) + commands = [] + if state == "overridden": + commands = self._state_overridden(want, have) + elif state == "deleted": + commands = self._state_deleted(want, have) + elif state == "merged" or state == "rendered": + commands = self._state_merged(want, have) + elif state == "replaced": + commands = self._state_replaced(want, 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 = [] + + # Drill each iteration of want n have and then based on dest and afi tyoe comparison take config call + for w in want: + for addr_want in w.get("address_families"): + for route_want in addr_want.get("routes"): + check = False + for h in have: + if h.get("address_families"): + for addr_have in h.get("address_families"): + for route_have in addr_have.get("routes"): + if ( + route_want.get("dest") + == route_have.get("dest") + and addr_want["afi"] + == addr_have["afi"] + ): + check = True + have_set = set() + new_hops = [] + for each in route_want.get( + "next_hops" + ): + want_set = set() + new_dict_to_set( + each, [], want_set, 0 + ) + new_hops.append(want_set) + new_dict_to_set( + addr_have, [], have_set, 0 + ) + # Check if the have dict next_hops value is diff from want dict next_hops + have_dict = filter_dict_having_none_value( + route_want.get("next_hops")[0], + route_have.get("next_hops")[0], + ) + # update the have_dict with forward_router_address + have_dict.update( + { + "forward_router_address": route_have.get( + "next_hops" + )[ + 0 + ].get( + "forward_router_address" + ) + } + ) + # updating the have_dict with next_hops val that's not None + new_have_dict = {} + for k, v in have_dict.items(): + if v is not None: + new_have_dict.update({k: v}) + + # Set the new config from the user provided want config + cmd = self._set_config( + w, + h, + addr_want, + route_want, + route_have, + new_hops, + have_set, + ) + + if cmd: + # since inplace update isn't allowed for static routes, preconfigured + # static routes needs to be deleted before the new want static routes changes + # are applied + clear_route_have = copy.deepcopy( + route_have + ) + # inplace update is allowed in case of ipv6 static routes, so not deleting it + # before applying the want changes + if ":" not in route_want.get( + "dest" + ): + commands.extend( + self._clear_config( + {}, + h, + {}, + addr_have, + {}, + clear_route_have, + ) + ) + commands.extend(cmd) + if check: + break + if check: + break + if not check: + # For configuring any non-existing want config + new_hops = [] + for each in route_want.get("next_hops"): + want_set = set() + new_dict_to_set(each, [], want_set, 0) + new_hops.append(want_set) + commands.extend( + self._set_config( + w, + {}, + addr_want, + route_want, + {}, + new_hops, + set(), + ) + ) + commands = [each for each in commands if "no" in each] + [ + each for each in commands if "no" not in each + ] + + 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 = [] + # Creating a copy of want, so that want dict is intact even after delete operation + # performed during override want n have comparison + temp_want = copy.deepcopy(want) + + # Drill each iteration of want n have and then based on dest and afi tyoe comparison take config call + for h in have: + if h.get("address_families"): + for addr_have in h.get("address_families"): + for route_have in addr_have.get("routes"): + check = False + for w in temp_want: + for addr_want in w.get("address_families"): + count = 0 + for route_want in addr_want.get("routes"): + if ( + route_want.get("dest") + == route_have.get("dest") + and addr_want["afi"] + == addr_have["afi"] + ): + check = True + have_set = set() + new_hops = [] + for each in route_want.get( + "next_hops" + ): + want_set = set() + new_dict_to_set( + each, [], want_set, 0 + ) + new_hops.append(want_set) + new_dict_to_set( + addr_have, [], have_set, 0 + ) + commands.extend( + self._clear_config( + w, + h, + addr_want, + addr_have, + route_want, + route_have, + ) + ) + commands.extend( + self._set_config( + w, + h, + addr_want, + route_want, + route_have, + new_hops, + have_set, + ) + ) + del addr_want.get("routes")[count] + count += 1 + if check: + break + if check: + break + if not check: + commands.extend( + self._clear_config( + {}, h, {}, addr_have, {}, route_have + ) + ) + # For configuring any non-existing want config + for w in temp_want: + for addr_want in w.get("address_families"): + for route_want in addr_want.get("routes"): + new_hops = [] + for each in route_want.get("next_hops"): + want_set = set() + new_dict_to_set(each, [], want_set, 0) + new_hops.append(want_set) + commands.extend( + self._set_config( + w, {}, addr_want, route_want, {}, new_hops, set() + ) + ) + # Arranging the cmds suct that all delete cmds are fired before all set cmds + commands = [each for each in sorted(commands) if "no" in each] + [ + each for each in sorted(commands) if "no" not in each + ] + + 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 = [] + + # Drill each iteration of want n have and then based on dest and afi tyoe comparison take config call + for w in want: + for addr_want in w.get("address_families"): + for route_want in addr_want.get("routes"): + check = False + for h in have: + if h.get("address_families"): + for addr_have in h.get("address_families"): + for route_have in addr_have.get("routes"): + if ( + route_want.get("dest") + == route_have.get("dest") + and addr_want["afi"] + == addr_have["afi"] + ): + check = True + have_set = set() + new_hops = [] + for each in route_want.get( + "next_hops" + ): + want_set = set() + new_dict_to_set( + each, [], want_set, 0 + ) + new_hops.append(want_set) + new_dict_to_set( + addr_have, [], have_set, 0 + ) + commands.extend( + self._set_config( + w, + h, + addr_want, + route_want, + route_have, + new_hops, + have_set, + ) + ) + if check: + break + if check: + break + if not check: + # For configuring any non-existing want config + new_hops = [] + for each in route_want.get("next_hops"): + want_set = set() + new_dict_to_set(each, [], want_set, 0) + new_hops.append(want_set) + commands.extend( + self._set_config( + w, + {}, + addr_want, + route_want, + {}, + new_hops, + set(), + ) + ) + + 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: + # Drill each iteration of want n have and then based on dest and afi type comparison fire delete config call + for w in want: + if w.get("address_families"): + for addr_want in w.get("address_families"): + for route_want in addr_want.get("routes"): + check = False + for h in have: + if h.get("address_families"): + for addr_have in h.get("address_families"): + for route_have in addr_have.get( + "routes" + ): + if ( + route_want.get("dest") + == route_have.get("dest") + and addr_want["afi"] + == addr_have["afi"] + ): + check = True + if route_want.get("next_hops"): + commands.extend( + self._clear_config( + {}, + w, + {}, + addr_want, + {}, + route_want, + ) + ) + else: + commands.extend( + self._clear_config( + {}, + h, + {}, + addr_have, + {}, + route_have, + ) + ) + if check: + break + if check: + break + else: + for h in have: + for addr_have in h.get("address_families"): + for route_have in addr_have.get("routes"): + if w.get("vrf") == h.get("vrf"): + commands.extend( + self._clear_config( + {}, + h, + {}, + addr_have, + {}, + route_have, + ) + ) + else: + # Drill each iteration of have and then based on dest and afi type comparison fire delete config call + for h in have: + for addr_have in h.get("address_families"): + for route_have in addr_have.get("routes"): + commands.extend( + self._clear_config( + {}, h, {}, addr_have, {}, route_have + ) + ) + + return commands + + def prepare_config_commands(self, config_dict, cmd): + """ + function to parse the input dict and form the prepare the config commands + :rtype: A str + :returns: The command necessary to configure the static routes + """ + + dhcp = config_dict.get("dhcp") + distance_metric = config_dict.get("distance_metric") + forward_router_address = config_dict.get("forward_router_address") + global_route_config = config_dict.get("global") + interface = config_dict.get("interface") + multicast = config_dict.get("multicast") + name = config_dict.get("name") + permanent = config_dict.get("permanent") + tag = config_dict.get("tag") + track = config_dict.get("track") + dest = config_dict.get("dest") + temp_dest = dest.split("/") + if temp_dest and ":" not in dest: + dest = validate_n_expand_ipv4(self._module, {"address": dest}) + + cmd = cmd + dest + if interface: + cmd = cmd + " {0}".format(interface) + if forward_router_address: + cmd = cmd + " {0}".format(forward_router_address) + if dhcp: + cmd = cmd + " DHCP" + if distance_metric: + cmd = cmd + " {0}".format(distance_metric) + if global_route_config: + cmd = cmd + " global" + if multicast: + cmd = cmd + " multicast" + if name: + cmd = cmd + " name {0}".format(name) + if permanent: + cmd = cmd + " permanent" + elif track: + cmd = cmd + " track {0}".format(track) + if tag: + cmd = cmd + " tag {0}".format(tag) + + return cmd + + def _set_config( + self, want, have, addr_want, route_want, route_have, hops, have_set + ): + """ + Set the interface config based on the want and have config + :rtype: A list + :returns: The commands necessary to configure the static routes + """ + + commands = [] + cmd = None + + vrf_diff = False + topology_diff = False + want_vrf = want.get("vrf") + have_vrf = have.get("vrf") + if want_vrf != have_vrf: + vrf_diff = True + want_topology = want.get("topology") + have_topology = have.get("topology") + if want_topology != have_topology: + topology_diff = True + + have_dest = route_have.get("dest") + if have_dest: + have_set.add(tuple(iteritems({"dest": have_dest}))) + + # configure set cmd for each hops under the same destination + for each in hops: + diff = each - have_set + if vrf_diff: + each.add(tuple(iteritems({"vrf": want_vrf}))) + if topology_diff: + each.add(tuple(iteritems({"topology": want_topology}))) + if diff or vrf_diff or topology_diff: + if want_vrf and not vrf_diff: + each.add(tuple(iteritems({"vrf": want_vrf}))) + if want_topology and not vrf_diff: + each.add(tuple(iteritems({"topology": want_topology}))) + each.add(tuple(iteritems({"afi": addr_want.get("afi")}))) + each.add(tuple(iteritems({"dest": route_want.get("dest")}))) + temp_want = {} + for each_want in each: + temp_want.update(dict(each_want)) + + if temp_want.get("afi") == "ipv4": + cmd = "ip route " + vrf = temp_want.get("vrf") + if vrf: + cmd = cmd + "vrf {0} ".format(vrf) + cmd = self.prepare_config_commands(temp_want, cmd) + elif temp_want.get("afi") == "ipv6": + cmd = "ipv6 route " + cmd = self.prepare_config_commands(temp_want, cmd) + commands.append(cmd) + + return commands + + def _clear_config( + self, want, have, addr_want, addr_have, route_want, route_have + ): + """ + Delete the interface config based on the want and have config + :rtype: A list + :returns: The commands necessary to configure the static routes + """ + + commands = [] + cmd = None + + vrf_diff = False + topology_diff = False + want_vrf = want.get("vrf") + have_vrf = have.get("vrf") + if want_vrf != have_vrf: + vrf_diff = True + want_topology = want.get("topology") + have_topology = have.get("topology") + if want_topology != have_topology: + topology_diff = True + + want_set = set() + new_dict_to_set(addr_want, [], want_set, 0) + + have_hops = [] + for each in route_have.get("next_hops"): + temp_have_set = set() + new_dict_to_set(each, [], temp_have_set, 0) + have_hops.append(temp_have_set) + + # configure delete cmd for each hops under the same destination + for each in have_hops: + diff = each - want_set + if vrf_diff: + each.add(tuple(iteritems({"vrf": have_vrf}))) + if topology_diff: + each.add(tuple(iteritems({"topology": want_topology}))) + if diff or vrf_diff or topology_diff: + if want_vrf and not vrf_diff: + each.add(tuple(iteritems({"vrf": want_vrf}))) + if want_topology and not vrf_diff: + each.add(tuple(iteritems({"topology": want_topology}))) + if addr_want: + each.add(tuple(iteritems({"afi": addr_want.get("afi")}))) + else: + each.add(tuple(iteritems({"afi": addr_have.get("afi")}))) + if route_want: + each.add( + tuple(iteritems({"dest": route_want.get("dest")})) + ) + else: + each.add( + tuple(iteritems({"dest": route_have.get("dest")})) + ) + temp_want = {} + for each_want in each: + temp_want.update(dict(each_want)) + + if temp_want.get("afi") == "ipv4": + cmd = "no ip route " + vrf = temp_want.get("vrf") + if vrf: + cmd = cmd + "vrf {0} ".format(vrf) + cmd = self.prepare_config_commands(temp_want, cmd) + elif temp_want.get("afi") == "ipv6": + cmd = "no ipv6 route " + cmd = self.prepare_config_commands(temp_want, cmd) + commands.append(cmd) + + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/vlans.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/vlans.py new file mode 100644 index 00000000..1e6edec8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/vlans.py @@ -0,0 +1,337 @@ +# +# -*- 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 ios_vlans 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 ( + to_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + dict_to_set, +) + + +class Vlans(ConfigBase): + """ + The ios_vlans class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["vlans"] + + def __init__(self, module): + super(Vlans, self).__init__(module) + + def get_vlans_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("vlans") + 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_vlans_facts = self.get_vlans_facts() + else: + existing_vlans_facts = [] + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_vlans_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_vlans_facts = self.get_vlans_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_vlans_facts(data=running_config) + else: + changed_vlans_facts = [] + + if self.state in self.ACTION_STATES: + result["before"] = existing_vlans_facts + if result["changed"]: + result["after"] = changed_vlans_facts + elif self.state == "gathered": + result["gathered"] = changed_vlans_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_vlans_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_vlans_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 + """ + + if ( + self.state in ("overridden", "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 == "overridden": + commands = self._state_overridden(want, have) + elif self.state == "deleted": + commands = self._state_deleted(want, have) + elif self.state in ("merged", "rendered"): + commands = self._state_merged(want, have) + elif self.state == "replaced": + commands = self._state_replaced(want, 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 = [] + + check = False + for each in want: + for every in have: + if every["vlan_id"] == each["vlan_id"]: + check = True + break + continue + if check: + commands.extend(self._set_config(each, every)) + else: + commands.extend(self._set_config(each, dict())) + + 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 = [] + + want_local = want + for each in have: + count = 0 + for every in want_local: + if each["vlan_id"] == every["vlan_id"]: + break + count += 1 + else: + # We didn't find a matching desired state, which means we can + # pretend we received an empty desired state. + commands.extend(self._clear_config(every, each)) + continue + commands.extend(self._set_config(every, each)) + # as the pre-existing VLAN are now configured by + # above set_config call, deleting the respective + # VLAN entry from the want_local list + del want_local[count] + + # Iterating through want_local list which now only have new VLANs to be + # configured + for each in want_local: + commands.extend(self._set_config(each, dict())) + + 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 = [] + + check = False + for each in want: + for every in have: + if each.get("vlan_id") == every.get("vlan_id"): + check = True + break + continue + if check: + commands.extend(self._set_config(each, every)) + else: + commands.extend(self._set_config(each, dict())) + + 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: + check = False + for each in want: + for every in have: + if each.get("vlan_id") == every.get("vlan_id"): + check = True + break + check = False + continue + if check: + commands.extend(self._clear_config(each, every)) + else: + for each in have: + commands.extend(self._clear_config(dict(), each)) + + return commands + + def remove_command_from_config_list(self, vlan, cmd, commands): + if vlan not in commands and cmd != "vlan": + commands.insert(0, vlan) + elif cmd == "vlan": + commands.append("no %s" % vlan) + return commands + commands.append("no %s" % cmd) + return commands + + def add_command_to_config_list(self, vlan_id, cmd, commands): + if vlan_id not in commands: + commands.insert(0, vlan_id) + if cmd not in commands: + commands.append(cmd) + + def _set_config(self, want, have): + # Set the interface config based on the want and have config + commands = [] + vlan = "vlan {0}".format(want.get("vlan_id")) + + # Get the diff b/w want n have + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + diff = want_dict - have_dict + + if diff: + name = dict(diff).get("name") + state = dict(diff).get("state") + shutdown = dict(diff).get("shutdown") + mtu = dict(diff).get("mtu") + remote_span = dict(diff).get("remote_span") + if name: + cmd = "name {0}".format(name) + self.add_command_to_config_list(vlan, cmd, commands) + if state: + cmd = "state {0}".format(state) + self.add_command_to_config_list(vlan, cmd, commands) + if mtu: + cmd = "mtu {0}".format(mtu) + self.add_command_to_config_list(vlan, cmd, commands) + if remote_span: + self.add_command_to_config_list(vlan, "remote-span", commands) + if shutdown == "enabled": + self.add_command_to_config_list(vlan, "shutdown", commands) + elif shutdown == "disabled": + self.add_command_to_config_list(vlan, "no shutdown", commands) + + return commands + + def _clear_config(self, want, have): + # Delete the interface config based on the want and have config + commands = [] + vlan = "vlan {0}".format(have.get("vlan_id")) + + if ( + have.get("vlan_id") + and "default" not in have.get("name") + and ( + have.get("vlan_id") != want.get("vlan_id") + or self.state == "deleted" + ) + ): + self.remove_command_from_config_list(vlan, "vlan", commands) + elif "default" not in have.get("name"): + if have.get("mtu") != want.get("mtu"): + self.remove_command_from_config_list(vlan, "mtu", commands) + if have.get("remote_span") != want.get("remote_span") and want.get( + "remote_span" + ): + self.remove_command_from_config_list( + vlan, "remote-span", commands + ) + if have.get("shutdown") != want.get("shutdown") and want.get( + "shutdown" + ): + self.remove_command_from_config_list( + vlan, "shutdown", commands + ) + if have.get("state") != want.get("state") and want.get("state"): + self.remove_command_from_config_list(vlan, "state", commands) + + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acl_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acl_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acl_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acl_interfaces/acl_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acl_interfaces/acl_interfaces.py new file mode 100644 index 00000000..03d65e94 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acl_interfaces/acl_interfaces.py @@ -0,0 +1,133 @@ +# +# -*- 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 ios_acl_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.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + get_interface_type, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.acl_interfaces.acl_interfaces import ( + Acl_InterfacesArgs, +) + + +class Acl_InterfacesFacts(object): + """ The ios_acl_interfaces fact class + """ + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Acl_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_acl_interfaces_data(self, connection): + return connection.get( + "sh running-config | include interface|ip access-group|ipv6 traffic-filter" + ) + + 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 + """ + objs = [] + + if not data: + data = self.get_acl_interfaces_data(connection) + # operate on a collection of resource x + config = ("\n" + data).split("\ninterface ") + for conf in config: + if conf: + obj = self.render_config(self.generated_spec, conf) + if obj: + objs.append(obj) + + facts = {} + if objs: + facts["acl_interfaces"] = [] + params = utils.validate_config( + self.argument_spec, {"config": objs} + ) + + for cfg in params["config"]: + facts["acl_interfaces"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def render_config(self, spec, 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 = deepcopy(spec) + match = re.search(r"^(\S+)", conf) + intf = match.group(1) + + if get_interface_type(intf) == "unknown": + return {} + config["name"] = intf + config["access_groups"] = [] + acl_v4_config = {} + acl_v6_config = {} + + def common_iter_code(cmd, conf): + # Common code for IPV4 and IPV6 config parsing + acls = [] + re_cmd = cmd + " (\\S+.*)" + ip_all = re.findall(re_cmd, conf) + for each in ip_all: + acl = {} + access_grp_config = each.split(" ") + acl["name"] = access_grp_config[0] + acl["direction"] = access_grp_config[1] + acls.append(acl) + return acls + + if "ip" in conf: + acls = common_iter_code("ip access-group", conf) + acl_v4_config["afi"] = "ipv4" + acl_v4_config["acls"] = acls + config["access_groups"].append(acl_v4_config) + if "ipv6" in conf: + acls = common_iter_code("ipv6 traffic-filter", conf) + acl_v6_config["afi"] = "ipv6" + acl_v6_config["acls"] = acls + config["access_groups"].append(acl_v6_config) + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/acls.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/acls.py new file mode 100644 index 00000000..7ef73e7e --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/acls.py @@ -0,0 +1,127 @@ +# pylint: skip-file +# -*- 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 ios_acls 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 ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.acls.acls import ( + AclsArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.acls import ( + AclsTemplate, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import ( + NetworkTemplate, +) + + +class AclsFacts(object): + """ The ios_acls fact class + """ + + def __init__(self, module, subspec="config", options="options"): + + self._module = module + self.argument_spec = AclsArgs.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_acl_data(self, connection): + # Get the access-lists from the ios router + return connection.get("sh access-list") + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for acls + :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_acl_data(connection) + + rmmod = NetworkTemplate(lines=data.splitlines(), tmplt=AclsTemplate()) + current = rmmod.parse() + + temp_v4 = [] + temp_v6 = [] + if current.get("acls"): + for k, v in iteritems(current.get("acls")): + if v.get("afi") == "ipv4": + del v["afi"] + temp_v4.append(v) + elif v.get("afi") == "ipv6": + del v["afi"] + temp_v6.append(v) + temp_v4 = sorted(temp_v4, key=lambda i: str(i["name"])) + temp_v6 = sorted(temp_v6, key=lambda i: str(i["name"])) + for each in temp_v4: + for each_ace in each.get("aces"): + if each["acl_type"] == "standard": + each_ace["source"] = each_ace.pop("std_source") + if each_ace.get("icmp_igmp_tcp_protocol"): + each_ace["protocol_options"] = { + each_ace["protocol"]: { + each_ace.pop("icmp_igmp_tcp_protocol").replace( + "-", "_" + ): True + } + } + if each_ace.get("std_source") == {}: + del each_ace["std_source"] + for each in temp_v6: + for each_ace in each.get("aces"): + if each_ace.get("std_source") == {}: + del each_ace["std_source"] + if each_ace.get("icmp_igmp_tcp_protocol"): + each_ace["protocol_options"] = { + each_ace["protocol"]: { + each_ace.pop("icmp_igmp_tcp_protocol").replace( + "-", "_" + ): True + } + } + + objs = [] + if temp_v4: + objs.append({"afi": "ipv4", "acls": temp_v4}) + if temp_v6: + objs.append({"afi": "ipv6", "acls": temp_v6}) + # objs['ipv6'] = {'acls': temp_v6} + facts = {} + if objs: + facts["acls"] = [] + params = utils.validate_config( + self.argument_spec, {"config": objs} + ) + for cfg in params["config"]: + facts["acls"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/bgp_global.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/bgp_global.py new file mode 100644 index 00000000..d10002c6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/bgp_global.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +""" +The cisco.ios 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. +""" + +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.bgp_global import ( + Bgp_globalTemplate, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.bgp_global.bgp_global import ( + Bgp_globalArgs, +) + + +class Bgp_globalFacts(object): + """ The cisco.ios bgp_global facts class + """ + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Bgp_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_bgp_global_data(self, connection): + return connection.get("sh running-config | section ^router 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 = {} + + if not data: + data = self.get_bgp_global_data(connection) + + # parse native config using the Bgp_global template + bgp_global_parser = Bgp_globalTemplate(lines=data.splitlines()) + objs = bgp_global_parser.parse() + + objs = utils.remove_empties(objs) + if "neighbor" in objs: + temp_neighbor = [] + temp = {} + temp["address"] = None + for each in objs["neighbor"]: + if temp["address"] != each["address"]: + if temp["address"]: + temp_neighbor.append(temp) + temp = {} + temp["address"] = each.pop("address") + if each: + temp.update(each) + else: + each.pop("address") + temp.update(each) + if temp: + temp_neighbor.append(temp) + objs["neighbor"] = temp_neighbor + if objs: + ansible_facts["ansible_network_resources"].pop("bgp_global", None) + + params = utils.remove_empties( + utils.validate_config(self.argument_spec, {"config": objs}) + ) + facts["bgp_global"] = params["config"] + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/facts.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/facts.py new file mode 100644 index 00000000..91c00404 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/facts.py @@ -0,0 +1,130 @@ +# +# -*- 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 facts class for ios +this file validates each subset of facts and selectively +calls the appropriate facts gathering function +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts import ( + FactsBase, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.interfaces.interfaces import ( + InterfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.l2_interfaces.l2_interfaces import ( + L2_InterfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vlans.vlans import ( + VlansFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.lag_interfaces.lag_interfaces import ( + Lag_interfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.lacp.lacp import ( + LacpFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.lacp_interfaces.lacp_interfaces import ( + Lacp_InterfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.lldp_global.lldp_global import ( + Lldp_globalFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.lldp_interfaces.lldp_interfaces import ( + Lldp_InterfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.l3_interfaces.l3_interfaces import ( + L3_InterfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.acl_interfaces.acl_interfaces import ( + Acl_InterfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.static_routes.static_routes import ( + Static_RoutesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.acls.acls import ( + AclsFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.ospfv2.ospfv2 import ( + Ospfv2Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.ospfv3.ospfv3 import ( + Ospfv3Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.ospf_interfaces.ospf_interfaces import ( + Ospf_InterfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.bgp_global.bgp_global import ( + Bgp_globalFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.legacy.base import ( + Default, + Hardware, + Interfaces, + Config, +) + + +FACT_LEGACY_SUBSETS = dict( + default=Default, hardware=Hardware, interfaces=Interfaces, config=Config +) + +FACT_RESOURCE_SUBSETS = dict( + interfaces=InterfacesFacts, + l2_interfaces=L2_InterfacesFacts, + vlans=VlansFacts, + lag_interfaces=Lag_interfacesFacts, + lacp=LacpFacts, + lacp_interfaces=Lacp_InterfacesFacts, + lldp_global=Lldp_globalFacts, + lldp_interfaces=Lldp_InterfacesFacts, + l3_interfaces=L3_InterfacesFacts, + acl_interfaces=Acl_InterfacesFacts, + static_routes=Static_RoutesFacts, + acls=AclsFacts, + ospfv2=Ospfv2Facts, + ospfv3=Ospfv3Facts, + ospf_interfaces=Ospf_InterfacesFacts, + bgp_global=Bgp_globalFacts, +) + + +class Facts(FactsBase): + """ The fact class for ios + """ + + 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 ios + :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/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/interfaces/interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/interfaces/interfaces.py new file mode 100644 index 00000000..098b3707 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/interfaces/interfaces.py @@ -0,0 +1,110 @@ +# +# -*- 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 ios 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 +import re +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + get_interface_type, + normalize_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.interfaces.interfaces import ( + InterfacesArgs, +) + + +class InterfacesFacts(object): + """ The ios 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_interfaces_data(self, connection): + return connection.get("sh running-config | section ^interface") + + 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 + """ + objs = [] + + if not data: + data = self.get_interfaces_data(connection) + # operate on a collection of resource x + config = ("\n" + data).split("\ninterface ") + for conf in config: + if conf: + obj = self.render_config(self.generated_spec, conf) + 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, spec, 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 = deepcopy(spec) + match = re.search(r"^(\S+)", conf) + intf = match.group(1) + + if get_interface_type(intf) == "unknown": + return {} + # populate the facts from the configuration + config["name"] = normalize_interface(intf) + config["description"] = utils.parse_conf_arg(conf, "description") + config["speed"] = utils.parse_conf_arg(conf, "speed") + if utils.parse_conf_arg(conf, "mtu"): + config["mtu"] = int(utils.parse_conf_arg(conf, "mtu")) + config["duplex"] = utils.parse_conf_arg(conf, "duplex") + enabled = utils.parse_conf_cmd_arg(conf, "shutdown", False) + config["enabled"] = enabled if enabled is not None else True + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l2_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l2_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l2_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l2_interfaces/l2_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l2_interfaces/l2_interfaces.py new file mode 100644 index 00000000..a507b2d1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l2_interfaces/l2_interfaces.py @@ -0,0 +1,138 @@ +# +# -*- 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 ios 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 +import re +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + get_interface_type, + normalize_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.l2_interfaces.l2_interfaces import ( + L2_InterfacesArgs, +) + + +class L2_InterfacesFacts(object): + """ The ios l2 interfaces fact class + """ + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = L2_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_l2_interfaces_data(self, connection): + return connection.get("show running-config | section ^interface") + + 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 + """ + objs = [] + if not data: + data = self.get_l2_interfaces_data(connection) + + # operate on a collection of resource x + config = ("\n" + data).split("\ninterface ") + for conf in config: + if conf: + obj = self.render_config(self.generated_spec, conf) + if obj: + objs.append(obj) + + facts = {} + if objs: + facts["l2_interfaces"] = [] + params = utils.validate_config( + self.argument_spec, {"config": objs} + ) + for cfg in params["config"]: + facts["l2_interfaces"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def render_config(self, spec, 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 = deepcopy(spec) + match = re.search(r"^(\S+)", conf) + intf = match.group(1) + + if get_interface_type(intf) == "unknown": + return {} + + if intf.upper()[:2] in ( + "HU", + "FO", + "TW", + "TE", + "GI", + "FA", + "ET", + "PO", + ): + # populate the facts from the configuration + config["name"] = normalize_interface(intf) + has_mode = utils.parse_conf_arg(conf, "switchport mode") + if has_mode: + config["mode"] = has_mode + has_access = utils.parse_conf_arg(conf, "switchport access vlan") + if has_access: + config["access"] = {"vlan": int(has_access)} + + has_voice = utils.parse_conf_arg(conf, "switchport voice vlan") + if has_voice: + config["voice"] = {"vlan": int(has_voice)} + + trunk = dict() + trunk["encapsulation"] = utils.parse_conf_arg( + conf, "switchport trunk encapsulation" + ) + native_vlan = utils.parse_conf_arg(conf, "native vlan") + if native_vlan: + trunk["native_vlan"] = int(native_vlan) + allowed_vlan = utils.parse_conf_arg(conf, "allowed vlan") + if allowed_vlan: + trunk["allowed_vlans"] = allowed_vlan.split(",") + pruning_vlan = utils.parse_conf_arg(conf, "pruning vlan") + if pruning_vlan: + trunk["pruning_vlans"] = pruning_vlan.split(",") + + config["trunk"] = trunk + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/l3_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/l3_interfaces.py new file mode 100644 index 00000000..8a709765 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/l3_interfaces.py @@ -0,0 +1,148 @@ +# +# -*- 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 ios_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 + + +from copy import deepcopy +import re +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + get_interface_type, + normalize_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.l3_interfaces.l3_interfaces import ( + L3_InterfacesArgs, +) + + +class L3_InterfacesFacts(object): + """ The ios 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 + """ + objs = [] + + if not data: + data = connection.get("show running-config | section ^interface") + # operate on a collection of resource x + config = ("\n" + data).split("\ninterface ") + for conf in config: + if conf: + obj = self.render_config(self.generated_spec, conf) + if obj: + objs.append(obj) + 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, spec, 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 = deepcopy(spec) + match = re.search(r"^(\S+)", conf) + intf = match.group(1) + + if get_interface_type(intf) == "unknown": + return {} + # populate the facts from the configuration + config["name"] = normalize_interface(intf) + + ipv4 = [] + ipv4_all = re.findall(r"ip address (\S+.*)", conf) + for each in ipv4_all: + each_ipv4 = dict() + if "secondary" not in each and "dhcp" not in each: + each_ipv4["address"] = each + elif "secondary" in each: + each_ipv4["address"] = each.split(" secondary")[0] + each_ipv4["secondary"] = True + elif "dhcp" in each: + each_ipv4["address"] = "dhcp" + if "client-id" in each: + try: + each_ipv4["dhcp_client"] = int( + each.split(" hostname ")[0].split("/")[-1] + ) + except ValueError: + obj = re.search("\\d+", each) + if obj: + dhcp_client = obj.group() + each_ipv4["dhcp_client"] = int(dhcp_client) + if "hostname" in each: + each_ipv4["dhcp_hostname"] = each.split(" hostname ")[-1] + if "client-id" in each and each_ipv4["dhcp_client"] is None: + try: + each_ipv4["dhcp_client"] = int(each.split("/")[-1]) + except ValueError: + obj = re.search("\\d+", each) + if obj: + dhcp_client = obj.group() + each_ipv4["dhcp_client"] = int(dhcp_client) + if "hostname" in each and not each_ipv4["dhcp_hostname"]: + each_ipv4["dhcp_hostname"] = each.split(" hostname ")[-1] + ipv4.append(each_ipv4) + config["ipv4"] = ipv4 + + # Get the configured IPV6 details + ipv6 = [] + ipv6_all = re.findall(r"ipv6 address (\S+)", conf) + for each in ipv6_all: + each_ipv6 = dict() + if "autoconfig" in each: + each_ipv6["autoconfig"] = True + if "dhcp" in each: + each_ipv6["dhcp"] = True + each_ipv6["address"] = each.lower() + ipv6.append(each_ipv6) + config["ipv6"] = ipv6 + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp/lacp.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp/lacp.py new file mode 100644 index 00000000..d67bd1bd --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp/lacp.py @@ -0,0 +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) +""" +The ios lacp 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 ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lacp.lacp import ( + LacpArgs, +) + + +class LacpFacts(object): + """ The ios lacp fact class + """ + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = LacpArgs.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 lacp + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if connection: + pass + + if not data: + data = connection.get("show lacp sys-id") + + obj = {} + if data: + lacp_obj = self.render_config(self.generated_spec, data) + if lacp_obj: + obj = lacp_obj + + ansible_facts["ansible_network_resources"].pop("lacp", None) + facts = {} + + params = utils.validate_config(self.argument_spec, {"config": obj}) + facts["lacp"] = utils.remove_empties(params["config"]) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def render_config(self, spec, 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 = deepcopy(spec) + + config["system"]["priority"] = int(conf.split(",")[0]) + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp_interfaces/lacp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 00000000..a1e4d6c4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,112 @@ +# +# -*- 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 ios_lacp_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.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + get_interface_type, + normalize_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lacp_interfaces.lacp_interfaces import ( + Lacp_InterfacesArgs, +) + + +class Lacp_InterfacesFacts(object): + """ The ios_lacp_interfaces fact class + """ + + def __init__(self, module, subspec="config", options="options"): + + self._module = module + self.argument_spec = Lacp_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 lacp_interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if connection: + pass + + objs = [] + if not data: + data = connection.get("show running-config | section ^interface") + # operate on a collection of resource x + config = ("\n" + data).split("\ninterface ") + + for conf in config: + if conf: + obj = self.render_config(self.generated_spec, conf) + if obj: + objs.append(obj) + facts = {} + + if objs: + facts["lacp_interfaces"] = [] + params = utils.validate_config( + self.argument_spec, {"config": objs} + ) + for cfg in params["config"]: + facts["lacp_interfaces"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def render_config(self, spec, 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 = deepcopy(spec) + match = re.search(r"^(\S+)", conf) + intf = match.group(1) + if get_interface_type(intf) == "unknown": + return {} + + config["name"] = normalize_interface(intf) + port_priority = utils.parse_conf_arg(conf, "lacp port-priority") + max_bundle = utils.parse_conf_arg(conf, "lacp max-bundle") + if port_priority: + config["port_priority"] = int(port_priority) + if "lacp fast-switchover" in conf: + config["fast_switchover"] = True + if max_bundle: + config["max_bundle"] = int(max_bundle) + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lag_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lag_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lag_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lag_interfaces/lag_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lag_interfaces/lag_interfaces.py new file mode 100644 index 00000000..364d4e61 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lag_interfaces/lag_interfaces.py @@ -0,0 +1,128 @@ +# +# -*- 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 ios 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 + +__metaclass__ = type + + +import re +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + get_interface_type, + normalize_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lag_interfaces.lag_interfaces import ( + Lag_interfacesArgs, +) + + +class Lag_interfacesFacts(object): + """ The ios_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 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 + """ + objs = [] + + if not data: + data = connection.get("show running-config | section ^interface") + # operate on a collection of resource x + config = ("\n" + data).split("\ninterface ") + for conf in config: + if conf: + obj = self.render_config(self.generated_spec, conf) + if obj: + if not obj.get("members"): + obj.update({"members": []}) + objs.append(obj) + + # for appending members configured with same channel-group + for each in range(len(objs)): + if each < (len(objs) - 1): + if objs[each]["name"] == objs[each + 1]["name"]: + objs[each]["members"].append(objs[each + 1]["members"][0]) + del objs[each + 1] + 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, spec, 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 = deepcopy(spec) + match = re.search(r"^(\S+)", conf) + intf = match.group(1) + + if get_interface_type(intf) == "unknown": + return {} + member_config = {} + channel_group = utils.parse_conf_arg(conf, "channel-group") + if intf.startswith("Gi"): + config["name"] = intf + config["members"] = [] + if channel_group: + channel_group = channel_group.split(" ") + id = channel_group[0] + config["name"] = "Port-channel{0}".format(str(id)) + if "mode" in channel_group: + mode = channel_group[2] + member_config.update({"mode": mode}) + if "link" in channel_group: + link = channel_group[2] + member_config.update({"link": link}) + if member_config.get("mode") or member_config.get("link"): + member_config["member"] = normalize_interface(intf) + config["members"].append(member_config) + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/legacy/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/legacy/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/legacy/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/legacy/base.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/legacy/base.py new file mode 100644 index 00000000..520a402f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/legacy/base.py @@ -0,0 +1,423 @@ +# +# -*- 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 ios legacy 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.cisco.ios.plugins.module_utils.network.ios.ios import ( + run_commands, + get_capabilities, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + normalize_interface, +) +from ansible.module_utils.six import iteritems +from ansible.module_utils.six.moves import zip + + +class FactsBase(object): + + COMMANDS = list() + + 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, commands=self.COMMANDS, check_rc=False + ) + + def run(self, cmd): + return run_commands(self.module, commands=cmd, check_rc=False) + + +class Default(FactsBase): + + COMMANDS = ["show version", "show virtual switch"] + + def populate(self): + super(Default, self).populate() + self.facts.update(self.platform_facts()) + data = self.responses[0] + if data: + self.facts["iostype"] = self.parse_iostype(data) + self.facts["serialnum"] = self.parse_serialnum(data) + self.parse_stacks(data) + data = self.responses[1] + vss_errs = ["Invalid input", "Switch Mode : Standalone"] + if data and not any(err in data for err in vss_errs): + self.parse_virtual_switch(data) + + def parse_iostype(self, data): + match = re.search(r"\sIOS-XE\s", data) + if match: + return "IOS-XE" + else: + return "IOS" + + def parse_serialnum(self, data): + match = re.search(r"board ID (\S+)", data) + if match: + return match.group(1) + + def parse_stacks(self, data): + match = re.findall(r"^Model [Nn]umber\s+: (\S+)", data, re.M) + if match: + self.facts["stacked_models"] = match + + match = re.findall( + r"^System [Ss]erial [Nn]umber\s+: (\S+)", data, re.M + ) + if match: + self.facts["stacked_serialnums"] = match + + if "stacked_models" in self.facts: + self.facts["virtual_switch"] = "STACK" + + def parse_virtual_switch(self, data): + match = re.search( + r"^Virtual switch domain number : ([0-9]+)", data, re.M + ) + if match: + self.facts["virtual_switch"] = "VSS" + self.facts["virtual_switch_domain"] = 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 Hardware(FactsBase): + + COMMANDS = ["dir", "show memory statistics"] + + def populate(self): + warnings = list() + super(Hardware, self).populate() + data = self.responses[0] + if data: + self.facts["filesystems"] = self.parse_filesystems(data) + self.facts["filesystems_info"] = self.parse_filesystems_info(data) + + data = self.responses[1] + if data: + if "Invalid input detected" in data: + warnings.append("Unable to gather memory statistics") + else: + processor_line = [ + l for l in data.splitlines() if "Processor" in l + ].pop() + match = re.findall(r"\s(\d+)\s", processor_line) + if match: + self.facts["memtotal_mb"] = int(match[0]) / 1024 + self.facts["memfree_mb"] = int(match[3]) / 1024 + + def parse_filesystems(self, data): + return re.findall(r"^Directory of (\S+)/", data, re.M) + + def parse_filesystems_info(self, data): + facts = dict() + fs = "" + for line in data.split("\n"): + match = re.match(r"^Directory of (\S+)/", line) + if match: + fs = match.group(1) + facts[fs] = dict() + continue + match = re.match(r"^(\d+) bytes total \((\d+) bytes free\)", line) + if match: + facts[fs]["spacetotal_kb"] = int(match.group(1)) / 1024 + facts[fs]["spacefree_kb"] = int(match.group(2)) / 1024 + return facts + + +class Config(FactsBase): + + COMMANDS = ["show running-config"] + + def populate(self): + super(Config, self).populate() + data = self.responses[0] + if data: + data = re.sub( + r"^Building configuration...\s+Current configuration : \d+ bytes\n", + "", + data, + flags=re.MULTILINE, + ) + self.facts["config"] = data + + +class Interfaces(FactsBase): + + COMMANDS = [ + "show interfaces", + "show ip interface", + "show ipv6 interface", + "show lldp", + "show cdp", + ] + + def populate(self): + super(Interfaces, self).populate() + + self.facts["all_ipv4_addresses"] = list() + self.facts["all_ipv6_addresses"] = list() + self.facts["neighbors"] = {} + + data = self.responses[0] + if data: + interfaces = self.parse_interfaces(data) + self.facts["interfaces"] = self.populate_interfaces(interfaces) + + data = self.responses[1] + if data: + data = self.parse_interfaces(data) + self.populate_ipv4_interfaces(data) + + data = self.responses[2] + if data: + data = self.parse_interfaces(data) + self.populate_ipv6_interfaces(data) + + data = self.responses[3] + lldp_errs = ["Invalid input", "LLDP is not enabled"] + + if data and not any(err in data for err in lldp_errs): + neighbors = self.run(["show lldp neighbors detail"]) + if neighbors: + self.facts["neighbors"].update( + self.parse_neighbors(neighbors[0]) + ) + + data = self.responses[4] + cdp_errs = ["CDP is not enabled"] + + if data and not any(err in data for err in cdp_errs): + cdp_neighbors = self.run(["show cdp neighbors detail"]) + if cdp_neighbors: + self.facts["neighbors"].update( + self.parse_cdp_neighbors(cdp_neighbors[0]) + ) + + def populate_interfaces(self, interfaces): + facts = dict() + for key, value in iteritems(interfaces): + intf = dict() + intf["description"] = self.parse_description(value) + intf["macaddress"] = self.parse_macaddress(value) + + intf["mtu"] = self.parse_mtu(value) + intf["bandwidth"] = self.parse_bandwidth(value) + intf["mediatype"] = self.parse_mediatype(value) + intf["duplex"] = self.parse_duplex(value) + intf["lineprotocol"] = self.parse_lineprotocol(value) + intf["operstatus"] = self.parse_operstatus(value) + intf["type"] = self.parse_type(value) + + facts[key] = intf + return facts + + def populate_ipv4_interfaces(self, data): + for key, value in data.items(): + self.facts["interfaces"][key]["ipv4"] = list() + primary_address = addresses = [] + primary_address = re.findall( + r"Internet address is (.+)$", value, re.M + ) + addresses = re.findall(r"Secondary address (.+)$", value, re.M) + if len(primary_address) == 0: + continue + addresses.append(primary_address[0]) + for address in addresses: + addr, subnet = address.split("/") + ipv4 = dict(address=addr.strip(), subnet=subnet.strip()) + self.add_ip_address(addr.strip(), "ipv4") + self.facts["interfaces"][key]["ipv4"].append(ipv4) + + def populate_ipv6_interfaces(self, data): + for key, value in iteritems(data): + try: + self.facts["interfaces"][key]["ipv6"] = list() + except KeyError: + self.facts["interfaces"][key] = dict() + self.facts["interfaces"][key]["ipv6"] = list() + addresses = re.findall(r"\s+(.+), subnet", value, re.M) + subnets = re.findall(r", subnet is (.+)$", value, re.M) + for addr, subnet in zip(addresses, subnets): + ipv6 = dict(address=addr.strip(), subnet=subnet.strip()) + self.add_ip_address(addr.strip(), "ipv6") + self.facts["interfaces"][key]["ipv6"].append(ipv6) + + def add_ip_address(self, address, family): + if family == "ipv4": + self.facts["all_ipv4_addresses"].append(address) + else: + self.facts["all_ipv6_addresses"].append(address) + + def parse_neighbors(self, neighbors): + facts = dict() + for entry in neighbors.split( + "------------------------------------------------" + ): + if entry == "": + continue + intf = self.parse_lldp_intf(entry) + if intf is None: + return facts + intf = normalize_interface(intf) + if intf not in facts: + facts[intf] = list() + fact = dict() + fact["host"] = self.parse_lldp_host(entry) + fact["port"] = self.parse_lldp_port(entry) + facts[intf].append(fact) + return facts + + def parse_cdp_neighbors(self, neighbors): + facts = dict() + for entry in neighbors.split("-------------------------"): + if entry == "": + continue + intf_port = self.parse_cdp_intf_port(entry) + if intf_port is None: + return facts + intf, port = intf_port + if intf not in facts: + facts[intf] = list() + fact = dict() + fact["host"] = self.parse_cdp_host(entry) + fact["platform"] = self.parse_cdp_platform(entry) + fact["port"] = port + facts[intf].append(fact) + return facts + + def parse_interfaces(self, data): + parsed = dict() + key = "" + for line in data.split("\n"): + if len(line) == 0: + continue + if line[0] == " ": + parsed[key] += "\n%s" % line + else: + match = re.match(r"^(\S+)", line) + if match: + key = match.group(1) + parsed[key] = line + return parsed + + def parse_description(self, data): + match = re.search(r"Description: (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_macaddress(self, data): + match = re.search(r"Hardware is (?:.*), address is (\S+)", data) + if match: + return match.group(1) + + def parse_ipv4(self, data): + match = re.search(r"Internet address is (\S+)", data) + if match: + addr, masklen = match.group(1).split("/") + return dict(address=addr, masklen=int(masklen)) + + def parse_mtu(self, data): + match = re.search(r"MTU (\d+)", data) + if match: + return int(match.group(1)) + + def parse_bandwidth(self, data): + match = re.search(r"BW (\d+)", data) + if match: + return int(match.group(1)) + + def parse_duplex(self, data): + match = re.search(r"(\w+) Duplex", data, re.M) + if match: + return match.group(1) + + def parse_mediatype(self, data): + match = re.search(r"media type is (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_type(self, data): + match = re.search(r"Hardware is (.+),", data, re.M) + if match: + return match.group(1) + + def parse_lineprotocol(self, data): + match = re.search(r"line protocol is (\S+)\s*$", data, re.M) + if match: + return match.group(1) + + def parse_operstatus(self, data): + match = re.search(r"^(?:.+) is (.+),", data, re.M) + if match: + return match.group(1) + + def parse_lldp_intf(self, data): + match = re.search(r"^Local Intf: (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_lldp_host(self, data): + match = re.search(r"System Name: (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_lldp_port(self, data): + match = re.search(r"Port id: (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_cdp_intf_port(self, data): + match = re.search( + r"^Interface: (.+), Port ID \(outgoing port\): (.+)$", data, re.M + ) + if match: + return match.group(1), match.group(2) + + def parse_cdp_host(self, data): + match = re.search(r"^Device ID: (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_cdp_platform(self, data): + match = re.search(r"^Platform: (.+),", data, re.M) + if match: + return match.group(1) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_global/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_global/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_global/lldp_global.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_global/lldp_global.py new file mode 100644 index 00000000..4c8ff3c1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_global/lldp_global.py @@ -0,0 +1,100 @@ +# +# -*- 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 ios 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 + +__metaclass__ = type + + +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lldp_global.lldp_global import ( + Lldp_globalArgs, +) + + +class Lldp_globalFacts(object): + """ The ios 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_lldp_global_data(self, connection): + return connection.get("show running-config | section ^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 + """ + objs = dict() + if not data: + data = self.get_lldp_global_data(connection) + # operate on a collection of resource x + config = data.split("\n") + for conf in config: + if conf: + obj = self.render_config(self.generated_spec, conf) + if obj: + objs.update(obj) + facts = {} + + if objs: + params = utils.validate_config( + self.argument_spec, {"config": utils.remove_empties(objs)} + ) + facts["lldp_global"] = utils.remove_empties(params["config"]) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def render_config(self, spec, 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 = deepcopy(spec) + + holdtime = utils.parse_conf_arg(conf, "lldp holdtime") + timer = utils.parse_conf_arg(conf, "lldp timer") + reinit = utils.parse_conf_arg(conf, "lldp reinit") + if holdtime: + config["holdtime"] = int(holdtime) + if "lldp run" in conf: + config["enabled"] = True + if timer: + config["timer"] = int(timer) + if reinit: + config["reinit"] = int(reinit) + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_interfaces/lldp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_interfaces/lldp_interfaces.py new file mode 100644 index 00000000..11a67238 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_interfaces/lldp_interfaces.py @@ -0,0 +1,115 @@ +# +# -*- 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 ios_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 + + +import re +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + get_interface_type, + normalize_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lldp_interfaces.lldp_interfaces import ( + Lldp_InterfacesArgs, +) + + +class Lldp_InterfacesFacts(object): + """ The ios_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 + """ + + objs = [] + if not data: + data = connection.get("show lldp interface") + # operate on a collection of resource x + config = data.split("\n\n") + for conf in config: + if conf: + obj = self.render_config(self.generated_spec, conf) + if obj: + objs.append(obj) + facts = {} + + if objs: + facts["lldp_interfaces"] = [] + params = utils.validate_config( + self.argument_spec, {"config": objs} + ) + for cfg in params["config"]: + facts["lldp_interfaces"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def render_config(self, spec, 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 = deepcopy(spec) + match = re.search(r"^(\S+)(:)", conf) + intf = "" + if match: + intf = match.group(1) + + if get_interface_type(intf) == "unknown": + return {} + if intf.lower().startswith("gi"): + config["name"] = normalize_interface(intf) + receive = utils.parse_conf_arg(conf, "Rx:") + transmit = utils.parse_conf_arg(conf, "Tx:") + + if receive == "enabled": + config["receive"] = True + elif receive == "disabled": + config["receive"] = False + if transmit == "enabled": + config["transmit"] = True + elif transmit == "disabled": + config["transmit"] = False + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospf_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospf_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospf_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospf_interfaces/ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 00000000..dcf96605 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,95 @@ +# -*- 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 cisco.ios 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. +""" + +from copy import deepcopy + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ospf_interfaces import ( + Ospf_InterfacesTemplate, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ospf_interfaces.ospf_interfaces import ( + Ospf_InterfacesArgs, +) + + +class Ospf_InterfacesFacts(object): + """ The cisco.ios ospf_interfaces facts class + """ + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Ospf_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_ospf_interfaces_data(self, connection): + return connection.get("sh running-config | section ^interface") + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for Ospf_interfaces network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + + if not data: + data = self.get_ospf_interfaces_data(connection) + + # parse native config using the Ospf_interfaces template + ospf_interfaces_parser = Ospf_InterfacesTemplate( + lines=data.splitlines() + ) + + objs = ospf_interfaces_parser.parse() + final_objs = [] + for key, value in iteritems(objs): + temp_af = [] + if value["address_family"].get("ip"): + temp_af.append(value["address_family"].get("ip")) + if value["address_family"].get("ipv6"): + temp_af.append(value["address_family"].get("ipv6")) + if temp_af: + value["address_family"] = temp_af + if value: + value = utils.remove_empties(value) + final_objs.append(value) + ansible_facts["ansible_network_resources"].pop("ospf_interfaces", None) + + params = utils.remove_empties( + utils.validate_config(self.argument_spec, {"config": final_objs}) + ) + + facts["ospf_interfaces"] = params["config"] + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv2/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv2/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv2/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv2/ospfv2.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv2/ospfv2.py new file mode 100644 index 00000000..7c67e3b7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv2/ospfv2.py @@ -0,0 +1,99 @@ +# -*- 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) +""" +The ios ospfv2 fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ospfv2.ospfv2 import ( + Ospfv2Args, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ospfv2 import ( + Ospfv2Template, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import ( + NetworkTemplate, +) + + +class Ospfv2Facts(object): + """ The ios 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_ospfv2_data(self, connection): + return connection.get("sh running-config | section ^router ospf") + + 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_ospfv2_data(connection) + + ipv4 = {"processes": []} + rmmod = NetworkTemplate( + lines=data.splitlines(), tmplt=Ospfv2Template() + ) + current = rmmod.parse() + + # convert some of the dicts to lists + for key, sortv in [("processes", "process_id")]: + if key in current and current[key]: + current[key] = current[key].values() + current[key] = sorted( + current[key], key=lambda k, sk=sortv: k[sk] + ) + + for process in current.get("processes", []): + if "areas" in process: + process["areas"] = list(process["areas"].values()) + process["areas"] = sorted( + process["areas"], key=lambda k, sk="area_id": k[sk] + ) + for area in process["areas"]: + if "filters" in area: + area["filters"].sort() + ipv4["processes"].append(process) + + ansible_facts["ansible_network_resources"].pop("ospfv2", None) + facts = {} + if current: + params = utils.validate_config( + self.argument_spec, {"config": ipv4} + ) + params = utils.remove_empties(params) + + facts["ospfv2"] = params["config"] + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv3/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv3/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv3/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv3/ospfv3.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv3/ospfv3.py new file mode 100644 index 00000000..50d3f32f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv3/ospfv3.py @@ -0,0 +1,180 @@ +# -*- 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) +""" +The ios 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 + +__metaclass__ = type + +import re +from copy import deepcopy +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ospfv3.ospfv3 import ( + Ospfv3Args, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ospfv3 import ( + Ospfv3Template, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import ( + NetworkTemplate, +) + + +class Ospfv3Facts(object): + """ The ios ospfv3 fact class + """ + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Ospfv3Args.argument_spec + + def get_ospfv3_data(self, connection): + return connection.get("sh running-config | section ^router ospfv3") + + def parse(self, net_template_obj): + """ Overrided network template parse + """ + result = {} + shared = {} + temp_pid = None + for line in net_template_obj._lines: + for parser in net_template_obj._tmplt.PARSERS: + cap = re.match(parser["getval"], line) + if cap: + capdict = cap.groupdict() + + capdict = { + k: v for k, v in iteritems(capdict) if v is not None + } + if "address-family" in line: + capdict.update({"id": temp_pid}) + if ( + "manet" in line + and "pid" not in shared + and shared.get("unicast") + ): + del shared["unicast"] + + if "router ospfv3" in line: + temp_pid = None + if parser.get("shared"): + shared = capdict + if not temp_pid and ( + shared.get("pid") or shared.get("id") + ): + temp_pid = shared.get("pid") or shared.get("id") + vals = utils.dict_merge(capdict, shared) + try: + res = net_template_obj._deepformat( + deepcopy(parser["result"]), vals + ) + except Exception: + continue + result = utils.dict_merge(result, res) + break + return result + + def parse_for_address_family(self, current): + """ Parsing and Fishing out address family contents + """ + pid_addr_family_dict = {} + temp_dict = {} + temp_pid = None + temp = [] + if current.get("address_family"): + for each in current.pop("address_family"): + each = utils.remove_empties(each) + if each.get("exit"): + if temp_pid == each.get("exit")["pid"]: + temp.append(temp_dict) + pid_addr_family_dict[temp_pid] = temp + temp_dict = dict() + else: + temp_pid = each.get("exit")["pid"] + pid_addr_family_dict[temp_pid] = [temp_dict] + temp = [] + temp.append(temp_dict) + temp_dict = dict() + elif each.get("manet") and temp_dict.get("manet"): + for k, v in iteritems(each.get("manet")): + if k in temp_dict.get("manet"): + temp_dict.get("manet")[k].update(v) + else: + temp_dict["manet"].update(each.get("manet")) + elif each.get("manet") and not temp_dict.get("manet"): + temp_dict["manet"] = each.get("manet") + else: + temp_dict.update(each) + return pid_addr_family_dict + + 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_ospfv3_data(connection) + + ipv4 = {"processes": []} + rmmod = NetworkTemplate( + lines=data.splitlines(), tmplt=Ospfv3Template() + ) + current = self.parse(rmmod) + address_family = self.parse_for_address_family(current) + if address_family: + for k, v in iteritems(current["processes"]): + temp = address_family.pop(k) + v.update({"address_family": temp}) + # convert some of the dicts to lists + for key, sortv in [("processes", "process_id")]: + if key in current and current[key]: + current[key] = current[key].values() + current[key] = sorted( + current[key], key=lambda k, sk=sortv: k[sk] + ) + + for process in current.get("processes", []): + if "areas" in process: + process["areas"] = list(process["areas"].values()) + process["areas"] = sorted( + process["areas"], key=lambda k, sk="area_id": k[sk] + ) + for area in process["areas"]: + if "filters" in area: + area["filters"].sort() + if "address_family" in process: + for each in process["address_family"]: + if "areas" in each: + each["areas"] = list(each["areas"].values()) + each["areas"] = sorted( + each["areas"], key=lambda k, sk="area_id": k[sk] + ) + for area in each["areas"]: + if "filters" in area: + area["filters"].sort() + ipv4["processes"].append(process) + + ansible_facts["ansible_network_resources"].pop("ospfv3", None) + facts = {} + if current: + params = utils.validate_config( + self.argument_spec, {"config": ipv4} + ) + params = utils.remove_empties(params) + + facts["ospfv3"] = params["config"] + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/static_routes/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/static_routes/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/static_routes/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/static_routes/static_routes.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/static_routes/static_routes.py new file mode 100644 index 00000000..09805ef0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/static_routes/static_routes.py @@ -0,0 +1,272 @@ +# +# -*- 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 ios_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 ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + netmask_to_cidr, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.static_routes.static_routes import ( + Static_RoutesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + is_valid_ip, +) + + +class Static_RoutesFacts(object): + """ The ios_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_static_routes_data(self, connection): + return connection.get( + "sh running-config | section ^ip route|ipv6 route" + ) + + 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 + """ + + objs = [] + if not data: + data = self.get_static_routes_data(connection) + # operate on a collection of resource x + config = data.split("\n") + + same_dest = self.populate_destination(config) + for key in same_dest.keys(): + if key: + obj = self.render_config( + self.generated_spec, key, same_dest[key] + ) + if obj: + objs.append(obj) + facts = {} + + # append all static routes address_family with NO VRF together + no_vrf_address_family = { + "address_families": [ + each.get("address_families")[0] + for each in objs + if each.get("vrf") is None + ] + } + + temp_objs = [each for each in objs if each.get("vrf") is not None] + temp_objs.append(no_vrf_address_family) + objs = temp_objs + + 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 update_netmask_to_cidr(self, filter, pos, del_pos): + netmask = filter.split(" ") + dest = netmask[pos] + "/" + netmask_to_cidr(netmask[del_pos]) + netmask[pos] = dest + del netmask[del_pos] + filter_vrf = " " + return filter_vrf.join(netmask), dest + + def populate_destination(self, config): + same_dest = {} + ip_str = "" + for i in sorted(config): + if i and "ospf" not in i: + if "::" in i and "vrf" in i: + ip_str = "ipv6 route vrf" + elif "::" in i and "vrf" not in i: + ip_str = "ipv6 route" + elif "." in i and "vrf" in i: + ip_str = "ip route vrf" + elif "." in i and "vrf" not in i: + ip_str = "ip route" + + if "vrf" in i: + filter_vrf = utils.parse_conf_arg(i, ip_str) + if "::" not in filter_vrf: + filter_vrf, dest_vrf = self.update_netmask_to_cidr( + filter_vrf, 1, 2 + ) + dest_vrf = dest_vrf + "_vrf" + else: + dest_vrf = filter_vrf.split(" ")[1] + if dest_vrf not in same_dest.keys(): + same_dest[dest_vrf] = [] + same_dest[dest_vrf].append("vrf " + filter_vrf) + elif "vrf" not in same_dest[dest_vrf][0]: + same_dest[dest_vrf] = [] + same_dest[dest_vrf].append("vrf " + filter_vrf) + else: + same_dest[dest_vrf].append(("vrf " + filter_vrf)) + else: + filter_non_vrf = utils.parse_conf_arg(i, ip_str) + if ( + "::" not in filter_non_vrf + ): # "/" not in filter_non_vrf and + filter_non_vrf, dest = self.update_netmask_to_cidr( + filter_non_vrf, 0, 1 + ) + else: + dest = filter_non_vrf.split(" ")[0] + if dest not in same_dest.keys(): + same_dest[dest] = [] + same_dest[dest].append(filter_non_vrf) + elif "vrf" in same_dest[dest][0]: + same_dest[dest] = [] + same_dest[dest].append(filter_non_vrf) + else: + same_dest[dest].append(filter_non_vrf) + return same_dest + + def render_config(self, spec, conf, conf_val): + """ + 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 = deepcopy(spec) + config["address_families"] = [] + route_dict = dict() + final_route = dict() + afi = dict() + final_route["routes"] = [] + next_hops = [] + hops = {} + vrf = "" + address_family = dict() + for each in conf_val: + route = each.split(" ") + if "vrf" in conf_val[0]: + vrf = route[route.index("vrf") + 1] + route_dict["dest"] = conf.split("_")[0] + else: + route_dict["dest"] = conf + if "vrf" in conf_val[0]: + hops = {} + if "::" in conf: + hops["forward_router_address"] = route[3] + afi["afi"] = "ipv6" + elif "." in conf: + if is_valid_ip(route[3]): + hops["forward_router_address"] = route[3] + afi["afi"] = "ipv4" + else: + hops["interface"] = route[3] + afi["afi"] = "ipv4" + if is_valid_ip(route[4]): + hops["forward_router_address"] = route[4] + else: + + if "::" in conf: + if is_valid_ip(route[1]): + hops["forward_router_address"] = route[1] + afi["afi"] = "ipv6" + else: + hops["interface"] = route[1] + afi["afi"] = "ipv6" + if is_valid_ip(route[2]): + hops["forward_router_address"] = route[2] + elif "." in conf: + if is_valid_ip(route[1]): + hops["forward_router_address"] = route[1] + afi["afi"] = "ipv4" + else: + hops["interface"] = route[1] + afi["afi"] = "ipv4" + if is_valid_ip(route[2]): + hops["forward_router_address"] = route[2] + try: + temp_list = each.split(" ") + if "tag" in temp_list: + del temp_list[temp_list.index("tag") + 1] + if "track" in temp_list: + del temp_list[temp_list.index("track") + 1] + # find distance metric + dist_metrics = int( + [ + i + for i in temp_list + if "." not in i + and ":" not in i + and ord(i[0]) > 48 + and ord(i[0]) < 57 + ][0] + ) + except IndexError: + dist_metrics = None + if dist_metrics: + hops["distance_metric"] = dist_metrics + if "name" in route: + hops["name"] = route[route.index("name") + 1] + if "multicast" in route: + hops["multicast"] = True + if "dhcp" in route: + hops["dhcp"] = True + if "global" in route: + hops["global"] = True + if "permanent" in route: + hops["permanent"] = True + if "tag" in route: + hops["tag"] = route[route.index("tag") + 1] + if "track" in route: + hops["track"] = route[route.index("track") + 1] + next_hops.append(hops) + hops = {} + route_dict["next_hops"] = next_hops + if route_dict: + final_route["routes"].append(route_dict) + address_family.update(afi) + address_family.update(final_route) + config["address_families"].append(address_family) + if vrf: + config["vrf"] = vrf + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/vlans.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/vlans.py new file mode 100644 index 00000000..d2959182 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/vlans.py @@ -0,0 +1,168 @@ +# +# -*- 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 ios vlans 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 ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vlans.vlans import ( + VlansArgs, +) + + +class VlansFacts(object): + """ The ios vlans fact class + """ + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = VlansArgs.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_vlans_data(self, connection): + return connection.get("show vlan") + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for vlans + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + + objs = [] + mtu_objs = [] + remote_objs = [] + final_objs = [] + if not data: + data = self.get_vlans_data(connection) + # operate on a collection of resource x + config = data.split("\n") + # Get individual vlan configs separately + vlan_info = "" + temp = "" + vlan_name = True + for conf in config: + if len(list(filter(None, conf.split(" ")))) <= 2 and vlan_name: + temp = temp + conf + if len(list(filter(None, temp.split(" ")))) <= 2: + continue + if "VLAN Name" in conf: + vlan_info = "Name" + elif "VLAN Type" in conf: + vlan_info = "Type" + vlan_name = False + elif "Remote SPAN" in conf: + vlan_info = "Remote" + vlan_name = False + elif "VLAN AREHops" in conf or "STEHops" in conf: + vlan_info = "Hops" + vlan_name = False + if temp: + conf = temp + temp = "" + if conf and " " not in filter(None, conf.split("-")): + obj = self.render_config(self.generated_spec, conf, vlan_info) + if "mtu" in obj: + mtu_objs.append(obj) + elif "remote_span" in obj: + remote_objs = obj + elif obj: + objs.append(obj) + # Appending MTU value to the retrieved dictionary + for o, m in zip(objs, mtu_objs): + o.update(m) + final_objs.append(o) + + # Appending Remote Span value to related VLAN: + if remote_objs: + if remote_objs.get("remote_span"): + for each in remote_objs.get("remote_span"): + for every in final_objs: + if each == every.get("vlan_id"): + every.update({"remote_span": True}) + break + facts = {} + if final_objs: + facts["vlans"] = [] + params = utils.validate_config( + self.argument_spec, {"config": objs} + ) + + for cfg in params["config"]: + facts["vlans"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def render_config(self, spec, conf, vlan_info): + """ + 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 = deepcopy(spec) + + if vlan_info == "Name" and "VLAN Name" not in conf: + conf = list(filter(None, conf.split(" "))) + config["vlan_id"] = int(conf[0]) + config["name"] = conf[1] + try: + if len(conf[2].split("/")) > 1: + if conf[2].split("/")[0] == "sus": + config["state"] = "suspend" + elif conf[2].split("/")[0] == "act": + config["state"] = "active" + config["shutdown"] = "enabled" + else: + if conf[2] == "suspended": + config["state"] = "suspend" + elif conf[2] == "active": + config["state"] = "active" + config["shutdown"] = "disabled" + except IndexError: + pass + elif vlan_info == "Type" and "VLAN Type" not in conf: + conf = list(filter(None, conf.split(" "))) + config["mtu"] = int(conf[3]) + elif vlan_info == "Remote": + if len(conf.split(",")) > 1 or conf.isdigit(): + remote_span_vlan = [] + if len(conf.split(",")) > 1: + remote_span_vlan = conf.split(",") + else: + remote_span_vlan.append(conf) + remote_span = [] + for each in remote_span_vlan: + remote_span.append(int(each)) + config["remote_span"] = remote_span + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/ios.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/ios.py new file mode 100644 index 00000000..18f87a09 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/ios.py @@ -0,0 +1,205 @@ +# 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.basic import env_fallback +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible.module_utils.connection import Connection, ConnectionError + +_DEVICE_CONFIGS = {} + +ios_provider_spec = { + "host": dict(), + "port": dict(type="int"), + "username": dict(fallback=(env_fallback, ["ANSIBLE_NET_USERNAME"])), + "password": dict( + fallback=(env_fallback, ["ANSIBLE_NET_PASSWORD"]), no_log=True + ), + "ssh_keyfile": dict( + fallback=(env_fallback, ["ANSIBLE_NET_SSH_KEYFILE"]), type="path" + ), + "authorize": dict( + default=False, + fallback=(env_fallback, ["ANSIBLE_NET_AUTHORIZE"]), + type="bool", + ), + "auth_pass": dict( + fallback=(env_fallback, ["ANSIBLE_NET_AUTH_PASS"]), no_log=True + ), + "timeout": dict(type="int"), +} +ios_argument_spec = { + "provider": dict( + type="dict", + options=ios_provider_spec, + removed_at_date="2022-06-01", + removed_from_collection="cisco.ios", + ) +} + + +def get_provider_argspec(): + return ios_provider_spec + + +def get_connection(module): + if hasattr(module, "_ios_connection"): + return module._ios_connection + + capabilities = get_capabilities(module) + network_api = capabilities.get("network_api") + if network_api == "cliconf": + module._ios_connection = Connection(module._socket_path) + else: + module.fail_json(msg="Invalid connection type %s" % network_api) + + return module._ios_connection + + +def get_capabilities(module): + if hasattr(module, "_ios_capabilities"): + return module._ios_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._ios_capabilities = json.loads(capabilities) + return module._ios_capabilities + + +def get_defaults_flag(module): + connection = get_connection(module) + try: + out = connection.get_defaults_flag() + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + return to_text(out, errors="surrogate_then_replace").strip() + + +def get_config(module, flags=None): + flags = to_list(flags) + + section_filter = False + if flags and "section" in flags[-1]: + section_filter = True + + flag_str = " ".join(flags) + + try: + return _DEVICE_CONFIGS[flag_str] + except KeyError: + connection = get_connection(module) + try: + out = connection.get_config(flags=flags) + except ConnectionError as exc: + if section_filter: + # Some ios devices don't understand `| section foo` + out = get_config(module, flags=flags[:-1]) + else: + module.fail_json( + msg=to_text(exc, errors="surrogate_then_replace") + ) + cfg = to_text(out, errors="surrogate_then_replace").strip() + _DEVICE_CONFIGS[flag_str] = cfg + return cfg + + +def run_commands(module, commands, check_rc=True): + connection = get_connection(module) + try: + return connection.run_commands(commands=commands, check_rc=check_rc) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc)) + + +def load_config(module, commands): + connection = get_connection(module) + + try: + resp = connection.edit_config(commands) + return resp.get("response") + except ConnectionError as exc: + module.fail_json(msg=to_text(exc)) + + +def normalize_interface(name): + """Return the normalized interface name + """ + if not name: + return + + def _get_number(name): + digits = "" + for char in name: + if char.isdigit() or char in "/.": + digits += char + return digits + + if name.lower().startswith("gi"): + if_type = "GigabitEthernet" + elif name.lower().startswith("te"): + if_type = "TenGigabitEthernet" + elif name.lower().startswith("fa"): + if_type = "FastEthernet" + elif name.lower().startswith("fo"): + if_type = "FortyGigabitEthernet" + elif name.lower().startswith("et"): + if_type = "Ethernet" + elif name.lower().startswith("vl"): + if_type = "Vlan" + elif name.lower().startswith("lo"): + if_type = "loopback" + elif name.lower().startswith("po"): + if_type = "port-channel" + elif name.lower().startswith("nv"): + if_type = "nve" + elif name.lower().startswith("twe"): + if_type = "TwentyFiveGigE" + elif name.lower().startswith("hu"): + if_type = "HundredGigE" + else: + if_type = None + + number_list = name.split(" ") + if len(number_list) == 2: + if_number = number_list[-1].strip() + else: + if_number = _get_number(name) + + if if_type: + proper_interface = if_type + if_number + else: + proper_interface = name + + return proper_interface diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/base.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/base.py new file mode 100644 index 00000000..848542bb --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/base.py @@ -0,0 +1,84 @@ +# +# (c) 2019, Ansible by Red Hat, inc +# 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 +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( + NetworkConfig, +) + + +class ConfigBase(object): + + argument_spec = {} + + mutually_exclusive = [] + + identifier = () + + def __init__(self, **kwargs): + self.values = {} + self._rendered_configuration = {} + self.active_configuration = None + + for item in self.identifier: + self.values[item] = kwargs.pop(item) + + for key, value in iteritems(kwargs): + if key in self.argument_spec: + setattr(self, key, value) + + for key, value in iteritems(self.argument_spec): + if value.get("default"): + if not getattr(self, key, None): + setattr(self, key, value.get("default")) + + def __getattr__(self, key): + if key in self.argument_spec: + return self.values.get(key) + + def __setattr__(self, key, value): + if key in self.argument_spec: + if key in self.identifier: + raise TypeError("cannot set value") + elif value is not None: + self.values[key] = value + else: + super(ConfigBase, self).__setattr__(key, value) + + def context_config(self, cmd): + if "context" not in self._rendered_configuration: + self._rendered_configuration["context"] = list() + self._rendered_configuration["context"].extend(to_list(cmd)) + + def global_config(self, cmd): + if "global" not in self._rendered_configuration: + self._rendered_configuration["global"] = list() + self._rendered_configuration["global"].extend(to_list(cmd)) + + def get_rendered_configuration(self): + config = list() + for section in ("context", "global"): + config.extend(self._rendered_configuration.get(section, [])) + return config + + def set_active_configuration(self, config): + self.active_configuration = config + + def render(self, config=None): + raise NotImplementedError + + def get_section(self, config, section): + if config is not None: + netcfg = NetworkConfig(indent=1, contents=config) + try: + config = netcfg.get_block_config(to_list(section)) + except ValueError: + config = None + return config diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/address_family.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/address_family.py new file mode 100644 index 00000000..0e0ce1ab --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/address_family.py @@ -0,0 +1,158 @@ +# +# (c) 2019, Ansible by Red Hat, inc +# 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 +import re + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.providers import ( + CliProvider, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.cli.config.bgp.neighbors import ( + AFNeighbors, +) +from ansible.module_utils.common.network import to_netmask + + +class AddressFamily(CliProvider): + def render(self, config=None): + commands = list() + safe_list = list() + + router_context = "router bgp %s" % self.get_value("config.bgp_as") + context_config = None + + for item in self.get_value("config.address_family"): + context = "address-family %s" % item["afi"] + if item["safi"] != "unicast": + context += " %s" % item["safi"] + context_commands = list() + + if config: + context_path = [router_context, context] + context_config = self.get_config_context( + config, context_path, indent=1 + ) + + for key, value in iteritems(item): + if value is not None: + meth = getattr(self, "_render_%s" % key, None) + if meth: + resp = meth(item, context_config) + if resp: + context_commands.extend(to_list(resp)) + + if context_commands: + commands.append(context) + commands.extend(context_commands) + commands.append("exit-address-family") + + safe_list.append(context) + + if self.params["operation"] == "replace": + if config: + resp = self._negate_config(config, safe_list) + commands.extend(resp) + + return commands + + def _negate_config(self, config, safe_list=None): + commands = list() + matches = re.findall(r"(address-family .+)$", config, re.M) + for item in set(matches).difference(safe_list): + commands.append("no %s" % item) + return commands + + def _render_auto_summary(self, item, config=None): + cmd = "auto-summary" + if item["auto_summary"] is False: + cmd = "no %s" % cmd + if not config or cmd not in config: + return cmd + + def _render_synchronization(self, item, config=None): + cmd = "synchronization" + if item["synchronization"] is False: + cmd = "no %s" % cmd + if not config or cmd not in config: + return cmd + + def _render_networks(self, item, config=None): + commands = list() + safe_list = list() + + for entry in item["networks"]: + network = entry["prefix"] + cmd = "network %s" % network + if entry["masklen"]: + cmd += " mask %s" % to_netmask(entry["masklen"]) + network += " mask %s" % to_netmask(entry["masklen"]) + if entry["route_map"]: + cmd += " route-map %s" % entry["route_map"] + network += " route-map %s" % entry["route_map"] + + safe_list.append(network) + + if not config or cmd not in config: + commands.append(cmd) + + if self.params["operation"] == "replace": + if config: + matches = re.findall(r"network (.*)", config, re.M) + for entry in set(matches).difference(safe_list): + commands.append("no network %s" % entry) + + return commands + + def _render_redistribute(self, item, config=None): + commands = list() + safe_list = list() + + for entry in item["redistribute"]: + option = entry["protocol"] + + cmd = "redistribute %s" % entry["protocol"] + + if entry["id"] and entry["protocol"] in ( + "ospf", + "ospfv3", + "eigrp", + ): + cmd += " %s" % entry["id"] + option += " %s" % entry["id"] + + if entry["metric"]: + cmd += " metric %s" % entry["metric"] + + if entry["route_map"]: + cmd += " route-map %s" % entry["route_map"] + + if not config or cmd not in config: + commands.append(cmd) + + safe_list.append(option) + + if self.params["operation"] == "replace": + if config: + matches = re.findall( + r"redistribute (\S+)(?:\s*)(\d*)", config, re.M + ) + for i in range(0, len(matches)): + matches[i] = " ".join(matches[i]).strip() + for entry in set(matches).difference(safe_list): + commands.append("no redistribute %s" % entry) + + return commands + + def _render_neighbors(self, item, config): + """ generate bgp neighbor configuration + """ + return AFNeighbors(self.params).render( + config, nbr_list=item["neighbors"] + ) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/neighbors.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/neighbors.py new file mode 100644 index 00000000..4db23b9d --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/neighbors.py @@ -0,0 +1,225 @@ +# +# (c) 2019, Ansible by Red Hat, inc +# 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 +import re + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.providers import ( + CliProvider, +) + + +class Neighbors(CliProvider): + def render(self, config=None, nbr_list=None): + commands = list() + safe_list = list() + if not nbr_list: + nbr_list = self.get_value("config.neighbors") + + for item in nbr_list: + neighbor_commands = list() + context = "neighbor %s" % item["neighbor"] + cmd = "%s remote-as %s" % (context, item["remote_as"]) + + if not config or cmd not in config: + neighbor_commands.append(cmd) + + for key, value in iteritems(item): + if value is not None: + meth = getattr(self, "_render_%s" % key, None) + if meth: + resp = meth(item, config) + if resp: + neighbor_commands.extend(to_list(resp)) + + commands.extend(neighbor_commands) + safe_list.append(context) + + if self.params["operation"] == "replace": + if config and safe_list: + commands.extend(self._negate_config(config, safe_list)) + + return commands + + def _negate_config(self, config, safe_list=None): + commands = list() + matches = re.findall(r"(neighbor \S+)", config, re.M) + for item in set(matches).difference(safe_list): + commands.append("no %s" % item) + return commands + + def _render_local_as(self, item, config=None): + cmd = "neighbor %s local-as %s" % (item["neighbor"], item["local_as"]) + if not config or cmd not in config: + return cmd + + def _render_port(self, item, config=None): + cmd = "neighbor %s port %s" % (item["neighbor"], item["port"]) + if not config or cmd not in config: + return cmd + + def _render_description(self, item, config=None): + cmd = "neighbor %s description %s" % ( + item["neighbor"], + item["description"], + ) + if not config or cmd not in config: + return cmd + + def _render_enabled(self, item, config=None): + cmd = "neighbor %s shutdown" % item["neighbor"] + if item["enabled"] is True: + if not config or cmd in config: + cmd = "no %s" % cmd + return cmd + elif not config or cmd not in config: + return cmd + + def _render_update_source(self, item, config=None): + cmd = "neighbor %s update-source %s" % ( + item["neighbor"], + item["update_source"], + ) + if not config or cmd not in config: + return cmd + + def _render_password(self, item, config=None): + cmd = "neighbor %s password %s" % (item["neighbor"], item["password"]) + if not config or cmd not in config: + return cmd + + def _render_ebgp_multihop(self, item, config=None): + cmd = "neighbor %s ebgp-multihop %s" % ( + item["neighbor"], + item["ebgp_multihop"], + ) + if not config or cmd not in config: + return cmd + + def _render_peer_group(self, item, config=None): + cmd = "neighbor %s peer-group %s" % ( + item["neighbor"], + item["peer_group"], + ) + if not config or cmd not in config: + return cmd + + def _render_timers(self, item, config): + """generate bgp timer related configuration + """ + keepalive = item["timers"]["keepalive"] + holdtime = item["timers"]["holdtime"] + min_neighbor_holdtime = item["timers"]["min_neighbor_holdtime"] + neighbor = item["neighbor"] + + if keepalive and holdtime: + cmd = "neighbor %s timers %s %s" % (neighbor, keepalive, holdtime) + if min_neighbor_holdtime: + cmd += " %s" % min_neighbor_holdtime + if not config or cmd not in config: + return cmd + + +class AFNeighbors(CliProvider): + def render(self, config=None, nbr_list=None): + commands = list() + if not nbr_list: + return + + for item in nbr_list: + neighbor_commands = list() + for key, value in iteritems(item): + if value is not None: + meth = getattr(self, "_render_%s" % key, None) + if meth: + resp = meth(item, config) + if resp: + neighbor_commands.extend(to_list(resp)) + + commands.extend(neighbor_commands) + + return commands + + def _render_advertisement_interval(self, item, config=None): + cmd = "neighbor %s advertisement-interval %s" % ( + item["neighbor"], + item["advertisement_interval"], + ) + if not config or cmd not in config: + return cmd + + def _render_route_reflector_client(self, item, config=None): + cmd = "neighbor %s route-reflector-client" % item["neighbor"] + if item["route_reflector_client"] is False: + if not config or cmd in config: + cmd = "no %s" % cmd + return cmd + elif not config or cmd not in config: + return cmd + + def _render_route_server_client(self, item, config=None): + cmd = "neighbor %s route-server-client" % item["neighbor"] + if item["route_server_client"] is False: + if not config or cmd in config: + cmd = "no %s" % cmd + return cmd + elif not config or cmd not in config: + return cmd + + def _render_remove_private_as(self, item, config=None): + cmd = "neighbor %s remove-private-as" % item["neighbor"] + if item["remove_private_as"] is False: + if not config or cmd in config: + cmd = "no %s" % cmd + return cmd + elif not config or cmd not in config: + return cmd + + def _render_next_hop_self(self, item, config=None): + cmd = "neighbor %s next-hop-self" % item["neighbor"] + if item["next_hop_self"] is False: + if not config or cmd in config: + cmd = "no %s" % cmd + return cmd + elif not config or cmd not in config: + return cmd + + def _render_activate(self, item, config=None): + cmd = "neighbor %s activate" % item["neighbor"] + if item["activate"] is False: + if not config or cmd in config: + cmd = "no %s" % cmd + return cmd + elif not config or cmd not in config: + return cmd + + def _render_maximum_prefix(self, item, config=None): + cmd = "neighbor %s maximum-prefix %s" % ( + item["neighbor"], + item["maximum_prefix"], + ) + if not config or cmd not in config: + return cmd + + def _render_prefix_list_in(self, item, config=None): + cmd = "neighbor %s prefix-list %s in" % ( + item["neighbor"], + item["prefix_list_in"], + ) + if not config or cmd not in config: + return cmd + + def _render_prefix_list_out(self, item, config=None): + cmd = "neighbor %s prefix-list %s out" % ( + item["neighbor"], + item["prefix_list_out"], + ) + if not config or cmd not in config: + return cmd diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/process.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/process.py new file mode 100644 index 00000000..9512d9d2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/process.py @@ -0,0 +1,168 @@ +# +# (c) 2019, Ansible by Red Hat, inc +# 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 +import re + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.providers import ( + register_provider, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.providers import ( + CliProvider, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.cli.config.bgp.neighbors import ( + Neighbors, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.cli.config.bgp.address_family import ( + AddressFamily, +) +from ansible.module_utils.common.network import to_netmask + +REDISTRIBUTE_PROTOCOLS = [ + "ospf", + "ospfv3", + "eigrp", + "isis", + "static", + "connected", + "odr", + "lisp", + "mobile", + "rip", +] + + +@register_provider("ios", "ios_bgp") +class Provider(CliProvider): + def render(self, config=None): + commands = list() + + existing_as = None + if config: + match = re.search(r"router bgp (\d+)", config, re.M) + if match: + existing_as = match.group(1) + + operation = self.params["operation"] + + context = None + if self.params["config"]: + context = "router bgp %s" % self.get_value("config.bgp_as") + + if operation == "delete": + if existing_as: + commands.append("no router bgp %s" % existing_as) + elif context: + commands.append("no %s" % context) + + else: + self._validate_input(config) + if operation == "replace": + if existing_as and int(existing_as) != self.get_value( + "config.bgp_as" + ): + commands.append("no router bgp %s" % existing_as) + config = None + + elif operation == "override": + if existing_as: + commands.append("no router bgp %s" % existing_as) + config = None + + context_commands = list() + + for key, value in iteritems(self.get_value("config")): + if value is not None: + meth = getattr(self, "_render_%s" % key, None) + if meth: + resp = meth(config) + if resp: + context_commands.extend(to_list(resp)) + + if context and context_commands: + commands.append(context) + commands.extend(context_commands) + commands.append("exit") + return commands + + def _render_router_id(self, config=None): + cmd = "bgp router-id %s" % self.get_value("config.router_id") + if not config or cmd not in config: + return cmd + + def _render_log_neighbor_changes(self, config=None): + cmd = "bgp log-neighbor-changes" + log_neighbor_changes = self.get_value("config.log_neighbor_changes") + if log_neighbor_changes is True: + if not config or cmd not in config: + return cmd + elif log_neighbor_changes is False: + if config and cmd in config: + return "no %s" % cmd + + def _render_networks(self, config=None): + commands = list() + safe_list = list() + + for entry in self.get_value("config.networks"): + network = entry["prefix"] + cmd = "network %s" % network + if entry["masklen"] and entry["masklen"] not in (24, 16, 8): + cmd += " mask %s" % to_netmask(entry["masklen"]) + network += " mask %s" % to_netmask(entry["masklen"]) + + if entry["route_map"]: + cmd += " route-map %s" % entry["route_map"] + network += " route-map %s" % entry["route_map"] + + safe_list.append(network) + + if not config or cmd not in config: + commands.append(cmd) + + if self.params["operation"] == "replace": + if config: + matches = re.findall(r"network (.*)", config, re.M) + for entry in set(matches).difference(safe_list): + commands.append("no network %s" % entry) + + return commands + + def _render_neighbors(self, config): + """ generate bgp neighbor configuration + """ + return Neighbors(self.params).render(config) + + def _render_address_family(self, config): + """ generate address-family configuration + """ + return AddressFamily(self.params).render(config) + + def _validate_input(self, config=None): + def device_has_AF(config): + return re.search(r"address-family (?:.*)", config) + + address_family = self.get_value("config.address_family") + root_networks = self.get_value("config.networks") + operation = self.params["operation"] + + if operation == "replace": + if address_family and root_networks: + for item in address_family: + if item["networks"]: + raise ValueError( + "operation is replace but provided both root level network(s) and network(s) under %s %s address family" + % (item["afi"], item["safi"]) + ) + + if root_networks and config and device_has_AF(config): + raise ValueError( + "operation is replace and device has one or more address family activated but root level network(s) provided" + ) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/module.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/module.py new file mode 100644 index 00000000..cc516ff1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/module.py @@ -0,0 +1,72 @@ +# +# (c) 2019, Ansible by Red Hat, inc +# 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 +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import Connection +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers import ( + providers, +) +from ansible.module_utils._text import to_text + + +class NetworkModule(AnsibleModule): + + fail_on_missing_provider = True + + def __init__(self, connection=None, *args, **kwargs): + super(NetworkModule, self).__init__(*args, **kwargs) + + if connection is None: + connection = Connection(self._socket_path) + + self.connection = connection + + @property + def provider(self): + if not hasattr(self, "_provider"): + capabilities = self.from_json(self.connection.get_capabilities()) + + network_os = capabilities["device_info"]["network_os"] + network_api = capabilities["network_api"] + + if network_api == "cliconf": + connection_type = "network_cli" + + cls = providers.get( + network_os, self._name.split(".")[-1], connection_type + ) + + if not cls: + msg = ( + "unable to find suitable provider for network os %s" + % network_os + ) + if self.fail_on_missing_provider: + self.fail_json(msg=msg) + else: + self.warn(msg) + + obj = cls(self.params, self.connection, self.check_mode) + + setattr(self, "_provider", obj) + + return getattr(self, "_provider") + + def get_facts(self, subset=None): + try: + self.provider.get_facts(subset) + except Exception as exc: + self.fail_json(msg=to_text(exc)) + + def edit_config(self, config_filter=None): + current_config = self.connection.get_config(flags=config_filter) + try: + commands = self.provider.edit_config(current_config) + changed = bool(commands) + return {"commands": commands, "changed": changed} + except Exception as exc: + self.fail_json(msg=to_text(exc)) diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/providers.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/providers.py new file mode 100644 index 00000000..485eb383 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/providers.py @@ -0,0 +1,128 @@ +# +# (c) 2019, Ansible by Red Hat, inc +# 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 +import json + +from threading import RLock + +from ansible.module_utils.six import itervalues +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( + NetworkConfig, +) + + +_registered_providers = {} +_provider_lock = RLock() + + +def register_provider(network_os, module_name): + def wrapper(cls): + _provider_lock.acquire() + try: + if network_os not in _registered_providers: + _registered_providers[network_os] = {} + for ct in cls.supported_connections: + if ct not in _registered_providers[network_os]: + _registered_providers[network_os][ct] = {} + for item in to_list(module_name): + for entry in itervalues(_registered_providers[network_os]): + entry[item] = cls + finally: + _provider_lock.release() + return cls + + return wrapper + + +def get(network_os, module_name, connection_type): + network_os_providers = _registered_providers.get(network_os) + if network_os_providers is None: + raise ValueError("unable to find a suitable provider for this module") + if connection_type not in network_os_providers: + raise ValueError("provider does not support this connection type") + elif module_name not in network_os_providers[connection_type]: + raise ValueError("could not find a suitable provider for this module") + return network_os_providers[connection_type][module_name] + + +class ProviderBase(object): + + supported_connections = () + + def __init__(self, params, connection=None, check_mode=False): + self.params = params + self.connection = connection + self.check_mode = check_mode + + @property + def capabilities(self): + if not hasattr(self, "_capabilities"): + resp = self.from_json(self.connection.get_capabilities()) + setattr(self, "_capabilities", resp) + return getattr(self, "_capabilities") + + def get_value(self, path): + params = self.params.copy() + for key in path.split("."): + params = params[key] + return params + + def get_facts(self, subset=None): + raise NotImplementedError(self.__class__.__name__) + + def edit_config(self): + raise NotImplementedError(self.__class__.__name__) + + +class CliProvider(ProviderBase): + + supported_connections = ("network_cli",) + + @property + def capabilities(self): + if not hasattr(self, "_capabilities"): + resp = self.from_json(self.connection.get_capabilities()) + setattr(self, "_capabilities", resp) + return getattr(self, "_capabilities") + + def get_config_context(self, config, path, indent=1): + if config is not None: + netcfg = NetworkConfig(indent=indent, contents=config) + try: + config = netcfg.get_block_config(to_list(path)) + except ValueError: + config = None + return config + + def render(self, config=None): + raise NotImplementedError(self.__class__.__name__) + + def cli(self, command): + try: + if not hasattr(self, "_command_output"): + setattr(self, "_command_output", {}) + return self._command_output[command] + except KeyError: + out = self.connection.get(command) + try: + out = json.loads(out) + except ValueError: + pass + self._command_output[command] = out + return out + + def get_facts(self, subset=None): + return self.populate() + + def edit_config(self, config=None): + commands = self.render(config) + if commands and self.check_mode is False: + self.connection.edit_config(commands) + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acls.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acls.py new file mode 100644 index 00000000..f9d64a58 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acls.py @@ -0,0 +1,284 @@ +# -*- 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 acls parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" +import re +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import ( + NetworkTemplate, +) + + +def _tmplt_access_list_name(config_data): + try: + acl_id = int(config_data.get("name")) + if not config_data.get("acl_type"): + if acl_id >= 1 and acl_id <= 99: + config_data["acl_type"] = "standard" + if acl_id >= 100 and acl_id <= 199: + config_data["acl_type"] = "extended" + except ValueError: + pass + afi = config_data.get("afi") + if afi == "ipv4": + command = "ip access-list {acl_type} {name}".format(**config_data) + elif afi == "ipv6": + command = "ipv6 access-list {name}".format(**config_data) + return command + + +def _tmplt_access_list_entries(config_data): + if "aces" in config_data: + command = [] + + def source_destination_common_config(config_data, command, type): + if config_data[type].get("address"): + command += " {address}".format(**config_data[type]) + if config_data[type].get("wildcard_bits"): + command += " {wildcard_bits}".format( + **config_data["source"] + ) + elif config_data[type].get("any"): + command += " any".format(**config_data[type]) + elif config_data[type].get("host"): + command += " host {host}".format(**config_data[type]) + if config_data[type].get("port_protocol"): + port_proto_type = list( + config_data[type]["port_protocol"].keys() + )[0] + command += " {0} {1}".format( + port_proto_type, + config_data[type]["port_protocol"][port_proto_type], + ) + return command + + command = "" + proto_option = None + if config_data.get("aces"): + aces = config_data["aces"] + if aces.get("sequence") and config_data.get("afi") == "ipv4": + command += "{sequence}".format(**aces) + if ( + aces.get("grant") + and aces.get("sequence") + and config_data.get("afi") == "ipv4" + ): + command += " {grant}".format(**aces) + elif ( + aces.get("grant") + and aces.get("sequence") + and config_data.get("afi") == "ipv6" + ): + command += "{grant}".format(**aces) + elif aces.get("grant"): + command += "{grant}".format(**aces) + if aces.get("protocol_options"): + if "protocol_number" in aces["protocol_options"]: + command += " {protocol_number}".format( + **aces["protocol_options"] + ) + else: + command += " {0}".format(list(aces["protocol_options"])[0]) + proto_option = aces["protocol_options"].get( + list(aces["protocol_options"])[0] + ) + elif aces.get("protocol"): + command += " {protocol}".format(**aces) + if aces.get("source"): + command = source_destination_common_config( + aces, command, "source" + ) + if aces.get("destination"): + command = source_destination_common_config( + aces, command, "destination" + ) + if proto_option: + command += " {0}".format(list(proto_option.keys())[0]) + if aces.get("dscp"): + command += " dscp {dscp}".format(**aces) + if aces.get("sequence") and config_data.get("afi") == "ipv6": + command += " sequence {sequence}".format(**aces) + if aces.get("fragments"): + command += " fragments {fragments}".format(**aces) + if aces.get("log"): + command += " log {log}".format(**aces) + if aces.get("log_input"): + command += " log-input {log_input}".format(**aces) + if aces.get("option"): + option_val = list(aces.get("option").keys())[0] + command += " option {0}".format(option_val) + if aces.get("precedence"): + command += " precedence {precedence}".format(**aces) + if aces.get("time_range"): + command += " time-range {time_range}".format(**aces) + if aces.get("tos"): + command += " tos" + if aces["tos"].get("service_value"): + command += " {service_value}".format(**aces["tos"]) + elif aces["tos"].get("max_reliability"): + command += " max-reliability" + elif aces["tos"].get("max_throughput"): + command += " max-throughput" + elif aces["tos"].get("min_delay"): + command += " min-delay" + elif aces["tos"].get("min_monetary_cost"): + command += " min-monetary-cost" + elif aces["tos"].get("normal"): + command += " normal" + if aces.get("ttl"): + command += " ttl {0}".format(list(aces["ttl"])[0]) + proto_option = aces["ttl"].get(list(aces["ttl"])[0]) + command += " {0}".format(proto_option) + return command + return command + + +class AclsTemplate(NetworkTemplate): + def __init__(self, lines=None): + super(AclsTemplate, self).__init__(lines=lines, tmplt=self) + + PARSERS = [ + { + "name": "acls_name", + "getval": re.compile( + r"""^(?P<acl_type>Standard|Extended)* + \s*(?P<afi>IP|IPv6)* + \s*access* + \s*list* + \s*(?P<acl_name>\S+)* + $""", + re.VERBOSE, + ), + "compval": "name", + "setval": _tmplt_access_list_name, + "result": { + "acls": { + "{{ acl_name }}": { + "name": "{{ acl_name }}", + "acl_type": "{{ acl_type.lower() if acl_type is defined }}", + "afi": "{{ 'ipv4' if afi == 'IP' else 'ipv6' }}", + } + } + }, + "shared": True, + }, + { + "name": "aces", + "getval": re.compile( + r"""\s*(?P<sequence>\d+)* + \s*(?P<grant>deny|permit)* + \s*(?P<std_source>any|(?:[0-9]{1,3}\.){3}[0-9]{1,3},\swildcard\sbits\s(?:[0-9]{1,3}\.){3}[0-9]{1,3}|(?:[0-9]{1,3}\.){3}[0-9]{1,3}|host\s(?:[0-9]{1,3}\.){3}[0-9]{1,3})* + \s*(?P<evaluate>evaluate\s\S+)* + \s*(?P<protocol>ahp|eigrp|esp|gre|icmp|igmp|ip|ipinip|nos|object-group|ospf|pcp|pim|sctp|tcp|udp)* + \s*(?P<protocol_num>\d+\s)* + \s*(?P<source>any|(?:[0-9]{1,3}\.){3}[0-9]{1,3}\s(?:[0-9]{1,3}\.){3}[0-9]{1,3}|(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\S+|host\s(?:[0-9]{1,3}\.){3}[0-9]{1,3}|object-group\s\S+)* + \s*(?P<source_port_protocol>(eq|gts|lt|neq)\s(\S+|\d+))* + \s*(?P<destination>any|(?:[0-9]{1,3}\.){3}[0-9]{1,3}\s(?:[0-9]{1,3}\.){3}[0-9]{1,3}|(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\S+|host\s(?:[0-9]{1,3}\.){3}[0-9]{1,3}|object-group\s\S+)* + \s*(?P<dest_port_protocol>(eq|gts|lt|neq)\s(\S+|\d+))* + \s*(?P<icmp_igmp_tcp_protocol>administratively-prohibited|alternate-address|conversion-error|dod-host-prohibited|dod-net-prohibited|echo|echo-reply|general-parameter-problem|host-isolated|host-precedence-unreachable|host-redirect|host-tos-redirect|host-tos-unreachable|host-unknown|host-unreachable|information-reply|information-request|mask-reply|mask-request|mobile-redirect|net-redirect|net-tos-redirect|net-tos-unreachable|net-unreachable|network-unknown|no-room-for-option|option-missing|packet-too-big|parameter-problem|port-unreachable|precedence-unreachable|protocol-unreachable|reassembly-timeout|redirect|router-advertisement|router-solicitation|source-quench|source-route-failed|time-exceeded|timestamp-reply|timestamp-request|traceroute|ttl-exceeded|unreachable|dvmrp|host-query|mtrace-resp|mtrace-route|pim|trace|v1host-report|v2host-report|v2leave-group|v3host-report|ack|established|fin|psh|rst|syn|urg)* + \s*(?P<dscp>dscp\s\S+)* + \s*(?P<fragment>fragments\s\S+)* + \s*(?P<log>log\s\S+)* + \s*(?P<log_input>log-input\s\S+)* + \s*(?P<option>option\s\S+|option\s\d+)* + \s*(?P<precedence>precedence\s\S+|precedence\s\d+)* + \s*(?P<time_range>time-range\s\S+)* + \s*(?P<tos>tos\s\S+|tos\s\d+)* + \s*(?P<ttl>ttl\s\S+\s\d+|ttl\s\d+\s\d+)* + \s*(?P<sequence_ipv6>sequence\s\d+)* + """, + re.VERBOSE, + ), + "setval": _tmplt_access_list_entries, + "compval": "aces", + "result": { + "acls": { + "{{ acl_name }}": { + "name": "{{ acl_name }}", + "aces": [ + { + "sequence": "{% if sequence is defined %}{{ sequence \ + }}{% elif sequence_ipv6 is defined %}{{ sequence_ipv6.split('sequence ')[1] }}{% endif %}", + "grant": "{{ grant }}", + "remark": "{{ remark.split('remark ')[1] if remark is defined }}", + "evaluate": "{{ evaluate.split(' ')[1] if evaluate is defined }}", + "protocol": "{{ protocol if protocol is defined }}", + "protocol_number": "{{ protocol_num if protocol_num is defined }}", + "icmp_igmp_tcp_protocol": "{{ icmp_igmp_tcp_protocol if icmp_igmp_tcp_protocol is defined }}", + "std_source": { + "address": "{% if std_source is defined and 'wildcard' in std_source and std_source.split(',')|length == 2 %}{{\ + std_source.split(',')[0]\ + }}{% elif std_source is defined and '.' in std_source and std_source.split(' ')|length == 1 %}{{\ + std_source }}{% endif %}", + "wildcard_bits": "{% if std_source is defined and 'wildcard' in std_source and std_source.split(',')|length == 2 %}{{\ + std_source.split('wildcard bits ')[1] }}{% endif %}", + "host": "{% if std_source is defined and 'host' in std_source %}{{ std_source.split(' ')[1] }}{% endif %}", + "any": "{{ True if std_source is defined and std_source == 'any' }}", + }, + "source": { + "address": "{% if source is defined and '.' in source and 'host' not in source %}{{\ + source.split(' ')[0] }}{% elif source is defined and '::' in source %}{{ source }}{% endif %}", + "wildcard_bits": "{{ source.split(' ')[1] if source is defined and '.' in source and 'host' not in source }}", + "any": "{{ True if source is defined and source == 'any' }}", + "host": "{{ source.split(' ')[1] if source is defined and 'host' in source }}", + "object_group": "{{ source.split(' ')[1] if source is defined and 'object-group' in source }}", + "port_protocol": { + "{{ source_port_protocol.split(' ')[0] if source_port_protocol is defined else None }}": "{{\ + source_port_protocol.split(' ')[1] if source_port_protocol is defined else None }}" + }, + }, + "destination": { + "address": "{% if destination is defined and '.' in destination and 'host' not in destination %}{{\ + destination.split(' ')[0] }}{% elif std_dest is defined and '.' in std_dest and 'host' not in std_dest %}{{\ + std_dest.split(' ')[0] }}{% elif destination is defined and '::' in destination %}{{ destination }}{% endif %}", + "wildcard_bits": "{% if destination is defined and '.' in destination and 'host' not in destination %}{{\ + destination.split(' ')[1] }}{% elif std_dest is defined and '.' in std_dest and 'host' not in std_dest %}{{\ + std_dest.split(' ')[1] }}{% endif %}", + "any": "{{ True if destination is defined and destination == 'any' else None }}", + "host": "{{ destination.split(' ')[1] if destination is defined and 'host' in destination }}", + "object_group": "{{ destination.split(' ')[1] if destination is defined and 'object-group' in destination else None }}", + "port_protocol": { + "{{ dest_port_protocol.split(' ')[0] if dest_port_protocol is defined else None }}": "{{\ + dest_port_protocol.split(' ')[1] if dest_port_protocol is defined else None }}" + }, + }, + "dscp": "{{ dscp.split(' ')[1] if dscp is defined }}", + "fragments": "{{ fragments.split(' ')[1] if fragments is defined }}", + "log": "{{ log.split('log ')[1] if log is defined }}", + "log_input": "{{ log_input.split(' ')[1] if log_input is defined }}", + "option": { + "{% if option is defined %}{{ option.split(' ')[1] if option is defined }}{% endif %}": "{{ True if option is defined }}" + }, + "precedence": "{{ precedence.split(' ')[1] if precedence is defined }}", + "time_range": "{{ time_range.split(' ')[1] if time_range is defined }}", + "tos": { + "max_reliability": "{{ True if tos is defined and 'max-reliability' in tos }}", + "max_throughput": "{{ True if tos is defined and 'max-throughput' in tos }}", + "min_delay": "{{ True if tos is defined and 'min-delay' in tos }}", + "min_monetary_cost": "{{ True if tos is defined and 'min-monetary-cost' in tos }}", + "normal": "{{ True if tos is defined and 'normal' in tos }}", + "service_value": "{{ tos.split(' ')[1] if tos is defined }}", + }, + "ttl": { + "eq": "{{ ttl.split(' ')[2] if ttl is defined and 'eq' in ttl }}", + "gt": "{{ ttl.split(' ')[2] if ttl is defined and 'gt' in ttl }}", + "lt": "{{ ttl.split(' ')[2] if ttl is defined and 'lt' in ttl }}", + "neq": "{{ ttl.split(' ')[2] if ttl is defined and 'neq' in ttl }}", + }, + } + ], + } + } + }, + }, + ] diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_global.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_global.py new file mode 100644 index 00000000..f96d3467 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_global.py @@ -0,0 +1,1835 @@ +# -*- 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 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.network_template import ( + NetworkTemplate, +) + + +def _tmplt_bgp_additional_paths(config_data): + if "bgp" in config_data: + if "additional_paths" in config_data["bgp"]: + cmd = "bgp additional-paths" + if "install" in config_data["bgp"]["additional_paths"]: + cmd += " install" + elif "select" in config_data["bgp"]["additional_paths"]: + cmd += " select" + if "all" in config_data["bgp"]["additional_paths"]["select"]: + cmd += " all" + elif ( + "best" in config_data["bgp"]["additional_paths"]["select"] + ): + cmd += " best {best}".format( + **config_data["bgp"]["additional_paths"]["select"] + ) + elif ( + "best_external" + in config_data["bgp"]["additional_paths"]["select"] + ): + cmd += " best-external" + elif ( + "group_best" + in config_data["bgp"]["additional_paths"]["select"] + ): + cmd += " group-best" + if "receive" in config_data["bgp"]["additional_paths"]: + cmd += " receive" + if "send" in config_data["bgp"]["additional_paths"]: + cmd += " send" + return cmd + + +def _tmplt_bgp_bestpath(config_data): + if "bgp" in config_data and "bestpath" in config_data["bgp"]: + commands = [] + val = config_data["bgp"]["bestpath"] + cmd = "bgp bestpath" + if val.get("aigp"): + commands.append("{0} aigp ignore".format(cmd)) + elif val.get("compare_routerid"): + commands.append("{0} compare-routerid".format(cmd)) + elif val.get("cost_community"): + commands.append("{0} cost-community ignore".format(cmd)) + elif val.get("igp_metric"): + commands.append("{0} igp-metric ignore".format(cmd)) + elif "med" in val: + self_cmd = "{0} med".format(cmd) + if "confed" in val["med"]: + self_cmd += " confed" + elif "missing_as_worst" in val["med"]: + self_cmd += " missing-as-worst" + commands.append(self_cmd) + return commands + + +def _tmplt_bgp_config(config_data): + if "bgp" in config_data: + cmd = [] + command = "bgp" + if config_data["bgp"].get("advertise_best_external"): + cmd.append("bgp advertise-best-external") + if config_data["bgp"].get("aggregate_timer"): + cmd.append( + "bgp aggregate-timer {aggregate_timer}".format( + **config_data["bgp"] + ) + ) + if config_data["bgp"].get("always_compare_med"): + cmd.append("bgp always-compare-med") + if config_data["bgp"].get("asnotation"): + cmd.append("bgp asnotation dot") + if "client_to_client" in config_data["bgp"]: + command = "bgp client-to-client reflection" + if "all" in config_data["bgp"]["client_to_client"]: + command += " all" + elif "intra_cluster" in config_data["bgp"]["client_to_client"]: + command += " intra-cluster cluster-id {intra_cluster}".format( + **config_data["bgp"]["client_to_client"] + ) + cmd.append(command) + if config_data["bgp"].get("cluster_id"): + cmd.append( + "bgp cluster-id {cluster_id}".format(**config_data["bgp"]) + ) + if "confederation" in config_data["bgp"]: + command = "bgp confederation" + if "identifier" in config_data["bgp"]["confederation"]: + command += "bgp identifier {identifier}".format( + **config_data["bgp"]["confederation"] + ) + elif "peers" in config_data["bgp"]["confederation"]: + command += "bgp peers {peers}".format( + **config_data["bgp"]["confederation"] + ) + cmd.append(command) + if "consistency_checker" in config_data["bgp"]: + command = "bgp consistency-checker" + if "auto_repair" in config_data["bgp"]["consistency_checker"]: + command += " auto-repair" + if ( + "interval" + in config_data["bgp"]["consistency_checker"]["auto_repair"] + ): + command += " interval {interval}".format( + **config_data["bgp"]["consistency_checker"][ + "auto_repair" + ] + ) + elif "error-message" in config_data["bgp"]["consistency_checker"]: + command += " error-message" + if ( + "interval" + in config_data["bgp"]["consistency_checker"][ + "error_message" + ] + ): + command += " interval {interval}".format( + **config_data["bgp"]["consistency_checker"][ + "error_message" + ] + ) + if config_data["bgp"].get("deterministic_med"): + cmd.append("bgp deterministic-med") + if config_data["bgp"].get("dmzlink_bw"): + cmd.append("bgp dmzlink-bw") + if config_data["bgp"].get("enforce_first_as"): + cmd.append("bgp enforce-first-as") + if config_data["bgp"].get("enhanced_error"): + cmd.append("bgp enhanced-error") + if config_data["bgp"].get("fast_external_fallover"): + cmd.append("bgp fast-external-fallover") + if "graceful_restart" in config_data["bgp"]: + command = "bgp graceful-restart" + if config_data["bgp"]["graceful_restart"].get("extended"): + command += " extended" + elif config_data["bgp"]["graceful_restart"].get("restart_time"): + command += " restart-time {restart_time}".format( + **config_data["bgp"]["graceful_restart"] + ) + elif config_data["bgp"]["graceful_restart"].get("stalepath_time"): + command += " stalepath-time {stalepath_time}".format( + **config_data["bgp"]["graceful_restart"] + ) + cmd.append(command) + if "inject_map" in config_data["bgp"]: + command = "bgp inject-map {name} exist-map {exist_map_name}".format( + **config_data["bgp"]["inject_map"] + ) + if config_data["bgp"]["inject_map"].get("copy_attributes"): + command += "copy-attributes" + cmd.append(command) + if "listen" in config_data["bgp"]: + command = "bgp listen" + if "limit" in config_data["bgp"]["listen"]: + command += " limit {limit}".format( + **config_data["bgp"]["listen"] + ) + elif "range" in config_data["bgp"]["listen"]: + if config_data["bgp"]["listen"]["range"].get( + "ipv4_with_subnet" + ): + command += " range {ipv4_with_subnet}".format( + **config_data["bgp"]["listen"]["range"] + ) + elif config_data["bgp"]["listen"]["range"].get( + "ipv6_with_subnet" + ): + command += " range {ipv6_with_subnet}".format( + **config_data["bgp"]["listen"]["range"] + ) + if config_data["bgp"]["listen"]["range"].get("peer_group"): + command += " peer-group {peer_group}".format( + **config_data["bgp"]["listen"]["range"] + ) + cmd.append(command) + if config_data["bgp"].get("log_neighbor_changes"): + cmd.append("bgp log-neighbor-changes") + if config_data["bgp"].get("maxas_limit"): + cmd.append( + "bgp maxas-limit {maxas_limit}".format(**config_data["bgp"]) + ) + if config_data["bgp"].get("maxextcommunity_limit"): + cmd.append( + "bgp maxextcommunity-limit {maxextcommunity_limit}".format( + **config_data["bgp"] + ) + ) + if "nexthop" in config_data["bgp"]: + command = "bgp nexthop" + if "route_map" in config_data["bgp"]["nexthop"]: + command += " route-map {route_map}".format( + **config_data["bgp"]["nexthop"] + ) + elif "trigger" in config_data["bgp"]["nexthop"]: + if config_data["bgp"]["nexthop"]["trigger"].get("delay"): + command += " trigger delay {delay}".format( + **config_data["bgp"]["nexthop"]["trigger"] + ) + elif config_data["bgp"]["nexthop"]["trigger"].get("delay"): + command += " trigger enable" + cmd.append(command) + if config_data["bgp"].get("recursion"): + cmd.append("bgp recursion host") + if config_data["bgp"].get("redistribute_internal"): + cmd.append("bgp redistribute-internal") + if "refresh" in config_data["bgp"]: + command = "bgp refresh" + if "max_eor_time" in config_data["bgp"]["refresh"]: + command += " max-eor-time {max_eor_time}".format( + **config_data["bgp"]["refresh"] + ) + elif "stalepath_time" in config_data["bgp"]["refresh"]: + command += " stalepath-time {stalepath_time}".format( + **config_data["bgp"]["refresh"] + ) + cmd.append(command) + if config_data["bgp"].get("regexp"): + cmd.append("bgp regexp deterministic") + if config_data["bgp"].get("route_map"): + cmd.append("bgp route-map priority") + if "router_id" in config_data["bgp"]: + command = "bgp router-id" + if "address" in config_data["bgp"]["router_id"]: + command += " {address}".format( + **config_data["bgp"]["router_id"] + ) + elif "interface" in config_data["bgp"]["router_id"]: + command += " interface {interface}".format( + **config_data["bgp"]["router_id"] + ) + elif "vrf" in config_data["bgp"]["router_id"]: + command += " vrf auto-assign" + cmd.append(command) + if config_data["bgp"].get("scan_time"): + cmd.append( + "bgp scan-time {scan_time}".format(**config_data["bgp"]) + ) + if "slow_peer" in config_data["bgp"]: + command = "bgp slow-peer" + if "detection" in config_data["bgp"]["slow_peer"]: + command += " detection" + if "threshold" in config_data["bgp"]["slow_peer"]["detection"]: + command += " threshold {threshold}".format( + **config_data["bgp"]["slow_peer"]["detection"] + ) + elif "split_update_group" in config_data["bgp"]["slow_peer"]: + if ( + "dynamic" + in config_data["bgp"]["slow_peer"]["split_update_group"] + ): + command += " dynamic" + if ( + "permanent" + in config_data["bgp"]["slow_peer"][ + "split_update_group" + ] + ): + command += " permanent {permanent}".format( + **config_data["bgp"]["slow_peer"][ + "split_update_group" + ] + ) + cmd.append(command) + if config_data["bgp"].get("snmp"): + cmd.append("bgp snmp traps add-type") + if config_data["bgp"].get("sso"): + cmd.append("bgp sso route-refresh-enable") + if config_data["bgp"].get("soft_reconfig_backup"): + cmd.append("bgp soft-reconfig-backup") + if config_data["bgp"].get("suppress_inactive"): + cmd.append("bgp suppress-inactive") + if config_data["bgp"].get("transport"): + cmd.append("bgp transport path-mtu-discovery") + if config_data["bgp"].get("update_delay"): + cmd.append( + "bgp update-delay {update_delay}".format(**config_data["bgp"]) + ) + if config_data["bgp"].get("update_group"): + cmd.append("bgp update-group split as-override") + if config_data["bgp"].get("upgrade_cli"): + command += "bgp upgrade-cli" + if config_data["bgp"]["upgrade_cli"].get("af_mode"): + command += " af-mode" + return cmd + + +def _tmplt_bgp_dampening(config_data): + if "bgp" in config_data and "dampening" in config_data["bgp"]: + if config_data["bgp"]["dampening"].get("penalty_half_time"): + command = "bgp dampening {penalty_half_time}".format( + **config_data["bgp"]["dampening"] + ) + if config_data["bgp"]["dampening"].get("reuse_route_val"): + command += " {reuse_route_val}".format( + **config_data["bgp"]["dampening"] + ) + if config_data["bgp"]["dampening"].get("suppress_route_val"): + command += " {suppress_route_val}".format( + **config_data["bgp"]["dampening"] + ) + if config_data["bgp"]["dampening"].get("max_suppress"): + command += " {max_suppress}".format( + **config_data["bgp"]["dampening"] + ) + elif config_data["bgp"]["dampening"].get("route_map"): + command = "bgp dampening {route_map}".format( + **config_data["bgp"]["dampening"] + ) + return command + + +def _tmplt_bgp_graceful_shutdown(config_data): + if "bgp" in config_data and "graceful_shutdown" in config_data["bgp"]: + command = "bgp graceful-shutdown all" + if config_data["bgp"]["graceful_shutdown"].get("neighbors"): + command += " neighbors" + if config_data["bgp"]["graceful_shutdown"]["neighbors"].get( + "activate" + ): + command += " activate" + elif config_data["bgp"]["graceful_shutdown"]["neighbors"].get( + "time" + ): + command += " {time}".format( + **config_data["bgp"]["graceful_shutdown"]["neighbors"] + ) + elif config_data["bgp"]["graceful_shutdown"].get("vrfs"): + command += " vrfs" + if config_data["bgp"]["graceful_shutdown"]["vrfs"].get("activate"): + command += " activate" + elif config_data["bgp"]["graceful_shutdown"]["neighbors"].get( + "time" + ): + command += " {time}".format( + **config_data["bgp"]["graceful_shutdown"]["vrfs"] + ) + if config_data["bgp"]["graceful_shutdown"].get("local_preference"): + command += " local-preference {local_preference}".format( + **config_data["bgp"]["graceful_shutdown"] + ) + if config_data["bgp"]["graceful_shutdown"].get("community"): + command += " community {community}".format( + **config_data["bgp"]["graceful_shutdown"] + ) + return command + + +def _tmplt_bgp_nopeerup_delay(config_data): + if "bgp" in config_data and "nopeerup_delay" in config_data["bgp"]: + commands = [] + val = config_data["bgp"]["nopeerup_delay"] + cmd = "bgp nopeerup-delay" + if val.get("cold_boot"): + commands.append("{0} cold-boot {cold_boot}".format(cmd, **val)) + elif val.get("post_boot"): + commands.append("{0} post-boot {post_boot}".format(cmd, **val)) + elif val.get("nsf_switchover"): + commands.append( + "{0} nsf-switchover {nsf_switchover}".format(cmd, **val) + ) + elif val.get("user_initiated"): + commands.append( + "{0} user-initiated {user_initiated}".format(cmd, **val) + ) + return commands + + +def _tmplt_neighbor(config_data): + if "neighbor" in config_data: + commands = [] + cmd = "neighbor" + if "address" in config_data["neighbor"]: + cmd += " {address}".format(**config_data["neighbor"]) + elif "tag" in config_data["neighbor"]: + cmd += " {tag}".format(**config_data["neighbor"]) + elif "ipv6_adddress" in config_data["neighbor"]: + cmd += " {ipv6_adddress}".format(**config_data["neighbor"]) + if "remote_as" in config_data["neighbor"]: + commands.append( + "{0} remote-as {remote_as}".format( + cmd, **config_data["neighbor"] + ) + ) + if "activate" in config_data["neighbor"]: + commands.append("{0} activate".format(cmd)) + if "additional_paths" in config_data["neighbor"]: + self_cmd = "{0} additional-paths".format(cmd) + if "disable" in config_data["neighbor"]["additional_paths"]: + self_cmd += " disable" + elif "receive" in config_data["neighbor"]["additional_paths"]: + self_cmd += " receive" + elif "send" in config_data["neighbor"]["additional_paths"]: + self_cmd += " send" + commands.append(self_cmd) + if "advertise" in config_data["neighbor"]: + self_cmd = "{0} advertise".format(cmd) + if "additional_paths" in config_data["neighbor"]["advertise"]: + self_cmd += " additional-paths" + if ( + "all" + in config_data["neighbor"]["advertise"]["additional_paths"] + ): + self_cmd += " all" + elif ( + "best" + in config_data["neighbor"]["advertise"]["additional_paths"] + ): + self_cmd += " best {best}".format( + **config_data["neighbor"]["advertise"][ + "additional_paths" + ] + ) + elif ( + "group_best" + in config_data["neighbor"]["advertise"]["additional_paths"] + ): + self_cmd += " group-best" + elif "best_external" in config_data["neighbor"]["advertise"]: + self_cmd += " best-external" + elif "diverse_path" in config_data["neighbor"]["advertise"]: + self_cmd += "diverse-path" + if ( + "backup" + in config_data["neighbor"]["advertise"]["diverse_path"] + ): + self_cmd += " backup" + elif ( + "mpath" + in config_data["neighbor"]["advertise"]["diverse_path"] + ): + self_cmd += " mpath" + commands.append(self_cmd) + if config_data["neighbor"].get("advertise_map"): + self_cmd = "{0} advertise-map {name}".format( + cmd, **config_data["neighbor"]["advertise_map"] + ) + if "exist_map" in config_data["neighbor"]["advertise_map"]: + self_cmd += " exist-map {exist_map}".format( + **config_data["neighbor"]["advertise_map"] + ) + elif "non_exist_map" in config_data["neighbor"]["advertise_map"]: + self_cmd += " exist-map {non_exist_map}".format( + **config_data["neighbor"]["advertise_map"] + ) + commands.append(self_cmd) + if config_data["neighbor"].get("advertisement_interval"): + commands.append( + "{0} advertisement-interval {advertisement_interval}".format( + cmd, **config_data["neighbor"] + ) + ) + if config_data["neighbor"].get("aigp"): + self_cmd = "{0} aigp".format(cmd) + if config_data["neighbor"]["aigp"].get("send"): + self_cmd += " send" + if config_data["neighbor"]["aigp"]["send"].get( + "cost_community" + ): + self_cmd += " cost-community {id}".format( + **config_data["neighbor"]["aigp"]["send"][ + "cost_community" + ] + ) + if config_data["neighbor"]["aigp"]["send"][ + "cost_community" + ].get("poi"): + self_cmd += " poi" + if config_data["neighbor"]["aigp"]["send"][ + "cost_community" + ]["poi"].get("igp_cost"): + self_cmd += " igp-cost" + if config_data["neighbor"]["aigp"]["send"][ + "cost_community" + ]["poi"].get("pre_bestpath"): + self_cmd += " pre-bestpath" + if config_data["neighbor"]["aigp"]["send"][ + "cost_community" + ]["poi"].get("transitive"): + self_cmd += " transitive" + if config_data["neighbor"]["aigp"]["send"].get("med"): + self_cmd += " med" + commands.append(self_cmd) + if config_data["neighbor"].get("allow_policy"): + commands.append("{0} allow-policy".format(cmd)) + if config_data["neighbor"].get("allowas_in"): + commands.append( + "{0} allowas-in {allowas_in}".format( + cmd, **config_data["neighbor"] + ) + ) + if config_data["neighbor"].get("as_override"): + commands.append("{0} as-override".format(cmd)) + if "bmp_activate" in config_data["neighbor"]: + self_cmd = "{0} bmp-activate".format(cmd) + if config_data["neighbor"]["bmp_activate"].get("all"): + self_cmd += " all" + if "server" in config_data["neighbor"]["bmp_activate"]: + self_cmd += " server {server}".format( + **config_data["neighbor"]["bmp_activate"] + ) + commands.append(self_cmd) + if "capability" in config_data["neighbor"]: + self_cmd = "{0} capability".format(cmd) + if config_data["neighbor"]["capability"].get("both"): + self_cmd += " both" + elif config_data["neighbor"]["capability"].get("receive"): + self_cmd += " receive" + elif config_data["neighbor"]["capability"].get("send"): + self_cmd += " send" + commands.append(self_cmd) + if config_data["neighbor"].get("cluster_id"): + commands.append( + "{0} cluster-id {cluster_id}".format( + cmd, **config_data["neighbor"] + ) + ) + if "default_originate" in config_data["neighbor"]: + self_cmd = "{0} default-originate".format(cmd) + if config_data["neighbor"]["default_originate"].get("route_map"): + self_cmd += " route-map {route_map}".format( + **config_data["neighbor"]["default_originate"] + ) + commands.append(self_cmd) + if "description" in config_data["neighbor"]: + commands.append( + "{0} description {description}".format( + cmd, **config_data["neighbor"] + ) + ) + if config_data["neighbor"].get("disable_connected_check"): + commands.append("{0} disable-connected-check".format(cmd)) + if "distribute_list" in config_data["neighbor"]: + self_cmd = "{0} distribute-list".format(cmd) + if "acl" in config_data["neighbor"]["distribute_list"]: + self_cmd += " {acl}".format( + **config_data["neighbor"]["distribute_list"] + ) + if config_data["neighbor"]["distribute_list"].get("in"): + self_cmd += " in" + elif config_data["neighbor"]["distribute_list"].get("out"): + self_cmd += " out" + commands.append(self_cmd) + if config_data["neighbor"].get("dmzlink_bw"): + commands.append("{0} dmzlink-bw".format(cmd)) + if "ebgp_multihop" in config_data["neighbor"]: + self_cmd = "{0} ebgp-multihop".format(cmd) + if "hop_count" in config_data["neighbor"]["ebgp_multihop"]: + self_cmd += " {hop_count}".format( + **config_data["neighbor"]["ebgp_multihop"] + ) + commands.append(self_cmd) + if "fall_over" in config_data["neighbor"]: + self_cmd = "{0} fall-over".format(cmd) + if "bfd" in config_data["neighbor"]["fall_over"]: + self_cmd += " bfd" + if config_data["neighbor"]["fall_over"]["bfd"].get( + "multi_hop" + ): + self_cmd += " multi-hop" + elif config_data["neighbor"]["fall_over"]["bfd"].get( + "single_hop" + ): + self_cmd += " single-hop" + elif "route_map" in config_data["neighbor"]["fall_over"]: + self_cmd += " {route_map}".format( + **config_data["neighbor"]["route_map"] + ) + commands.append(self_cmd) + if "filter_list" in config_data["neighbor"]: + self_cmd = "{0} filter-list".format(cmd) + if "path_acl" in config_data["neighbor"]["filter_list"]: + self_cmd += " {path_acl}".format( + **config_data["neighbor"]["filter_list"] + ) + if config_data["neighbor"]["filter_list"].get("in"): + self_cmd += " in" + elif config_data["neighbor"]["filter_list"].get("out"): + self_cmd += " out" + commands.append(self_cmd) + if "ha_mode" in config_data["neighbor"]: + self_cmd = "{0} ha-mode".format(cmd) + if config_data["neighbor"]["ha_mode"].get("disable"): + self_cmd += " disable" + commands.append(self_cmd) + if "inherit" in config_data["neighbor"]: + self_cmd = "{0} inherit {inherit}".format( + cmd, **config_data["neighbor"] + ) + commands.append(self_cmd) + if "local_as" in config_data["neighbor"]: + self_cmd = "{0} local-as".format(cmd) + if "number" in config_data["neighbor"]["local_as"]: + self_cmd += " {number}".format( + **config_data["neighbor"]["local_as"] + ) + if config_data["neighbor"]["local_as"].get("dual_as"): + self_cmd += " dual-as" + elif config_data["neighbor"]["local_as"].get("no_prepend"): + self_cmd += " no-prepend" + if config_data["neighbor"]["local_as"]["no_prepend"]: + self_cmd += " replace-as" + commands.append(self_cmd) + if "log_neighbor_changes" in config_data["neighbor"]: + self_cmd = "{0} log-neighbor-changes".format(cmd) + if config_data["neighbor"]["log_neighbor_changes"].get("disable"): + self_cmd += " disable" + commands.append(self_cmd) + if "maximum_prefix" in config_data["neighbor"]: + self_cmd = "{0} maximum-prefix".format(cmd) + if "max_no" in config_data["neighbor"]["maximum_prefix"]: + self_cmd += " {max_no}".format( + **config_data["neighbor"]["maximum_prefix"] + ) + if "threshold_val" in config_data["neighbor"]["maximum_prefix"]: + self_cmd += " {threshold_val}".format( + **config_data["neighbor"]["maximum_prefix"] + ) + if config_data["neighbor"]["maximum_prefix"].get("restart"): + self_cmd += " restart {restart}".format( + **config_data["neighbor"]["maximum_prefix"] + ) + elif config_data["neighbor"]["filter_list"].get("warning_only"): + self_cmd += " warning-only" + commands.append(self_cmd) + if "next_hop_self" in config_data["neighbor"]: + self_cmd = "{0} next-hop-self".format(cmd) + if config_data["neighbor"]["next_hop_self"].get("all"): + self_cmd += " all" + commands.append(self_cmd) + if "next_hop_unchanged" in config_data["neighbor"]: + self_cmd = "{0} next-hop-unchanged".format(cmd) + if config_data["neighbor"]["next_hop_unchanged"].get("allpaths"): + self_cmd += " allpaths" + commands.append(self_cmd) + if "password" in config_data["neighbor"]: + commands.append( + "{0} password {password}".format( + cmd, **config_data["neighbor"] + ) + ) + if "path_attribute" in config_data["neighbor"]: + self_cmd = "{0} path-attribute".format(cmd) + if "discard" in config_data["neighbor"]["path_attribute"]: + self_cmd += " discard" + if ( + "type" + in config_data["neighbor"]["path_attribute"]["discard"] + ): + self_cmd += " {type}".format( + **config_data["neighbor"]["path_attribute"]["discard"] + ) + elif ( + "range" + in config_data["neighbor"]["path_attribute"]["discard"] + ): + self_cmd += " range" + if ( + "start" + in config_data["neighbor"]["path_attribute"][ + "discard" + ]["range"] + ): + self_cmd += " {start}".format( + **config_data["neighbor"]["path_attribute"][ + "discard" + ]["range"] + ) + elif ( + "end" + in config_data["neighbor"]["path_attribute"][ + "discard" + ]["range"] + ): + self_cmd += " {start}".format( + **config_data["neighbor"]["path_attribute"][ + "discard" + ]["range"] + ) + if ( + "in" + in config_data["neighbor"]["path_attribute"]["discard"] + ): + self_cmd += " in" + if ( + "treat_as_withdraw" + in config_data["neighbor"]["path_attribute"] + ): + self_cmd += " treat-as-withdraw" + if ( + "type" + in config_data["neighbor"]["path_attribute"][ + "treat_as_withdraw" + ] + ): + self_cmd += " {type}".format( + **config_data["neighbor"]["path_attribute"][ + "treat_as_withdraw" + ] + ) + elif ( + "range" + in config_data["neighbor"]["path_attribute"][ + "treat_as_withdraw" + ] + ): + self_cmd += " range" + if ( + "start" + in config_data["neighbor"]["path_attribute"][ + "treat_as_withdraw" + ]["range"] + ): + self_cmd += " {start}".format( + **config_data["neighbor"]["path_attribute"][ + "treat_as_withdraw" + ]["range"] + ) + elif ( + "end" + in config_data["neighbor"]["path_attribute"][ + "treat_as_withdraw" + ]["range"] + ): + self_cmd += " {start}".format( + **config_data["neighbor"]["path_attribute"][ + "treat_as_withdraw" + ]["range"] + ) + if ( + "in" + in config_data["neighbor"]["path_attribute"][ + "treat_as_withdraw" + ] + ): + self_cmd += " in" + commands.append(self_cmd) + if "peer_group" in config_data["neighbor"]: + commands.append( + "{0} peer-group {peer_group}".format( + cmd, **config_data["neighbor"] + ) + ) + if "remove_private_as" in config_data["neighbor"]: + self_cmd = "{0} remove-private-as".format(cmd) + if config_data["neighbor"]["remove_private_as"].get("all"): + self_cmd += " all" + elif config_data["neighbor"]["remove_private_as"].get( + "replace_as" + ): + self_cmd += " replace_as" + commands.append(self_cmd) + if "route_map" in config_data["neighbor"]: + self_cmd = "{0} route-map".format(cmd) + if "name" in config_data["neighbor"]["route_map"]: + self_cmd += " {name}".format( + **config_data["neighbor"]["route_map"] + ) + if "in" in config_data["neighbor"]["route_map"]: + self_cmd += " in" + elif "out" in config_data["neighbor"]["route_map"]: + self_cmd += " out" + commands.append(self_cmd) + if "route_reflector_client" in config_data["neighbor"]: + commands.append("{0} route-reflector-client".format(cmd)) + if "route_server_client" in config_data["neighbor"]: + self_cmd = "{0} route-server-client".format(cmd) + if "context" in config_data["neighbor"]["route_map"]: + self_cmd += " context {context}".format( + **config_data["neighbor"]["route_server_client"] + ) + commands.append(self_cmd) + if "send_community" in config_data["neighbor"]: + self_cmd = "{0} send-community".format(cmd) + if config_data["neighbor"]["send_community"].get("both"): + self_cmd += " both" + elif config_data["neighbor"]["send_community"].get("extended"): + self_cmd += " extended" + elif config_data["neighbor"]["send_community"].get("standard"): + self_cmd += " standard" + commands.append(self_cmd) + if "send_label" in config_data["neighbor"]: + self_cmd = "{0} send-label".format(cmd) + if config_data["neighbor"]["send_label"].get("explicit_null"): + self_cmd += " explicit-null" + commands.append(self_cmd) + if "shutdown" in config_data["neighbor"]: + self_cmd = "{0} shutdown".format(cmd) + if "graceful" in config_data["neighbor"]["route_map"]: + self_cmd += " graceful {graceful}".format( + **config_data["neighbor"]["shutdown"] + ) + commands.append(self_cmd) + if "slow_peer" in config_data["neighbor"]: + self_cmd = "{0} slow-peer".format(cmd) + if "detection" in config_data["neighbor"]["slow_peer"]: + self_cmd += " detection" + if ( + "disable" + in config_data["neighbor"]["slow_peer"]["detection"] + ): + self_cmd += " disable" + elif ( + "threshold" + in config_data["neighbor"]["slow_peer"]["detection"] + ): + self_cmd += " threshold {threshold}".format( + **config_data["neighbor"]["slow_peer"]["detection"] + ) + elif "split_update_group" in config_data["neighbor"]["slow_peer"]: + self_cmd += " split-update-group" + if ( + "dynamic" + in config_data["neighbor"]["slow_peer"][ + "split_update_group" + ] + ): + self_cmd += " dynamic" + if ( + "disable" + in config_data["neighbor"]["slow_peer"][ + "split_update_group" + ]["dynamic"] + ): + self_cmd += " disable" + elif ( + "permanent" + in config_data["neighbor"]["slow_peer"][ + "split_update_group" + ]["dynamic"] + ): + self_cmd += " permanent" + elif ( + "static" + in config_data["neighbor"]["slow_peer"][ + "split_update_group" + ] + ): + self_cmd += " static" + commands.append(self_cmd) + if "soft_reconfiguration" in config_data["neighbor"]: + commands.append("{0} soft-reconfiguration".format(cmd)) + if "timers" in config_data["neighbor"]: + self_cmd = "{0} timers {interval} {holdtime}".format( + cmd, **config_data["neighbor"]["timers"] + ) + if "min_holdtime" in config_data["neighbor"]["timers"]: + self_cmd += " {min_holdtime}".format( + **config_data["neighbor"]["timers"] + ) + commands.append(self_cmd) + if "translate_update" in config_data["neighbor"]: + self_cmd = "{0} translate-update".format(cmd) + if config_data["neighbor"]["send_community"].get("nlri"): + self_cmd += " nlri" + if config_data["neighbor"]["nlri"].get("multicast"): + self_cmd += "multicast" + if config_data["neighbor"]["nlri"].get("unicast"): + self_cmd += "unicast" + commands.append(self_cmd) + if "transport" in config_data["neighbor"]: + self_cmd = "{0} transport".format(cmd) + if config_data["neighbor"]["transport"].get("connection_mode"): + self_cmd += " connection-mode" + if config_data["neighbor"]["transport"]["connection_mode"].get( + "active" + ): + self_cmd += " active" + elif config_data["neighbor"]["transport"][ + "connection_mode" + ].get("passive"): + self_cmd += " passive" + elif config_data["neighbor"]["transport"].get("multi_session"): + self_cmd += " multi-session" + elif config_data["neighbor"]["transport"].get( + "path_mtu_discovery" + ): + self_cmd += " path-mtu-discovery" + if config_data["neighbor"]["transport"][ + "path_mtu_discovery" + ].get("disable"): + self_cmd += " disable" + commands.append(self_cmd) + if "ttl_security" in config_data["neighbor"]: + commands.append( + "{0} ttl-security {ttl_security}".format( + cmd, **config_data["neighbor"] + ) + ) + if "unsuppress_map" in config_data["neighbor"]: + commands.append( + "{0} unsuppress-map {unsuppress_map}".format( + cmd, **config_data["neighbor"] + ) + ) + if "version" in config_data["neighbor"]: + commands.append( + "{0} version {version}".format(cmd, **config_data["neighbor"]) + ) + if "weight" in config_data["neighbor"]: + commands.append( + "{0} weight {weight}".format(cmd, **config_data["neighbor"]) + ) + return commands + + +def _tmplt_redistribute(config_data): + if "redistribute" in config_data: + + def common_config(command, param): + if config_data["redistribute"][param].get("metric"): + command += " metric {metric}".format( + **config_data["redistribute"][param] + ) + if config_data["redistribute"][param].get("route_map"): + command += " route-map {route_map}".format( + **config_data["redistribute"][param] + ) + return command + + command = "redistribute" + if config_data["redistribute"].get("application"): + command += " application {name}".format( + **config_data["redistribute"]["application"] + ) + command = common_config(command, "application") + elif config_data["redistribute"].get("bgp"): + command += " bgp {as_number}".format( + **config_data["redistribute"]["bgp"] + ) + command = common_config(command, "bgp") + elif config_data["redistribute"].get("connected"): + command += " connected" + command = common_config(command, "connected") + elif config_data["redistribute"].get("eigrp"): + command += " eigrp {as_number}".format( + **config_data["redistribute"]["eigrp"] + ) + command = common_config(command, "eigrp") + elif config_data["redistribute"].get("isis"): + command += " isis {area_tag}".format( + **config_data["redistribute"]["isis"] + ) + if config_data["redistribute"]["isis"].get("clns"): + command += " clns" + elif config_data["redistribute"]["isis"].get("ip"): + command += " ip" + command = common_config(command, "isis") + elif config_data["redistribute"].get("iso_igrp"): + command += " iso-igrp {area_tag}".format( + **config_data["redistribute"]["iso_igrp"] + ) + if config_data["redistribute"]["iso_igrp"].get("route_map"): + command += " route-map {route_map}".format( + **config_data["redistribute"]["iso_igrp"] + ) + elif config_data["redistribute"].get("lisp"): + command += " lisp" + command = common_config(command, "lisp") + elif config_data["redistribute"].get("mobile"): + command += " mobile" + command = common_config(command, "mobile") + elif config_data["redistribute"].get("odr"): + command += " odr" + command = common_config(command, "odr") + elif config_data["redistribute"].get("rip"): + command += " rip" + command = common_config(command, "rip") + elif config_data["redistribute"].get("ospf"): + command += " ospf {process_id}".format( + **config_data["redistribute"]["ospf"] + ) + if config_data["redistribute"]["ospf"].get("match"): + command += " match" + if config_data["redistribute"]["ospf"]["match"].get( + "external" + ): + command += " external" + if config_data["redistribute"]["ospf"]["match"].get( + "internal" + ): + command += " internal" + if config_data["redistribute"]["ospf"]["match"].get( + "nssa_external" + ): + command += " nssa-external" + if config_data["redistribute"]["ospf"]["match"].get("type_1"): + command += " 1" + elif config_data["redistribute"]["ospf"]["match"].get( + "type_2" + ): + command += " 2" + if config_data["redistribute"]["ospf"].get("vrf"): + command += " vrf" + command = common_config(command, "ospf") + elif config_data["redistribute"].get("ospfv3"): + command += " ospfv3 {process_id}".format( + **config_data["redistribute"]["ospfv3"] + ) + if config_data["redistribute"]["ospfv3"].get("match"): + command += " match" + if config_data["redistribute"]["ospfv3"]["match"].get( + "external" + ): + command += " external" + if config_data["redistribute"]["ospfv3"]["match"].get( + "internal" + ): + command += " internal" + if config_data["redistribute"]["ospfv3"]["match"].get( + "nssa_external" + ): + command += " nssa-external" + if config_data["redistribute"]["ospfv3"]["match"].get( + "type_1" + ): + command += " 1" + elif config_data["redistribute"]["ospfv3"]["match"].get( + "type_2" + ): + command += " 2" + command = common_config(command, "ospf") + elif config_data["redistribute"].get("static"): + command += " static" + command = common_config(command, "static") + elif config_data["redistribute"].get("vrf"): + if config_data["redistribute"]["vrf"].get("name"): + command += " vrf {name}".format( + **config_data["redistribute"]["vrf"] + ) + elif config_data["redistribute"]["vrf"].get("global"): + command += " vrf global" + + return command + + +def _tmplt_bgp_timers(config_data): + if "timers" in config_data: + command = "timers bgp" + if config_data["timers"].get("keepalive"): + command += " {keepalive}".format(**config_data["timers"]) + if config_data["timers"].get("holdtime"): + command += " {holdtime}".format(**config_data["timers"]) + if config_data["timers"].get("min_holdtime"): + command += " {min_holdtime}".format(**config_data["timers"]) + return command + + +class Bgp_globalTemplate(NetworkTemplate): + def __init__(self, lines=None): + super(Bgp_globalTemplate, self).__init__(lines=lines, tmplt=self) + + PARSERS = [ + { + "name": "as_number", + "getval": re.compile( + r"""^router* + \s*bgp* + \s*(?P<as_number>\d+)* + $""", + re.VERBOSE, + ), + "compval": "as_number", + "setval": "router bgp {{ as_number }}", + "result": {"as_number": "{{ as_number }}"}, + "shared": True, + }, + { + "name": "bgp.additional_paths", + "getval": re.compile( + r"""\s*bgp* + \s*additional-paths* + \s*(?P<install>install)* + \s*(?P<receive>receive)* + \s*(?P<select>select)* + \s*(?P<select_all>all)* + \s*(?P<select_backup>backup)* + \s*(?P<select_best>best\s\d)* + \s*(?P<select_best_external>best-external)* + \s*(?P<select_group_best>group-best)* + \s*(?P<send>send)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_additional_paths, + "result": { + "bgp": { + "additional_paths": { + "install": "{{ True if install is defined }}", + "receive": "{{ True if receive is defined }}", + "select": { + "all": "{{ True if select_all is defined }}", + "backup": "{{ True if select_backup is defined }}", + "best": "{{ select_best.split('best ')[1] if select_best is defined }}", + "best_external": "{{ True if select_best_external is defined }}", + "group_best": "{{ True if select_group_best is defined }}", + }, + "send": "{{ True if send is defined }}", + } + } + }, + }, + { + "name": "bgp.bestpath", + "getval": re.compile( + r"""\s*bgp* + \s*bestpath* + \s*(?P<aigp>aigp\signore)* + \s*(?P<compare_routerid>compare-routerid)* + \s*(?P<cost_community>cost-community\signore)* + \s*(?P<med>med\s(confed|missing-as-worst|confed\smissing-as-worst))* + $""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_bestpath, + "result": { + "bgp": { + "bestpath": [ + { + "aigp": "{{ True if aigp is defined }}", + "compare_routerid": "{{ True if compare_routerid is defined }}", + "cost_community": "{{ True if cost_community is defined }}", + "med": { + "confed": "{{ True if med is defined and 'confed' in med }}", + "missing_as_worst": "{{ True if med is defined and 'missing-as-worst' in med }}", + }, + } + ] + } + }, + }, + { + "name": "bgp.config", + "getval": re.compile( + r"""\s*bgp* + \s*(?P<advertise_best_external>advertise-best-external)* + \s*(?P<aggregate_timer>aggregate-timer\s\d+)* + \s*(?P<always_compare_med>always-compare-med)* + \s*(?P<asnotation>asnotation\sdot)* + \s*(?P<client_to_client>client-to-client\sreflection\s(all|intra-cluster))* + \s*(?P<cluster_id>cluster-id\s.*)* + \s*(?P<confederation>confederation\s(identifier\s\d+|peers\s\d+))* + \s*(?P<consistency_checker>consistency-checker\s((auto-repair\sinterval\s\d+|auto-repair)|(error-message\sinterval\s\d+|error-message)))* + \s*(?P<deterministic_med>deterministic-med)* + \s*(?P<dmzlink_bw>dmzlink-bw)* + \s*(?P<enforce_first_as>enforce-first-as)* + \s*(?P<enhanced_error>enhanced-error)* + \s*(?P<fast_external_fallover>fast-external-fallover)* + \s*(?P<graceful_restart>graceful-restart(\sextended|\srestart-time\s\d+|\sstalepath-time\s\d+))* + \s*(?P<inject_map>inject-map\s\S+\sexist-map\s\S+\scopy-attributes|inject-map\s\S+\sexist-map\s\S+)* + \s*(?P<listen>listen\s(limit\s\d+|(range\s.*peer-group\s\S+|range\s.*)))* + \s*(?P<log_neighbor_changes>log-neighbor-changes)* + \s*(?P<maxas_limit>maxas-limit\s\d+)* + \s*(?P<maxcommunity_limit>maxas-limit\s\d+)* + \s*(?P<maxextcommunity_limit>maxextcommunity-limit\s\d+)* + \s*(?P<nexthop>nexthop\s(route-map\s\S+|trigger\s(delay\s\d+|enable)))* + \s*(?P<recursion>recursion\shost)* + \s*(?P<redistribute_internal>redistribute-internal)* + \s*(?P<refresh>refresh\s(max-eor-time\s\d+|stalepath-time\s\d+))* + \s*(?P<regexp>regexp\sdeterministic)* + \s*(?P<router_id>router-id\s((?:[0-9]{1,3}\.){3}[0-9]{1,3}|interface\s\S+\s\S+|vrf\sauto-assign))* + \s*(?P<route_map>route-map\spriority)* + \s*(?P<scan_time>scan-time\s\d+)* + \s*(?P<slow_peer>slow-peer((\sdetection\sthreshold\d+|\sdetection)|(\ssplit-update-group\sdynamic\spermanent|\ssplit-update-group\sdynamic)))* + \s*(?P<snmp>snmp\straps\sadd-type)* + \s*(?P<sso>sso\sroute-refresh-enable)* + \s*(?P<soft_reconfig_backup>soft-reconfig-backup)* + \s*(?P<suppress_inactive>suppress-inactive)* + \s*(?P<transport>transport\spath-mtu-discovery)* + \s*(?P<update_delay>update-delay\s\d+)* + \s*(?P<update_group>update-group\ssplit\sas-override)* + \s*(?P<upgrade_cli>upgrade-cli\saf-mode|upgrade-cli)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_config, + "compval": "bgp", + "result": { + "bgp": { + "advertise_best_external": "{{ True if advertise_best_external is defined }}", + "aggregate_timer": "{{ aggregate_timer.split('aggregate-timer ')[1] if aggregate_timer is defined }}", + "always_compare_med": "{{ True if always_compare_med is defined }}", + "asnotation": "{{ True if asnotation is defined }}", + "client_to_client": { + "set": "{{ True if client_to_client is defined and client_to_client.split(' ')|length == 2 }}", + "all": "{{ True if client_to_client is defined and 'all' in client_to_client }}", + "intra_cluster": "{{\ + client_to_client.split('cluster-id ')[1]\ + if client_to_client is defined and 'intra-cluster' in client_to_client }}", + }, + "cluster_id": "{{ cluster_id.split('cluster-id ')[1] if cluster_id is defined }}", + "confederation": { + "identifier": "{{ confederation.split('confederation identifier ')[1] if confederation is defined }}", + "peers": "{{ confederation.split('confederation peers ')[1] if confederation is defined }}", + }, + "consistency_checker": { + "auto_repair": { + "set": "{{\ + True if consistency_checker is defined and\ + 'auto-repair' in consistency_checker and\ + consistency_checker.split(' ')|length == 2 }}", + "interval": "{{\ + consistency_checker.split('interval ')[1] if consistency_checker is defined and\ + consistency_checker.split(' ')|length > 2 }}", + }, + "error_message": { + "set": "{{\ + True if consistency_checker is defined and\ + 'error-message' in consistency_checker and consistency_checker.split(' ')|length == 2 }}", + "interval": "{{\ + consistency_checker.split('interval ')[1] if consistency_checker is defined and\ + consistency_checker.split(' ')|length > 2 }}", + }, + }, + "deterministic_med": "{{ True if deterministic_med is defined }}", + "dmzlink_bw": "{{ True if dmzlink_bw is defined }}", + "enforce_first_as": "{{ True if enforce_first_as is defined }}", + "enhanced_error": "{{ True if enhanced_error is defined }}", + "fast_external_fallover": "{{ True if fast_external_fallover is defined }}", + "graceful_restart": { + "set": "{{ True if graceful_restart is defined and graceful_restart.split(' ')|length == 2 }}", + "extended": "{{ True if graceful_restart is defined and 'extended' in graceful_restart }}", + "restart_time": "{{\ + graceful_restart.split('graceful-restart restart-time ')[1] if graceful_restart is defined and\ + 'restart-time' in graceful_restart }}", + "stalepath_time": "{{\ + graceful_restart.split('graceful-restart stalepath-time ')[1] if graceful_restart is defined and\ + 'stalepath-time' in graceful_restart }}", + }, + "inject_map": { + "name": "{{ inject_map.split(' ')[1] if inject_map is defined }}", + "exist_map_name": "{{ inject_map.split('exist-map ')[1].split(' ')[0] if inject_map is defined and 'exist-map' in inject_map }}", + "copy_attributes": "{{ True if inject_map is defined and 'copy-attributes' in inject_map }}", + }, + "listen": { + "limit": "{{ listen.split('limit ')[1] if listen is defined and 'limit' in listen }}", + "range": { + "ipv4_with_subnet": "{{ listen.split('range ')[1].split(' ')[0] if listen is defined and ':' not in listen and '.' in listen }}", + "ipv6_with_subnet": "{{ listen.split('range ')[1].split(' ')[0] if listen is defined and ':' in listen and '.' in listen }}", + "peer_group": "{{ listen.split('peer-group ')[1] if listen is defined and 'peer-group' in listen }}", + }, + }, + "log_neighbor_changes": "{{ True if log_neighbor_changes is defined }}", + "maxas_limit": "{{ maxas_limit.split('maxas-limit ')[1] if maxas_limit is defined }}", + "maxcommunity_limit": "{{ maxcommunity_limit.split('maxcommunity-limit ')[1] if maxcommunity_limit is defined }}", + "maxextcommunity_limit": "{{ maxextcommunity_limit.split('maxextcommunity-limit ')[1] if maxextcommunity_limit is defined }}", + "nexthop": { + "route_map": "{{ nexthop.split('route-map ')[1] if nexthop is defined and 'route-map' in nexthop }}", + "trigger": { + "delay": "{{ nexthop.split('delay ')[1] if nexthop is defined and 'delay' in nexthop }}", + "enable": "{{ True if nexthop is defined and 'enable' in nexthop }}", + }, + }, + "recursion": "{{ True if recursion is defined }}", + "redistribute_internal": "{{ True if redistribute_internal is defined }}", + "refresh": { + "max_eor_time": "{{ refresh.split('max-eor-time ')[1] if refresh is defined }}", + "stalepath_time": "{{ refresh.split('stalepath-time ')[1] if refresh is defined }}", + }, + "regexp": "{{ True if regexp is defined }}", + "router_id": { + "address": "{{ router_id.split(' ')[1] if router_id is defined and '.' in router_id }}", + "interface": "{{ router_id.split('interface ')[1] if router_id is defined and 'interface' in router_id }}", + "vrf": "{{ True if router_id is defined and 'vrf auto-assign' in router_id }}", + }, + "route_map": "{{ True if route_map is defined }}", + "scan_time": "{{ scan_time.split('scan-time ')[1] if scan_time is defined }}", + "slow_peer": { + "detection": { + "set": "{{ True if slow_peer is defined and 'detection' in slow_peer and 'threshold' not in slow_peer }}", + "threshold": "{{ slow_peer.split('threshold ')[1] if slow_peer is defined and 'threshold' in slow_peer }}", + }, + "split_update_group": { + "dynamic": "{{ True if split_dynamic is defined and split_dynamic.split('dynamic ')[1] == 'disable' }}", + "static": "{{ True if split_static is defined }}", + }, + }, + "snmp": "{{ True if snmp is defined }}", + "sso": "{{ True if sso is defined }}", + "soft_reconfig_backup": "{{ True if soft_reconfig_backup is defined }}", + "suppress_inactive": "{{ True if suppress_inactive is defined }}", + "transport": "{{ True if transport is defined }}", + "update_delay": "{{ update_delay.split('update-delay ')[1] if update_delay is defined }}", + "update_group": "{{ True if update_group is defined }}", + "upgrade_cli": { + "set": "{{ True if graceful_restart is defined and graceful_restart.split(' ')|length == 2 }}", + "af_mode": "{{ True if upgrade_cli is defined and 'af-mode' in upgrade_cli }}", + }, + } + }, + }, + { + "name": "bgp.dampening", + "getval": re.compile( + r"""\s*bgp* + \s*dampening* + \s*(?P<penalty_half_time>\d+)* + \s*(?P<reuse_route_val>\d+)* + \s*(?P<suppress_route_val>\d+)* + \s*(?P<max_suppress>\d+)* + \s*(?P<route_map>\S+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_dampening, + "result": { + "bgp": { + "dampening": { + "penalty_half_time": "{{ penalty_half_time if penalty_half_time is defined }}", + "reuse_route_val": "{{ reuse_route_val if penalty_half_time is defined }}", + "suppress_route_val": "{{ suppress_route_val if penalty_half_time is defined }}", + "max_suppress": "{{ max_suppress if penalty_half_time is defined }}", + "route_map": "{{ dampening.split('route-map ')[1] if dampening is defined and 'route-map' in dampening }}", + } + } + }, + }, + { + "name": "bgp.graceful_shutdown", + "getval": re.compile( + r"""\s*bgp* + \s*graceful-shutdown\sall* + \s*(?P<neighbors>neighbors\s(\d+|activate))* + \s*(?P<vrfs>vrfs\s(\d+|activate))* + \s*(?P<local_preference>local-preference\s\d+)* + \s*(?P<community>community\s\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_graceful_shutdown, + "result": { + "bgp": { + "graceful_shutdown": { + "neighbors": { + "time": "{{ neighbors.split('neighbors ')[1] if neighbors is defined and 'activate' not in neighbors }}", + "activate": "{{ True if neighbors is defined and 'activate' in neighbors }}", + }, + "vrfs": { + "time": "{{ vrfs.split('vrfs ')[1] if vrfs is defined and 'activate' not in vrfs }}", + "activate": "{{ True if vrfs is defined and 'activate' in vrfs }}", + }, + "community": "{{ community.split('community ')[1] if community is defined }}", + "local_preference": "{{ local_preference.split('local-preference ')[1] if local_preference is defined }}", + } + } + }, + }, + { + "name": "bgp.nopeerup_delay", + "getval": re.compile( + r"""\s*bgp* + \s*nopeerup-delay* + \s*(?P<cold_boot>cold-boot\s\d+)* + \s*(?P<nsf_switchover>nsf-switchover\s\d+)* + \s*(?P<post_boot>post-boot\s\d+)* + \s*(?P<user_initiated>user-initiated\s\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_bgp_nopeerup_delay, + "result": { + "bgp": { + "nopeerup_delay": [ + { + "cold_boot": "{{ cold_boot.split('cold-boot ')[1] if cold_boot is defined }}", + "nsf_switchover": "{{ nsf_switchover.split('nsf-switchover ')[1] if nsf_switchover is defined }}", + "post_boot": "{{ post_boot.split('post-boot ')[1] if post_boot is defined }}", + "user_initiated": "{{ user_initiated.split('user-initiated ')[1] if user_initiated is defined }}", + } + ] + } + }, + }, + { + "name": "neighbor", + "getval": re.compile( + r"""\s*neighbor* + \s*(?P<neighbor>(?:[0-9]{1,3}\.){3}[0-9]{1,3}|host\s(?:[0-9]{1,3}\.){3}[0-9]{1,3}|(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\S+|\S+)* + \s*(?P<activate>activate)* + \s*(?P<additional_paths>additional-paths\s(disable|receive|send))* + \s*(?P<advertise>advertise\sadditional-paths\sall\sbest\s\d+\sgroup-best|advertise\sadditional-paths\sbest\s\d+\sgroup-best|advertise\sadditional-paths\sall\sgroup-best|advertise\sbest-external|advertise\sdiverse-path\sbackup\smpath|advertise\sdiverse-path\sbackup|advertise\sdiverse-path\smpath)* + \s*(?P<advertisement_interval>advertisement-interval\s\d+)* + \s*(?P<aigp>aigp\ssend\scost-community\s\d+\spoi\sigp-cost\stransitive|aigp\ssend\scost-community\s\d+\spoi\spre-bestpath\stransitive|aigp\ssend\smed|aigp)* + \s*(?P<allow_policy>allow-policy)* + \s*(?P<allowas_in>allowas-in\s\d+)* + \s*(?P<as_override>as-override)* + \s*(?P<bmp_activate>bmp-activate\s(all|server\s\d+))* + \s*(?P<capability>capability\s(both|receive|send))* + \s*(?P<cluster_id>cluster-id\s\S+)* + \s*(?P<default_originate>default-originate\sroute-map|default-originate)* + \s*(?P<description>description\s\S.+)* + \s*(?P<disable_connected_check>disable-connected-check)* + \s*(?P<distribute_list>distribute-list\s\d+\s(in|out))* + \s*(?P<dmzlink_bw>dmzlink-bw)* + \s*(?P<ebgp_multihop>(ebgp-multihop\s\d+|ebgp-multihop))* + \s*(?P<fall_over>fall-over\s((bfd\s(single-hop|multi-hop)|bfd)|route-map\s\S+))* + \s*(?P<filter_list>filter-list\s\d+\s(in|out))* + \s*(?P<ha_mode>ha-mode\s(graceful-restart\sdisable|graceful-restart))* + \s*(?P<inherit>inherit\speer-session\s\S+)* + \s*(?P<local_as>(local-as\s\d+\s(dual-as|(no-prepend\sreplace-as|no-prepend))|local-as))* + \s*(?P<log_neighbor_changes>log-neighbor-changes\sdisable|log-neighbor-changes)* + \s*(?P<maximum_prefix>maximum-prefix\s(\d+\s\d+\s(restart\s\d+|warning-only)|\d+\s(restart\s\d+|warning-only)))* + \s*(?P<next_hop_self>next-hop-self\sall|next-hop-self)* + \s*(?P<next_hop_unchanged>next-hop-unchanged\sallpaths|next-hop-unchanged)* + \s*(?P<password>password\s\S+)* + \s*(?P<path_attribute>path-attribute\s(discard\srange\s\d+\s\d+\sin|discard\s\d+\sin)|path-attribute\s(treat-as-withdraw\srange\s\d+\s\d+\sin|treat-as-withdraw\s\d+\sin))* + \s*(?P<peer_group>peer-group\s\S+)* + \s*(?P<remote_as>remote-as\s\d+)* + \s*(?P<remove_private_as>remove-private-as\sall\sreplace-as|remove-private-as\sall|remove-private-as)* + \s*(?P<route_map>route-map\s\S+\s(in|out))* + \s*(?P<route_reflector_client>route-reflector-client)* + \s*(?P<route_server_client>route-server-client\scontext\s\S+|route-server-client)* + \s*(?P<send_community>send-community\s(both|extended|standard)|send-community)* + #\s*(?P<send_label>send-label\sexplicit-null|send-label)* + \s*(?P<soft_reconfiguration>soft-reconfiguration\sinbound)* + \s*(?P<slow_peer>slow-peer\s(detection.*|split-update-group.*))* + \s*(?P<timers>(timers\s\d+\s\d+\s\d+|timers\s\d+\s\d+))* + \s*(?P<transport>(transport\s(connection-mode\sactive|connection-mode\spassive)|transport\smulti-session|transport\s(path-mtu-discovery\sdisable|path-mtu-discovery)))* + \s*(?P<ttl_security>ttl-security\shops\s\d+)* + \s*(?P<unsuppress_map>unsuppress-map\s\S+)* + \s*(?P<version>version\s\d+)* + \s*(?P<weight>weight\s\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_neighbor, + "compval": "neighbor", + "result": { + "neighbor": [ + { + "address": "{{ neighbor if ':' not in neighbor and '.' in neighbor }}", + "ipv6_address": "{{ neighbor if ':' in neighbor and '.' in neighbor }}", + "tag": "{{ neighbor if ':' not in neighbor and '.' not in neighbor }}", + "activate": "{{ True if activate is defined }}", + "additional_paths": { + "disable": "{{ True if additional_paths is defined and 'disable' in additional_paths }}", + "receive": "{{ True if additional_paths is defined and 'receive' in additional_paths }}", + "send": "{{ True if additional_paths is defined and 'send' in additional_paths }}", + }, + "advertise": { + "additional_paths": { + "all": "{{ True if advertise is defined and 'additional-paths' in advertise and 'all' in advertise }}", + "best": "{{\ + advertise.split('best ')[1].split(' ')[0] if advertise is defined and\ + 'additional-paths' in advertise and 'best' in advertise }}", + "group_best": "{{ True if advertise is defined and 'additional-paths' in advertise and 'group-best' in advertise }}", + }, + "best_external": "{{ True if advertise is defined and 'best-external' in advertise }}", + "diverse_path": { + "backup": "{{ True if advertise is defined and 'diverse-path' in advertise and 'backup' in advertise }}", + "mpath": "{{ True if advertise is defined and 'diverse-path' in advertise and 'mpath' in advertise }}", + }, + }, + "advertisement_interval": "{{ advertisement_interval.split('advertisement-interval ')[1] if advertisement_interval is defined }}", + "aigp": { + "enable": "{{ True if aigp is defined and aigp.split(' ')|length == 1 }}", + "send": { + "cost_community": { + "id": "{{ aigp.split('send cost-community ')[1].split(' ')[0] if aigp is defined and 'send cost-community' in aigp }}", + "poi": { + "igp_cost": "{{ True if aigp is defined and 'poi igp-cost' in aigp }}", + "pre_bestpath": "{{ True if aigp is defined and 'poi pre-bestpath' in aigp }}", + "transitive": "{{ True if aigp is defined and 'transitive' in aigp }}", + }, + }, + "med": "{{ True if aigp is defined and 'send med' in aigp }}", + }, + }, + "allow_policy": "{{ True if allow_policy is defined }}", + "allowas_in": "{{ allowas_in.split('allowas-in ')[1] if allowas_in is defined }}", + "as_override": "{{ True if as_override is defined }}", + "bmp_activate": { + "all": "{{ True if bmp_activate is defined and 'all' in bmp_activate }}", + "server": "{{ bmp_activate.split('server ')[1] if bmp_activate is defined and 'server' in bmp_activate }}", + }, + "capability": { + "both": "{{ True if capability is defined and 'both' in capability }}", + "receive": "{{ True if capability is defined and 'receive' in capability }}", + "send": "{{ True if capability is defined and 'send' in capability }}", + }, + "cluster_id": "{{ cluster_id.split('cluster-id ')[1] if bmp_activate is defined }}", + "default_originate": { + "set": "{{ True if default_originate is defined and default_originate.split(' ')|length == 1 }}", + "route_map": "{{ default_originate.split(' ')[1] if default_originate is defined and default_originate.split(' ')|length > 1 }}", + }, + "description": "{{ description.split('description ')[1] if description is defined }}", + "disable_connected_check": "{{ True if disable_connected_check is defined }}", + "distribute_list": { + "acl": "{{ distribute_list.split(' ')[1] if distribute_list is defined }}", + "in": "{{ True if distribute_list is defined and 'in' in distribute_list }}", + "out": "{{ True if distribute_list is defined and 'out' in distribute_list }}", + }, + "dmzlink_bw": "{{ True if dmzlink_bw is defined }}", + "ebgp_multihop": { + "enable": "{{ True if ebgp_multihop is defined and ebgp_multihop.split(' ')|length == 1 }}", + "hop_count": "{{ ebgp_multihop.split(' ')[1] if ebgp_multihop is defined and len(ebgp_multihop.split(' ')) > 1 }}", + }, + "fall_over": { + "bfd": { + "set": "{{ True if fall_over is defined and\ + 'bfd' in fall_over and 'single-hop' not in fall_over and 'multi-hop' not in fall_over }}", + "multi_hop": "{{ True if fall_over is defined and 'bfd' in fall_over and 'multi-hop' in fall_over }}", + "single_hop": "{{ True if fall_over is defined and 'bfd' in fall_over and 'single-hop' in fall_over }}", + }, + "route_map": "{{ fall_over.split('fall-over route-map ')[1] if fall_over is defined and 'route-map' in fall_over }}", + }, + "filter_list": { + "acl": "{{ filter_list.split(' ')[1] if filter_list is defined }}", + "in": "{{ True if filter_list is defined and 'in' in filter_list }}", + "out": "{{ True if filter_list is defined and 'out' in filter_list }}", + }, + "ha_mode": { + "set": "{{ True if ha_mode is defined and 'disable' not in ha_mode }}", + "disable": "{{ True if ha_mode is defined and 'disable' in ha_mode }}", + }, + "inherit": "{{ inherit.split('inherit peer-session ')[1] if inherit is defined }}", + "local_as": { + "set": "{{ True if local_as is defined and local_as.split(' ')|length == 1 }}", + "number": "{{ local_as.split(' ')[1] if local_as is defined and local_as.split(' ')|length > 1 }}", + "dual_as": "{{ True if local_as is defined and local_as.split(' ')|length > 2 and 'dual-as' in local_as }}", + "no_prepend": { + "set": "{{\ + True if local_as is defined and\ + local_as.split(' ')|length > 2 and 'no-prepend' in local_as and\ + 'replace-as' not in local_as }}", + "replace_as": "{{ True if local_as is defined and\ + local_as.split(' ')|length > 2 and 'no-prepend' in local_as and 'replace-as' in local_as }}", + }, + }, + "log_neighbor_changes": { + "set": "{{ True if log_neighbor_changes is defined and 'disable' not in log_neighbor_changes }}", + "disable": "{{ True if log_neighbor_changes is defined and 'disable' in log_neighbor_changes }}", + }, + "maximum_prefix": { + "max_no": "{{ maximum_prefix.split(' ')[1] if maximum_prefix is defined }}", + "threshold_val": "{{ maximum_prefix.split(' ')[2] if maximum_prefix is defined and\ + maximum_prefix.split(' ')|length > 3 and maximum_prefix.split(' ')[1] != 'restart' }}", + "restart": "{{ maximum_prefix.split('restart ')[1] if maximum_prefix is defined and 'restart' in maximum_prefix }}", + "warning_only": "{{ True if maximum_prefix is defined and 'warning-only' in maximum_prefix }}", + }, + "next_hop_self": { + "set": "{{ True if next_hop_self is defined and next_hop_self.split(' ')|length == 1 }}", + "all": "{{ True if next_hop_self is defined and next_hop_self.split(' ')|length > 1 }}", + }, + "next_hop_unchanged": { + "set": "{{ True if next_hop_unchanged is defined and next_hop_unchanged.split(' ')|length == 1 }}", + "allpaths": "{{ True if next_hop_unchanged is defined and next_hop_unchanged.split(' ')|length > 1 }}", + }, + "password": "{{ password.split(' ')[1] if password is defined }}", + "path_attribute": { + "discard": { + "type": "{% if path_attribute is defined and 'discard range' in path_attribute and\ + path_attribute.split(' ')|length <= 5 %}{{ path_attribute.split(' ')[3] }}{% endif %}", + "range": { + "start": "{% if path_attribute is defined and 'discard range' in path_attribute and\ + path_attribute.split(' ')|length > 5 %}{{ path_attribute.split(' ')[3] }}{% endif %}", + "end": "{% if path_attribute is defined and 'discard range' in path_attribute and\ + path_attribute.split(' ')|length > 5 %}{{ path_attribute.split(' ')[4] }}{% endif %}", + }, + "in": "{% if path_attribute is defined and 'discard range' in path_attribute and\ + 'in' in path_attribute %}{{ True }}{% endif %}", + }, + "treat_as_withdraw": { + "type": "{% if path_attribute is defined and 'discard treat-as-withdraw' in path_attribute and\ + path_attribute.split(' ')|length <= 5 %}{{ path_attribute.split(' ')[3] }}{% endif %}", + "range": { + "start": "{% if path_attribute is defined and 'discard treat-as-withdraw' in path_attribute and\ + path_attribute.split(' ')|length > 5 %}{{ path_attribute.split(' ')[3] }}{% endif %}", + "end": "{% if path_attribute is defined and 'discard treat-as-withdraw' in path_attribute and\ + path_attribute.split(' ')|length > 5 %}{{ path_attribute.split(' ')[4] }}{% endif %}", + }, + "in": "{% if path_attribute is defined and 'discard treat-as-withdraw' in path_attribute and\ + 'in' in path_attribute %}{{ True }}{% endif %}", + }, + }, + "peer_group": "{{ listen.split('peer-group ')[1] if listen is defined and 'peer-group' in listen }}", + "remote_as": "{{ remote_as.split('remote-as ')[1] if remote_as is defined }}", + "remove_private_as": { + "set": "{{ True if remove_private_as is defined and remove_private_as.split(' ')|length == 1 }}", + "all": "{{ True if remove_private_as is defined and remove_private_as.split(' ')|length > 1 and 'all' in remove_private_as }}", + "replace_as": "{{ True if remove_private_as is defined and remove_private_as.split(' ')|length > 1 and\ + 'replace-as' in remove_private_as }}", + }, + "route_map": { + "name": "{{ route_map.split(' ')[1] if route_map is defined }}", + "in": "{{ True if route_map is defined and 'in' in route_map.split(' ') }}", + "out": "{{ True if route_map is defined and 'out' in route_map.split(' ') }}", + }, + "route_reflector_client": "{{ True if route_reflector_client is defined }}", + "route_server_client": { + "set": "{{ True if route_server_client is defined and route_server_client.split(' ')|length == 1 }}", + "context": "{{ route_server_client.split('route-server-client context ')[1] if route_server_client is defined }}", + }, + "send_community": { + "set": "{{ True if send_community is defined and send_community.split(' ')|length == 1 }}", + "both": "{{ True if send_community is defined and 'both' in send_community }}", + "extended": "{{ True if send_community is defined and 'extended' in send_community }}", + "standard": "{{ True if send_community is defined and 'standard' in send_community }}", + }, + "send_label": { + "set": "{{ True if send_label is defined and send_label.split(' ')|length == 1 }}", + "explicit_null": "{{ True if send_label is defined and 'explicit-null' in send_label }}", + }, + "slow_peer": { + "detection": { + "enable": "{{ True if slow_peer is defined and 'disable' not in slow_peer and 'threshold' not in slow_peer }}", + "disable": "{{ True if slow_peer is defined and 'disable' in slow_peer }}", + "threshold": "{{ slow_peer.split('threshold ')[1] if slow_peer is defined and 'threshold' in slow_peer }}", + }, + "split_update_group": { + "dynamic": { + "enable": "{{ True if slow_peer is defined and 'split-update-group' in slow_peer and\ + 'disable' not in slow_peer and 'threshold' not in slow_peer }}", + "disable": "{{ True if slow_peer is defined and 'split-update-group' in slow_peer and 'disable' in slow_peer }}", + "permanent": "{{ True if slow_peer is defined and 'split-update-group' in slow_peer and 'permanent' in slow_peer }}", + }, + "static": "{{ True if slow_peer is defined and 'split-update-group' in slow_peer and 'static' in slow_peer }}", + }, + }, + "soft_reconfiguration": "{{ True if soft_reconfiguration is defined }}", + "timers": { + "interval": "{{ timers.split(' ')[1] if timers is defined }}", + "holdtime": "{{ timers.split(' ')[2] if timers is defined }}", + "min_holdtime": "{{ timers.split(' ')[3] if timers is defined and timers.split(' ')|length > 3 }}", + }, + "translate_update": { + "set": "{{ True if translate_update is defined and translate_update.split(' ')|length == 1 }}", + "nlri": { + "multicast": "{{ True if translate_update is defined and 'nlri multicast' in translate_update }}", + "unicast": "{{ True if translate_update is defined and 'nlri unicast' in translate_update }}", + }, + }, + "transport": { + "connection_mode": { + "active": "{{ True if transport is defined and 'connection-mode active' in transport }}", + "passive": "{{ True if transport is defined and 'connection-mode passive' in transport }}", + }, + "multi_session": "{{ True if transport is defined and 'multi-session' in transport }}", + "path_mtu_discovery": { + "set": "{{ True if transport is defined and 'path-mtu-discovery' in transport and 'disable' not in transport }}", + "disable": "{{ True if transport is defined and 'path-mtu-discovery' in transport and 'disable' in transport }}", + }, + }, + "ttl_security": "{{ ttl_security.split('ttl-security hops ')[1] if ttl_security is defined }}", + "unsuppress_map": "{{ unsuppress_map.split('unsuppress-map ')[1] if unsuppress_map is defined }}", + "version": "{{ version.split('version ')[1] if version is defined }}", + "weight": "{{ weight.split('weight ')[1] if weight is defined }}", + } + ] + }, + }, + { + "name": "redistribute", + "getval": re.compile( + r"""\s*redistribute* + \s*(?P<application>application\s\S+\smetric\s\d+\sroute-map\s\S+|application\s\S+\s(metric\s\d+|route-map\s\S+))* + \s*(?P<bgp>bgp\s\d+\smetric\s\d+\sroute-map\s\S+|bgp\s\d+\s(metric\s\d+\sroute-map\s\S+))* + \s*(?P<connected>connected\s(metric\s\d+\sroute-map\s\S+|metric\s\d+))* + \s*(?P<eigrp>eigrp\s\d+\smetric\s\d+\sroute-map\s\S+|eigrp\s\d+\s(metric\s\d+\sroute-map\s\S+))* + \s*(?P<isis>isis\s\S+\sclns\smetric\s\d+\sroute-map\s\S+|isis\s\S+\sip\smetric\s\d+\sroute-map\s\S+|isis\s\S+\s(clns|ip)\s(metric\s\d+\sroute-map\s\S+))* + \s*(?P<iso_igrp>iso-igrp\s\S+\sroute-map\s\S+|iso-igrp\s\S+)* + \s*(?P<lisp>lisp\smetric\s\d+\sroute-map\s\S+|lisp\s(metric\s\d+\sroute-map\s\S+))* + \s*(?P<mobile>mobile\smetric\s\d+\sroute-map\s\S+|mobile\s(metric\s\d+\sroute-map\s\S+))* + \s*(?P<odr>odr\smetric\s\d+\sroute-map\s\S+|odr\s(metric\s\d+\sroute-map\s\S+))* + \s*(?P<ospf>ospf\s\d+(\s.*))* + \s*(?P<ospfv3>ospfv3\s\d+(\s.*))* + \s*(?P<rip>rip\smetric\s\d+\sroute-map\s\S+|rip\s(metric\s\d+\sroute-map\s\S+))* + \s*(?P<static>static\sclns\smetric\s\d+\sroute-map\s\S+|static\sip\smetric\s\d+\sroute-map\s\S+|static\s(clns|ip)\s(metric\s\d+\sroute-map\s\S+))* + \s*(?P<vrf>vrf\s\S+|vrf\sglobal)* + $""", + re.VERBOSE, + ), + "compval": "redistribute", + "setval": _tmplt_redistribute, + "result": { + "redistribute": [ + { + "application": { + "name": "{{ application.split(' ')[1] if application is defined }}", + "metric": "{{ application.split('metric ')[1].split(' ')[0] if application is defined and 'metric' in application }}", + "route_map": "{{ application.split('route-map ')[1].split(' ')[0] if application is defined and 'route-map' in application }}", + }, + "bgp": { + "as_number": "{{ bgp.split(' ')[1] if bgp is defined }}", + "metric": "{{ bgp.split('metric ')[1].split(' ')[0] if bgp is defined and 'metric' in bgp }}", + "route_map": "{{ bgp.split('route-map ')[1].split(' ')[0] if bgp is defined and 'route-map' in bgp }}", + }, + "connected": { + "metric": "{{ connected.split('metric ')[1].split(' ')[0] if connected is defined and 'metric' in connected }}", + "route_map": "{{ connected.split('route-map ')[1].split(' ')[0] if connected is defined and 'route-map' in connected }}", + }, + "eigrp": { + "as_number": "{{ eigrp.split(' ')[1] if eigrp is defined }}", + "metric": "{{ eigrp.split('metric ')[1].split(' ')[0] if eigrp is defined and 'metric' in eigrp }}", + "route_map": "{{ eigrp.split('route-map ')[1].split(' ')[0] if eigrp is defined and 'route-map' in eigrp }}", + }, + "isis": { + "area_tag": "{{ isis.split(' ')[1] if isis is defined }}", + "clns": "{{ True if isis is defined and 'clns' in isis }}", + "ip": "{{ True if isis is defined and 'ip' in isis }}", + "metric": "{{ isis.split('metric ')[1].split(' ')[0] if isis is defined and 'metric' in isis }}", + "route_map": "{{ isis.split('route-map ')[1].split(' ')[0] if isis is defined and 'route-map' in isis }}", + }, + "iso_igrp": { + "area_tag": "{{ iso_igrp.split(' ')[1] if iso_igrp is defined }}", + "route_map": "{{ iso_igrp.split('route-map ')[1].split(' ')[0] if iso_igrp is defined and 'route-map' in iso_igrp }}", + }, + "lisp": { + "metric": "{{ lisp.split('metric ')[1].split(' ')[0] if lisp is defined and 'metric' in lisp }}", + "route_map": "{{ lisp.split('route-map ')[1].split(' ')[0] if lisp is defined and 'route-map' in lisp }}", + }, + "mobile": { + "metric": "{{ mobile.split('metric ')[1].split(' ')[0] if mobile is defined and 'metric' in mobile }}", + "route_map": "{{ mobile.split('route-map ')[1].split(' ')[0] if mobile is defined and 'route-map' in mobile }}", + }, + "odr": { + "metric": "{{ odr.split('metric ')[1].split(' ')[0] if odr is defined and 'metric' in odr }}", + "route_map": "{{ odr.split('route-map ')[1].split(' ')[0] if odr is defined and 'route-map' in odr }}", + }, + "ospf": { + "process_id": "{{ ospf.split(' ')[1] if ospf is defined }}", + "match": { + "external": "{{ True if ospf is defined and 'external' in ospf }}", + "internal": "{{ True if ospf is defined and 'internal' in ospf }}", + "nssa_external": "{{ True if ospf is defined and 'nssa-external' in ospf }}", + "type_1": "{{ True if ospf is defined and '1' in ospf }}", + "type_2": "{{ True if ospf is defined and '2' in ospf }}", + }, + "metric": "{{ ospf.split('metric ')[1].split(' ')[0] if ospf is defined and 'metric' in ospf }}", + "route_map": "{{ ospf.split('route-map ')[1].split(' ')[0] if ospf is defined and 'route-map' in ospf }}", + "vrf": "{{ ospf.split('vrf ')[1].split(' ')[0] if ospf is defined and 'vrf' in ospf }}", + }, + "ospfv3": { + "process_id": "{{ ospfv3.split(' ')[1] if ospf is defined }}", + "match": { + "external": "{{ True if ospfv3 is defined and 'external' in ospfv3 }}", + "internal": "{{ True if ospfv3 is defined and 'internal' in ospfv3 }}", + "nssa_external": "{{ True if ospfv3 is defined and 'nssa-external' in ospfv3 }}", + "type_1": "{{ True if ospfv3 is defined and '1' in ospfv3 }}", + "type_2": "{{ True if ospfv3 is defined and '2' in ospfv3 }}", + }, + "metric": "{{ ospfv3.split('metric ')[1].split(' ')[0] if ospfv3 is defined and 'metric' in ospfv3 }}", + "route_map": "{{ ospfv3.split('route-map ')[1].split(' ')[0] if ospfv3 is defined and 'route-map' in ospfv3 }}", + "vrf": "{{ ospfv3.split('vrf ')[1].split(' ')[0] if ospfv3 is defined and 'vrf' in ospfv3 }}", + }, + "rip": { + "metric": "{{ rip.split('metric ')[1].split(' ')[0] if rip is defined and 'metric' in rip }}", + "route_map": "{{ rip.split('route-map ')[1].split(' ')[0] if rip is defined and 'route-map' in rip }}", + }, + "static": { + "clns": "{{ True if static is defined and 'clns' in static }}", + "ip": "{{ True if static is defined and 'ip' in static }}", + "metric": "{{ static.split('metric ')[1].split(' ')[0] if static is defined and 'metric' in static }}", + "route_map": "{{ static.split('route-map ')[1].split(' ')[0] if static is defined and 'route-map' in static }}", + }, + "vrf": { + "name": "{{ vrf.split('vrf ')[1].split(' ')[0] if vrf is defined and 'vrf' in vrf and 'global' not in vrf }}", + "global": "{{ True if vrf is defined and 'vrf' in vrf and 'global' in vrf }}", + }, + } + ] + }, + }, + { + "name": "route_server_context", + "getval": re.compile( + r"""\s*(?P<route_server_context>route-server-context\s\S+)""", + re.VERBOSE, + ), + "compval": "route_server_context", + "setval": "", + "result": { + "route_server_context": "{{ route_server_context.split('route-server-context ')[1] if route_server_context is defined }}" + }, + }, + { + "name": "synchronization", + "getval": re.compile( + r"""\s*(?P<synchronization>synchronization)""", re.VERBOSE + ), + "compval": "synchronization", + "setval": "synchronization", + "result": { + "synchronization": "{{ Trues if synchronization is defined }}" + }, + }, + { + "name": "table_map", + "getval": re.compile( + r"""\s*(?P<table_map>route-server-context\s\S+)""", re.VERBOSE + ), + "compval": "table_map", + "setval": "", + "result": { + "table_map": "{{ table_map.split('route-server-context ')[1] if table_map is defined }}" + }, + }, + { + "name": "timers", + "getval": re.compile( + r"""\s*(?P<timers>timers\sbgp\s\d+\s\d+\s\d+)""", re.VERBOSE + ), + "compval": "timers", + "setval": _tmplt_bgp_timers, + "result": { + "timers": { + "keepalive": "{{ timers.split(' ')[2] if timers is defined }}", + "holdtime": "{{ timers.split(' ')[3] if timers is defined }}", + "min_holdtime": "{{ timers.split(' ')[4] if timers is defined and timers.split(' ')|length > 4 }}", + } + }, + }, + ] diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospf_interfaces.py new file mode 100644 index 00000000..419c4859 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospf_interfaces.py @@ -0,0 +1,895 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +""" +The Ospf_interfaces parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import ( + NetworkTemplate, +) + + +def _tmplt_ospf_interface(config_data): + command = "interface {name}".format(**config_data) + return command + + +def _tmplt_ospf_interface_process(config_data): + if "process" in config_data: + if config_data.get("afi") == "ipv4": + command = "ip ospf {id} area {area_id}".format( + **config_data["process"] + ) + elif config_data.get("afi") == "ipv6": + command = "ipv6 ospf {id} area {area_id}".format( + **config_data["process"] + ) + if "secondaries" in config_data["process"]: + command += " secondaries" + return command + + +def _tmplt_ip_ospf_authentication(config_data): + if "authentication" in config_data: + if config_data.get("afi") == "ipv4": + command = "ip ospf authentication" + elif config_data.get("afi") == "ipv6": + command = "ipv6 ospf authentication" + if "key_chain" in config_data["authentication"]: + command += " key-chain {key_chain}".format( + **config_data["authentication"] + ) + elif "message_digest" in config_data["authentication"]: + command += " message-digest" + elif "null" in config_data["authentication"]: + command += " null" + return command + + +def _tmplt_ip_ospf_cost(config_data): + if "cost" in config_data: + if config_data.get("afi") == "ipv4": + command = "ip ospf cost {interface_cost}".format( + **config_data["cost"] + ) + elif config_data.get("afi") == "ipv6": + command = "ipv6 ospf cost" + if "interface_cost" in config_data["cost"]: + command = "ipv6 ospf cost {interface_cost}".format( + **config_data["cost"] + ) + if "dynamic_cost" in config_data["cost"]: + if "default" in config_data["cost"]["dynamic_cost"]: + command += " dynamic default {default}".format( + **config_data["cost"]["dynamic_cost"] + ) + elif "hysteresis" in config_data["cost"]["dynamic_cost"]: + command += " dynamic hysteresis" + if ( + "percent" + in config_data["cost"]["dynamic_cost"]["hysteresis"] + ): + command += " percent {percent}".format( + **config_data["cost"]["dynamic_cost"]["hysteresis"] + ) + elif ( + "threshold" + in config_data["cost"]["dynamic_cost"]["hysteresis"] + ): + command += " threshold {threshold}".format( + **config_data["cost"]["dynamic_cost"]["hysteresis"] + ) + elif "weight" in config_data["cost"]["dynamic_cost"]: + command += " dynamic weight" + if ( + "l2_factor" + in config_data["cost"]["dynamic_cost"]["weight"] + ): + command += " L2-factor {l2_factor}".format( + **config_data["cost"]["dynamic_cost"]["weight"] + ) + elif ( + "latency" + in config_data["cost"]["dynamic_cost"]["weight"] + ): + command += " latency {latency}".format( + **config_data["cost"]["dynamic_cost"]["weight"] + ) + elif ( + "oc" in config_data["cost"]["dynamic_cost"]["weight"] + and config_data["cost"]["dynamic_cost"]["weight"]["oc"] + ): + command += " oc cdr" + elif ( + "resources" + in config_data["cost"]["dynamic_cost"]["weight"] + ): + command += " resources {resources}".format( + **config_data["cost"]["dynamic_cost"]["weight"] + ) + elif ( + "throughput" + in config_data["cost"]["dynamic_cost"]["weight"] + ): + command += " throughput {throughput}".format( + **config_data["cost"]["dynamic_cost"]["weight"] + ) + return command + + +def _tmplt_ip_ospf_dead_interval(config_data): + if "dead_interval" in config_data: + if config_data.get("afi") == "ipv4": + command = "ip ospf dead-interval" + if "time" in config_data["dead_interval"]: + command += " {time}".format(**config_data["dead_interval"]) + elif "minimal" in config_data["dead_interval"]: + command += " minimal hello-multiplier {minimal}".format( + **config_data["dead_interval"] + ) + elif config_data.get("afi") == "ipv6": + command = "ipv6 ospf dead-interval {time}".format( + **config_data["dead_interval"] + ) + return command + + +def _tmplt_ip_ospf_demand_circuit(config_data): + if "demand_circuit" in config_data: + if config_data.get("afi") == "ipv4": + command = "ip ospf demand-circuit" + if config_data["demand_circuit"]["ignore"]: + command += " ignore" + elif config_data["demand_circuit"]["enable"]: + return command + elif config_data.get("afi") == "ipv6": + command = "ipv6 ospf demand-circuit" + if config_data["demand_circuit"]["enable"]: + return command + elif config_data["demand_circuit"]["ignore"]: + command += " ignore" + elif config_data["demand_circuit"]["disable"]: + command += " disable" + return command + + +def _tmplt_ip_ospf_manet(config_data): + if "manet" in config_data: + command = "ipv6 ospf manet peering" + if "cost" in config_data["manet"]: + command += " cost" + if "percent" in config_data["manet"]["cost"]: + command += " percent {percent}".format( + **config_data["manet"]["cost"] + ) + elif "threshold" in config_data["manet"]["cost"]: + command += " threshold {threshold}".format( + **config_data["manet"]["cost"] + ) + elif "link_metrics" in config_data["manet"]: + command += " link-metrics" + if "cost_threshold" in config_data["manet"]["link_metrics"]: + command += " {cost_threshold}".format( + **config_data["manet"]["link_metrics"] + ) + return command + + +def _tmplt_ip_ospf_multi_area(config_data): + if "multi_area" in config_data: + command = "ip ospf multi-area {id}".format(**config_data["multi_area"]) + if "cost" in config_data["multi_area"]: + command += " cost {cost}".format(**config_data["multi_area"]) + return command + + +def _tmplt_ip_ospf_neighbor(config_data): + if "neighbor" in config_data: + command = "ipv6 ospf neighbor {address}".format( + **config_data["neighbor"] + ) + if "cost" in config_data["neighbor"]: + command += " cost {cost}".format(**config_data["neighbor"]) + if ( + "database_filter" in config_data["neighbor"] + and config_data["neighbor"]["database_filter"] + ): + command += " database-filter all out" + if "poll_interval" in config_data["neighbor"]: + command += " poll-interval {poll_interval}".format( + **config_data["neighbor"]["poll_interval"] + ) + if "priority" in config_data["neighbor"]: + command += " priority {priority}".format( + **config_data["neighbor"]["priority"] + ) + return command + + +def _tmplt_ip_ospf_network(config_data): + if "network" in config_data: + if config_data.get("afi") == "ipv4": + command = "ip ospf network" + elif config_data.get("afi") == "ipv6": + command = "ipv6 ospf network" + if "broadcast" in config_data["network"]: + command += " broadcast" + if "manet" in config_data["network"]: + command += " manet" + if "non_broadcast" in config_data["network"]: + command += " non-broadcast" + if "point_to_multipoint" in config_data["network"]: + command += " point-to-multipoint" + if "point_to_point" in config_data["network"]: + command += " point-to-point" + return command + + +def _tmplt_ip_ospf_ttl_security(config_data): + if "ttl_security" in config_data: + command = "ip ospf ttl-security" + if "hops" in config_data["ttl_security"]: + command += " hops {hops}".format(**config_data["ttl_security"]) + return command + + +class Ospf_InterfacesTemplate(NetworkTemplate): + def __init__(self, lines=None): + super(Ospf_InterfacesTemplate, self).__init__(lines=lines, tmplt=self) + + PARSERS = [ + { + "name": "name", + "getval": re.compile( + r""" + ^interface + \s(?P<name>\S+)$""", + re.VERBOSE, + ), + "setval": "interface {{ name }}", + "result": { + "{{ name }}": {"name": "{{ name }}", "address_family": {}} + }, + "shared": True, + }, + { + "name": "process", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<id>\d+)* + \s*(?P<area>area\s\d+)* + \s*(?P<secondaries>secondaries)* + + \s*(?P<instance>instance*\s*\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_interface_process, + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "process": { + "id": "{{ id }}", + "area_id": "{{ area.split(' ')[1] }}", + "secondaries": "{{ True if secondaries is defined }}", + "instance_id": "{{ instance.split(' ')[1]}}", + }, + } + } + } + }, + }, + { + "name": "adjacency", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<adjacency>adjacency*\s*stagger)* + \s*(?P<disable>disable)* + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} adjacency stagger disable", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "adjacency": "{{ True if adjacency is defined }}", + } + } + } + }, + }, + { + "name": "authentication", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*authentication* + \s*(?P<key_chain>key-chain*\s*\S+)* + \s*(?P<message_digest>message-digest)* + \s*(?P<null>null)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_ip_ospf_authentication, + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "authentication": { + "key_chain": "{{ key_chain.split(' ')[1] }}", + "message_digest": "{{ True if message_digest is defined }}", + "null": "{{ True if null is defined }}", + }, + } + } + } + }, + }, + { + "name": "bfd", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<bfd>bfd)* + \s*(?P<disable>disable)* + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} bfd", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "bfd": "{{ True if bfd is defined }}", + } + } + } + }, + }, + { + "name": "cost_ip", + "getval": re.compile( + r""" + \s+(?P<afi>ip)* + \s*ospf* + \s*(?P<cost>cost*\s*\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_ip_ospf_cost, + "compval": "cost.interface_cost", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "cost": { + "interface_cost": "{{ cost.split(' ')[1] }}" + }, + } + } + } + }, + }, + { + "name": "cost_ipv6_dynamic_cost", + "getval": re.compile( + r""" + \s+(?P<afi>ipv6)* + \s*ospf* + \s*cost* + \s*(?P<interface_cost>\d+)* + \s*dynamic* + \s*(?P<default>default*\s*\d+)* + \s*(?P<hysteresis>hysteresis*\s*)* + \s*(?P<h_params>(percent|threshold)*\s*\d+)* + \s*(?P<weight>weight)* + \s*(?P<w_params>(L2-factor|latency|resources|throughput)*\s*\d+)* + \s*(?P<weight_oc>oc)* + $""", + re.VERBOSE, + ), + "compval": "cost.dynamic_cost", + "setval": _tmplt_ip_ospf_cost, + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "cost": { + "interface_cost": "{{ interface_cost }}", + "dynamic_cost": { + "default": "{{ default.split(' ')[1] }}", + "hysteresis": { + "percent": "{{ h_params.split(' ')[1] if h_params is defined and 'percent' in h_params }}", + "threshold": "{{ h_params.split(' ')[1] if h_params is defined and 'threshold' in h_params }}", + }, + "weight": { + "l2_factor": "{{ w_params.split(' ')[1] if w_params is defined and 'L2-factor' in w_params }}", + "latency": "{{ w_params.split(' ')[1] if w_params is defined and 'latency' in w_params }}", + "oc": "{{ True if weight_oc is defined}}", + "resources": "{{ w_params.split(' ')[1] if w_params is defined and 'resources' in w_params }}", + "throughput": "{{ w_params.split(' ')[1] if w_params is defined and 'throughput' in w_params }}", + }, + }, + }, + } + } + } + }, + }, + { + "name": "database_filter", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<database_filter>database-filter\sall\sout)* + \s*(?P<disable>disable)* + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} database-filter all out", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "database_filter": "{{ True if database_filter is defined }}", + } + } + } + }, + }, + { + "name": "dead_interval", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<dead_interval>dead-interval)* + \s*(?P<seconds>\d+)* + \s*(?P<minimal>minimal\shello-multiplier\s\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_ip_ospf_dead_interval, + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "dead_interval": { + "time": "{{ seconds }}", + "minimal": { + "hello_multiplier": "{{ minimal.split(' ')[2] }}" + }, + }, + } + } + } + }, + }, + { + "name": "demand_circuit", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<demand_circuit>demand-circuit)* + \s*(?P<ignore>ignore)* + \s*(?P<disable>disable)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_ip_ospf_demand_circuit, + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "demand_circuit": { + "enable": "{{ True if demand_circuit is defined and ignore is not defined }}", + "ignore": "{{ True if ignore is defined }}", + "disable": "{{ True if disable is defined }}", + }, + } + } + } + }, + }, + { + "name": "flood_reduction", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<flood_reduction>flood-reduction)* + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} flood-reduction", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "flood_reduction": "{{ True if flood_reduction is defined }}", + } + } + } + }, + }, + { + "name": "hello_interval", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<hello_interval>hello-interval\s\d+)* + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} hello-interval {{ hello_interval }}", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "hello_interval": "{{ hello_interval.split(' ')[1] }}", + } + } + } + }, + }, + { + "name": "lls", + "getval": re.compile( + r""" + \s+(?P<afi>ip)* + \s*ospf* + \s*(?P<lls>lls)* + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} lls", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "lls": "{{ True if lls is defined }}", + } + } + } + }, + }, + { + "name": "manet", + "getval": re.compile( + r""" + \s+(?P<afi>ipv6)* + \s*ospf* + \s*(?P<manet>manet\speering)* + \s*(?P<cost>(cost\spercent|cost\sthreshold)\s\d+)* + \s*(?P<link_metrics>link-metrics\s\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_ip_ospf_manet, + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "manet": { + "cost": { + "percent": "{{ cost.split(' ')[2] }}", + "threshold": "{{ cost.split(' ')[2] }}", + }, + "link_metrics": { + "set": "{{ True if link_metrics is not defined and link_metrics is defined }}", + "cost_threshold": "{{ link_metrics.split(' ')[1] }}", + }, + }, + } + } + } + }, + }, + { + "name": "mtu_ignore", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<mtu_ignore>mtu-ignore)* + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} mtu-ignore", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "mtu_ignore": "{{ True if mtu_ignore is defined }}", + } + } + } + }, + }, + { + "name": "multi_area", + "getval": re.compile( + r""" + \s+(?P<afi>ip)* + \s*ospf* + \s*(?P<multi_area>multi-area\s\d+)* + \s*(?P<cost>cost\s\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_ip_ospf_multi_area, + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "multi_area": { + "id": "{{ multi_area.split(' ')[1] }}", + "cost": "{{ cost.split(' ')[1] }}", + }, + } + } + } + }, + }, + { + "name": "neighbor", + "getval": re.compile( + r""" + \s+(?P<afi>ipv6)* + \s*ospf* + \s*neighbor* + \s*(?P<address>(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\S+)* + \s*(?P<cost>cost\s\d+)* + \s*(?P<database_filter>database-filter\sall\sout)* + \s*(?P<poll_interval>poll-interval\s\d+)* + \s*(?P<priority>priority\s\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_ip_ospf_neighbor, + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "neighbor": { + "address": "{{ address }}", + "cost": "{{ cost.split(' ')[1] }}", + "database_filter": "{{ True if database_filter is defined }}", + "poll_interval": "{{ poll_interval.split(' ')[1] }}", + "priority": "{{ priority.split(' ')[1] }}", + }, + } + } + } + }, + }, + { + "name": "network", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<network>network)* + \s*(?P<broadcast>broadcast)* + \s*(?P<manet>manet)* + \s*(?P<non_broadcast>non-broadcast)* + \s*(?P<point_to_multipoint>point-to-multipoint)* + \s*(?P<point_to_point>point-to-point)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_ip_ospf_network, + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "network": { + "broadcast": "{{ True if broadcast is defined }}", + "manet": "{{ True if manet is defined }}", + "non_broadcast": "{{ True if non_broadcast is defined }}", + "point_to_multipoint": "{{ True if point_to_multipoint is defined }}", + "point_to_point": "{{ True if point_to_point is defined }}", + }, + } + } + } + }, + }, + { + "name": "prefix_suppression", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<prefix_suppression>prefix-suppression)* + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} prefix-suppression", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "prefix_suppression": "{{ True if prefix_suppression is defined }}", + } + } + } + }, + }, + { + "name": "priority", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<priority>priority*\s*\d+)* + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} priority {{ priority }}", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "priority": "{{ priority.split(' ')[1] }}", + } + } + } + }, + }, + { + "name": "resync_timeout", + "getval": re.compile( + r""" + \s+(?P<afi>ip)* + \s*ospf* + \s*(?P<resync_timeout>resync-timeout*\s*\d+)* + $""", + re.VERBOSE, + ), + "setval": "ip ospf resync-timeout {{ resync_timeout }}", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "resync_timeout": "{{ resync_timeout.split(' ')[1] }}", + } + } + } + }, + }, + { + "name": "retransmit_interval", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<retransmit_interval>retransmit-interval*\s*\d+)* + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} retransmit-interval {{ retransmit_interval }}", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "retransmit_interval": "{{ retransmit_interval.split(' ')[1] }}", + } + } + } + }, + }, + { + "name": "shutdown", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)* + \s*ospf* + \s*(?P<shutdown>shutdown)* + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} shutdown", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "shutdown": "{{ True if shutdown is defined }}", + } + } + } + }, + }, + { + "name": "transmit_delay", + "getval": re.compile( + r""" + \s+(?P<afi>ipv6)* + \s*ospf* + \s*(?P<transmit_delay>transmit-delay*\s*\d+) + *$""", + re.VERBOSE, + ), + "setval": "ipv6 ospf transmit-delay {{ transmit_delay }}", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "transmit_delay": "{{ transmit_delay.split(' ')[1] }}", + } + } + } + }, + }, + { + "name": "ttl_security", + "getval": re.compile( + r""" + \s+(?P<afi>ip)* + \s*ospf* + \s*(?P<ttl_security>ttl-security)* + \s*(?P<hops>hops\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ip_ospf_ttl_security, + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "ttl_security": { + "set": "{{ True if hops is not defined and ttl_security is defined }}", + "hops": "{{ hops.split(' ')[1] }}", + }, + } + } + } + }, + }, + ] diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv2.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv2.py new file mode 100644 index 00000000..9a09e237 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv2.py @@ -0,0 +1,1947 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import re +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import ( + NetworkTemplate, +) + + +def _tmplt_ospf_vrf_cmd(process): + command = "router ospf {process_id}".format(**process) + if "vrf" in process: + command += " vrf {vrf}".format(**process) + return command + + +def _tmplt_ospf_adjacency_cmd(config_data): + if "adjacency" in config_data: + command = "adjacency stagger " + if "none" in config_data["adjacency"]: + command += " none" + else: + command += " {min_adjacency}".format(**config_data["adjacency"]) + if "max_adjacency" in config_data["adjacency"]: + command += " {min_adjacency}".format(**config_data["adjacency"]) + return command + + +def _tmplt_ospf_address_family_cmd(config_data): + if "address_family" in config_data: + command = ["address-family ipv4 multicast", "exit-address-family"] + if config_data["address_family"].get("topology"): + if "base" in config_data["address_family"].get("topology"): + command.insert(1, "topology base") + elif "name" in config_data["address_family"].get("topology"): + cmd = "topology {name}".format( + **config_data["address_family"].get("topology") + ) + if "tid" in config_data["address_family"].get("topology"): + cmd += " tid {tid}".format( + **config_data["address_family"].get("topology") + ) + command.insert(1, cmd) + return command + + +def _tmplt_ospf_area_authentication(config_data): + if "authentication" in config_data: + command = "area {area_id} authentication".format(**config_data) + if config_data["authentication"].get("message_digest"): + command += " message-digest" + return command + + +def _tmplt_ospf_area_filter(config_data): + if "filter_list" in config_data: + command = [] + for key, value in iteritems(config_data.get("filter_list")): + cmd = "area {area_id}".format(**config_data) + if value.get("name") and value.get("direction"): + cmd += " filter-list prefix {name} {direction}".format(**value) + command.append(cmd) + return command + + +def _tmplt_ospf_area_nssa(config_data): + if "nssa" in config_data: + command = "area {area_id} nssa".format(**config_data) + if "default_information_originate" in config_data["nssa"]: + command += " default-information-originate" + if ( + "metric" + in config_data["nssa"]["default_information_originate"] + ): + command += " metric {metric}".format( + **config_data["nssa"]["default_information_originate"] + ) + if ( + "metric_type" + in config_data["nssa"]["default_information_originate"] + ): + command += " metric-type {metric_type}".format( + **config_data["nssa"]["default_information_originate"] + ) + if ( + "nssa_only" + in config_data["nssa"]["default_information_originate"] + ): + command += " nssa-only" + if config_data["nssa"].get("no_ext_capability"): + command += " no-ext-capability" + if config_data["nssa"].get("no_redistribution"): + command += " no-redistribution" + if config_data["nssa"].get("no_summary"): + command += " no-summary" + return command + + +def _tmplt_ospf_area_nssa_translate(config_data): + if "nssa" in config_data: + command = "area {area_id} nssa".format(**config_data) + if "translate" in config_data["nssa"]: + command += " translate type7 {translate}".format( + **config_data["nssa"] + ) + return command + + +def _tmplt_ospf_area_ranges(config_data): + if "ranges" in config_data: + commands = [] + for k, v in iteritems(config_data["ranges"]): + cmd = "area {area_id} range".format(**config_data) + temp_cmd = " {address} {netmask}".format(**v) + if "advertise" in v: + temp_cmd += " advertise" + elif "not_advertise" in v: + temp_cmd += " not-advertise" + if "cost" in v: + temp_cmd += " cost {cost}".format(**v) + cmd += temp_cmd + commands.append(cmd) + return commands + + +def _tmplt_ospf_area_sham_link(config_data): + if "sham_link" in config_data: + command = "area {area_id} sham-link".format(**config_data) + if "source" in config_data["sham_link"]: + command += " {source} {destination}".format( + **config_data["sham_link"] + ) + if "cost" in config_data["sham_link"]: + command += " cost {cost}".format(**config_data["sham_link"]) + if "ttl_security" in config_data["sham_link"]: + command += " ttl-security hops {ttl_security}".format( + **config_data["sham_link"] + ) + return command + + +def _tmplt_ospf_area_stub_link(config_data): + if "stub" in config_data: + command = "area {area_id} stub".format(**config_data) + if "no_ext_capability" in config_data["stub"]: + command += " no-ext-capability" + if "no_summary" in config_data["stub"]: + command += " no-summary" + return command + + +def _tmplt_ospf_auto_cost(config_data): + if "auto_cost" in config_data: + command = "auto-cost" + if "reference_bandwidth" in config_data["auto_cost"]: + command += " reference-bandwidth {reference_bandwidth}".format( + **config_data["auto_cost"] + ) + return command + + +def _tmplt_ospf_capability(config_data): + if "capability" in config_data: + if "lls" in config_data["capability"]: + command = "capability lls" + elif "opaque" in config_data["capability"]: + command = "capability opaque" + elif "transit" in config_data["capability"]: + command = "capability transit" + elif "vrf_lite" in config_data["capability"]: + command = "capability vrf_lite" + return command + + +def _tmplt_ospf_compatible(config_data): + if "compatible" in config_data: + if "rfc1583" in config_data["compatible"]: + command = "compatible rfc1583" + elif "rfc1587" in config_data["compatible"]: + command = "compatible rfc1587" + elif "rfc5243" in config_data["compatible"]: + command = "compatible rfc5243" + return command + + +def _tmplt_ospf_default_information(config_data): + if "default_information" in config_data: + command = "default-information" + if "originate" in config_data["default_information"]: + command += " originate" + if "always" in config_data["default_information"]: + command += " always" + if "metric" in config_data["default_information"]: + command += " metric {metric}".format( + **config_data["default_information"] + ) + if "metric_type" in config_data["default_information"]: + command += " metric-type {metric_type}".format( + **config_data["default_information"] + ) + if "metric" in config_data["default_information"]: + command += " route-map {route_map}".format( + **config_data["default_information"] + ) + return command + + +def _tmplt_ospf_discard_route(config_data): + if "discard_route" in config_data: + command = "discard-route" + if "external" in config_data["discard_route"]: + command += " external {external}".format( + **config_data["discard_route"] + ) + if "internal" in config_data["discard_route"]: + command += " internal {internal}".format( + **config_data["discard_route"] + ) + return command + + +def _tmplt_ospf_distance_admin_distance(config_data): + if "admin_distance" in config_data["distance"]: + command = "distance {distance}".format( + **config_data["distance"]["admin_distance"] + ) + if "address" in config_data["distance"]["admin_distance"]: + command += " {address} {wildcard_bits}".format( + **config_data["distance"]["admin_distance"] + ) + if "acl" in config_data["distance"]["admin_distance"]: + command += " {acl}".format( + **config_data["distance"]["admin_distance"] + ) + return command + + +def _tmplt_ospf_distance_ospf(config_data): + if "ospf" in config_data["distance"]: + command = "distance ospf" + if "inter_area" in config_data["distance"]["ospf"]: + command += " inter-area {inter_area}".format( + **config_data["distance"]["ospf"] + ) + if config_data["distance"].get("ospf").get("intra_area"): + command += " intra-area {intra_area}".format( + **config_data["distance"]["ospf"] + ) + if config_data["distance"].get("ospf").get("external"): + command += " external {external}".format( + **config_data["distance"]["ospf"] + ) + return command + + +def _tmplt_ospf_distribute_list_acls(config_data): + if "acls" in config_data.get("distribute_list"): + command = [] + for k, v in iteritems(config_data["distribute_list"]["acls"]): + cmd = "distribute-list {name} {direction}".format(**v) + if "interface" in v: + cmd += " {interface}".format(**v) + if "protocol" in v: + cmd += " {protocol}".format(**v) + command.append(cmd) + return command + + +def _tmplt_ospf_distribute_list_prefix(config_data): + if "prefix" in config_data.get("distribute_list"): + command = "distribute-list prefix {name}".format( + **config_data["distribute_list"]["prefix"] + ) + if "gateway_name" in config_data["distribute_list"]["prefix"]: + command += " gateway {gateway_name}".format( + **config_data["distribute_list"]["prefix"] + ) + if "direction" in config_data["distribute_list"]["prefix"]: + command += " {direction}".format( + **config_data["distribute_list"]["prefix"] + ) + if "interface" in config_data["distribute_list"]["prefix"]: + command += " {interface}".format( + **config_data["distribute_list"]["prefix"] + ) + if "protocol" in config_data["distribute_list"]["prefix"]: + command += " {protocol}".format( + **config_data["distribute_list"]["prefix"] + ) + return command + + +def _tmplt_ospf_domain_id(config_data): + if "domain_id" in config_data: + command = "domain-id" + if "ip_address" in config_data["domain_id"]: + if "address" in config_data["domain_id"]["ip_address"]: + command += " {address}".format( + **config_data["domain_id"]["ip_address"] + ) + if "secondary" in config_data["domain_id"]["ip_address"]: + command += " {secondary}".format( + **config_data["domain_id"]["ip_address"] + ) + elif "null" in config_data["domain_id"]: + command += " null" + return command + + +def _tmplt_ospf_event_log(config_data): + if "event_log" in config_data: + command = "event-log" + if "one_shot" in config_data["event_log"]: + command += " one-shot" + if "pause" in config_data["event_log"]: + command += " pause" + if "size" in config_data["event_log"]: + command += " size {size}".format(**config_data["event_log"]) + return command + + +def _tmplt_ospf_limit(config_data): + if "limit" in config_data: + command = "limit retransmissions" + if "dc" in config_data["limit"]: + if "number" in config_data["limit"]["dc"]: + command += " dc {number}".format(**config_data["limit"]["dc"]) + if "disable" in config_data["limit"]["dc"]: + command += " dc disable" + if "non_dc" in config_data["limit"]: + if "number" in config_data["limit"]["non_dc"]: + command += " non-dc {number}".format( + **config_data["limit"]["non_dc"] + ) + if "disable" in config_data["limit"]["dc"]: + command += " non-dc disable" + return command + + +def _tmplt_ospf_vrf_local_rib_criteria(config_data): + if "local_rib_criteria" in config_data: + command = "local-rib-criteria" + if "forwarding_address" in config_data["local_rib_criteria"]: + command += " forwarding-address" + if "inter_area_summary" in config_data["local_rib_criteria"]: + command += " inter-area-summary" + if "nssa_translation" in config_data["local_rib_criteria"]: + command += " nssa-translation" + return command + + +def _tmplt_ospf_log_adjacency_changes(config_data): + if "log_adjacency_changes" in config_data: + command = "log-adjacency-changes" + if "detail" in config_data["log_adjacency_changes"]: + command += " detail" + return command + + +def _tmplt_ospf_max_lsa(config_data): + if "max_lsa" in config_data: + command = "max-lsa {number}".format(**config_data["max_lsa"]) + if "threshold_value" in config_data["max_lsa"]: + command += " {threshold_value}".format(**config_data["max_lsa"]) + if "ignore_count" in config_data["max_lsa"]: + command += " ignore-count {ignore_count}".format( + **config_data["max_lsa"] + ) + if "ignore_time" in config_data["max_lsa"]: + command += " ignore-time {ignore_time}".format( + **config_data["max_lsa"] + ) + if "reset_time" in config_data["max_lsa"]: + command += " reset-time {reset_time}".format( + **config_data["max_lsa"] + ) + if "warning_only" in config_data["max_lsa"]: + command += " warning-only" + return command + + +def _tmplt_ospf_max_metric(config_data): + if "max_metric" in config_data: + command = "max-metric" + if "router_lsa" in config_data["max_metric"]: + command += " router-lsa" + if "external_lsa" in config_data["max_metric"]: + command += " external-lsa {external_lsa}".format( + **config_data["max_metric"] + ) + if "include_stub" in config_data["max_metric"]: + command += " include-stub" + if "on_startup" in config_data["max_metric"]: + if "time" in config_data["max_metric"]["on_startup"]: + command += " on-startup {time}".format( + **config_data["max_metric"]["on_startup"] + ) + elif "wait_for_bgp" in config_data["max_metric"]["on_startup"]: + command += " on-startup wait-for-bgp" + if "summary_lsa" in config_data["max_metric"]: + command += " summary-lsa {summary_lsa}".format( + **config_data["max_metric"] + ) + return command + + +def _tmplt_ospf_mpls_ldp(config_data): + if "ldp" in config_data["mpls"]: + command = "mpls ldp" + if "autoconfig" in config_data["mpls"]["ldp"]: + command += " autoconfig" + if "area" in config_data["mpls"]["ldp"]["autoconfig"]: + command += " area {area}".format( + **config_data["mpls"]["ldp"]["autoconfig"] + ) + elif "sync" in config_data["mpls"]["ldp"]: + command += " sync" + return command + + +def _tmplt_ospf_mpls_traffic_eng(config_data): + if "traffic_eng" in config_data["mpls"]: + command = "mpls traffic-eng" + if "area" in config_data["mpls"]["traffic_eng"]: + command += " area {area}".format( + **config_data["mpls"]["traffic_eng"] + ) + elif "autoroute_exclude" in config_data["mpls"]["traffic_eng"]: + command += " autoroute-exclude prefix-list {autoroute_exclude}".format( + **config_data["mpls"]["traffic_eng"] + ) + elif "interface" in config_data["mpls"]["traffic_eng"]: + command += " interface {int_type}".format( + **config_data["mpls"]["traffic_eng"]["interface"] + ) + if "area" in config_data["mpls"]["traffic_eng"]["interface"]: + command += " area {area}".format( + **config_data["mpls"]["traffic_eng"]["interface"] + ) + elif "mesh_group" in config_data["mpls"]["traffic_eng"]: + command += " mesh-group {id} {interface}".format( + **config_data["mpls"]["traffic_eng"]["mesh_group"] + ) + if "area" in config_data["mpls"]["traffic_eng"]["mesh_group"]: + command += " area {area}".format( + **config_data["mpls"]["traffic_eng"]["mesh_group"] + ) + elif "multicast_intact" in config_data["mpls"]["traffic_eng"]: + command += " multicast-intact" + elif "router_id_interface" in config_data["mpls"]["traffic_eng"]: + command += " router-id {router_id_interface}".format( + **config_data["mpls"]["traffic_eng"] + ) + return command + + +def _tmplt_ospf_neighbor(config_data): + if "neighbor" in config_data: + command = "neighbor" + if "address" in config_data["neighbor"]: + command += " {address}".format(**config_data["neighbor"]) + if "cost" in config_data["neighbor"]: + command += " cost {cost}".format(**config_data["neighbor"]) + if "database_filter" in config_data["neighbor"]: + command += " database-filter all out" + if "poll_interval" in config_data["neighbor"]: + command += " poll-interval {poll_interval}".format( + **config_data["neighbor"] + ) + if "priority" in config_data["neighbor"]: + command += " priority {priority}".format(**config_data["neighbor"]) + return command + + +def _tmplt_ospf_network(config_data): + if "network" in config_data: + command = [] + for each in config_data["network"]: + cmd = "network" + if "address" in each: + cmd += " {address} {wildcard_bits}".format(**each) + if "area" in each: + cmd += " area {area}".format(**each) + command.append(cmd) + return command + + +def _tmplt_ospf_nsf_cisco(config_data): + if "cisco" in config_data["nsf"]: + command = "nsf cisco helper" + if "disable" in config_data["nsf"]["cisco"]: + command += " disable" + return command + + +def _tmplt_ospf_nsf_ietf(config_data): + if "ietf" in config_data["nsf"]: + command = "nsf ietf helper" + if "disable" in config_data["nsf"]["ietf"]: + command += " disable" + elif "strict_lsa_checking" in config_data["nsf"]["ietf"]: + command += " strict-lsa-checking" + return command + + +def _tmplt_ospf_queue_depth_hello(config_data): + if "hello" in config_data["queue_depth"]: + command = "queue-depth hello" + if "max_packets" in config_data["queue_depth"]["hello"]: + command += " {max_packets}".format( + **config_data["queue_depth"]["hello"] + ) + elif "unlimited" in config_data["queue_depth"]["hello"]: + command += " unlimited" + return command + + +def _tmplt_ospf_queue_depth_update(config_data): + if "update" in config_data["queue_depth"]: + command = "queue-depth update" + if "max_packets" in config_data["queue_depth"]["update"]: + command += " {max_packets}".format( + **config_data["queue_depth"]["update"] + ) + elif "unlimited" in config_data["queue_depth"]["update"]: + command += " unlimited" + return command + + +def _tmplt_ospf_summary_address(config_data): + if "summary_address" in config_data: + command = "summary-address {address} {mask}".format( + **config_data["summary_address"] + ) + if "not_advertise" in config_data["summary_address"]: + command += " not-advertise" + elif "nssa_only" in config_data["summary_address"]: + command += " nssa-only" + if "tag" in config_data["summary_address"]: + command += " tag {tag}".format( + **config_data["summary_address"] + ) + return command + + +def _tmplt_ospf_timers_pacing(config_data): + if "pacing" in config_data["timers"]: + command = "timers pacing" + if "flood" in config_data["timers"]["pacing"]: + command += " flood {flood}".format( + **config_data["timers"]["pacing"] + ) + elif "lsa_group" in config_data["timers"]["pacing"]: + command += " lsa-group {lsa_group}".format( + **config_data["timers"]["pacing"] + ) + elif "retransmission" in config_data["timers"]["pacing"]: + command += " retransmission {retransmission}".format( + **config_data["timers"]["pacing"] + ) + return command + + +def _tmplt_ospf_ttl_security(config_data): + if "ttl_security" in config_data: + command = "ttl-security all-interfaces" + if "hops" in config_data["ttl_security"]: + command += " hops {hops}".format(**config_data["ttl_security"]) + return command + + +class Ospfv2Template(NetworkTemplate): + def __init__(self, lines=None): + super(Ospfv2Template, self).__init__(lines=lines, tmplt=self) + + PARSERS = [ + { + "name": "pid", + "getval": re.compile( + r""" + ^router\s + ospf* + \s(?P<pid>\S+) + \svrf + \s(?P<vrf>\S+) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_vrf_cmd, + "result": { + "processes": { + "{{ pid }}": { + "process_id": "{{ pid|int }}", + "vrf": "{{ vrf }}", + } + } + }, + "shared": True, + }, + { + "name": "pid", + "getval": re.compile( + r""" + ^router\s + ospf* + \s(?P<pid>\S+) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_vrf_cmd, + "result": { + "processes": {"{{ pid }}": {"process_id": "{{ pid|int }}"}} + }, + "shared": True, + }, + { + "name": "adjacency", + "getval": re.compile( + r"""\s+adjacency + \sstagger* + \s*((?P<min>\d+)|(?P<none_adj>none))* + \s*(?P<max>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_adjacency_cmd, + "result": { + "processes": { + "{{ pid }}": { + "adjacency": { + "min_adjacency": "{{ min|int }}", + "max_adjacency": "{{ max|int }}", + "none": "{{ True if none_adj is defined else None }}", + } + } + } + }, + }, + { + "name": "address_family", + "getval": re.compile( + r"""\s+topology + \s(?P<base>base)* + \s*(?P<name>\S+)* + \s*(?P<tid>tid\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_address_family_cmd, + "result": { + "processes": { + "{{ pid }}": { + "address_family": { + "topology": { + "base": "{{ True if base is defined }}", + "name": "{{ name }}", + "tid": "{{ tid.split(" ")[1] }}", + } + } + } + } + }, + }, + { + "name": "area.authentication", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \s*(?P<auth>authentication)* + \s*(?P<md>message-digest) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_authentication, + "compval": "authentication", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "authentication": { + "enable": "{{ True if auth is defined and md is undefined }}", + "message_digest": "{{ not not md }}", + }, + } + } + } + } + }, + }, + { + "name": "area.capability", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \s*(?P<capability>capability)* + \s*(?P<df>default-exclusion) + *$""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} capability default-exclusion", + "compval": "capability", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "capability": "{{ not not capability }}", + } + } + } + } + }, + }, + { + "name": "area.default_cost", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \sdefault-cost* + \s*(?P<default_cost>\S+) + *$""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} default-cost {{ default_cost }}", + "compval": "default_cost", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "default_cost": "{{ default_cost|int }}", + } + } + } + } + }, + }, + { + "name": "area.filter_list", + "getval": re.compile( + r"""\s+area + \s*(?P<area_id>\S+)* + \s*filter-list\sprefix* + \s*(?P<name>\S+)* + \s*(?P<dir>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_filter, + "compval": "filter_list", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "filter_list": [ + { + "name": "{{ name }}", + "direction": "{{ dir }}", + } + ], + } + } + } + } + }, + }, + { + "name": "area.nssa", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \s(?P<nssa>nssa)* + \s*(?P<no_redis>no-redistribution)* + \s*(?P<def_origin>default-information-originate)* + \s*(?P<metric>metric\s\d+)* + \s*(?P<metric_type>metric-type\s\d+)* + \s*(?P<no_summary>no-summary)* + \s*(?P<no_ext>no-ext-capability)*$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa_translate, + "compval": "nssa", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "set": "{{ True if nssa is defined and def_origin is undefined and " + "no_ext is undefined and no_redis is undefined and nssa_only is undefined }}", + "default_information_originate": { + "set": "{{ True if def_origin is defined and metric is undefined and " + "metric_type is undefined and nssa_only is undefined }}", + "metric": "{{ metric.split(" + ")[1]|int }}", + "metric_type": "{{ metric_type.split(" + ")[1]|int }}", + "nssa_only": "{{ True if nssa_only is defined }}", + }, + "no_ext_capability": "{{ True if no_ext is defined }}", + "no_redistribution": "{{ True if no_redis is defined }}", + "no_summary": "{{ True if no_summary is defined }}", + }, + } + } + } + } + }, + }, + { + "name": "area.nssa.translate", + "getval": re.compile( + r"""\s+area* + \s*(?P<area_id>\S+)* + \s*(?P<nssa>nssa)* + \stranslate\stype7* + \s(?P<translate_always>always)* + \s* (?P<translate_supress>suppress-fa) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa, + "compval": "nssa.translate", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "translate": "{{ translate_always if translate_always is defined else translate_supress if translate_supress is defined }}" + }, + } + } + } + } + }, + }, + { + "name": "area.ranges", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \srange + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + \s(?P<netmask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*((?P<advertise>advertise)|(?P<not_advertise>not-advertise))* + \s*(?P<cost>cost\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_ranges, + "compval": "ranges", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "ranges": [ + { + "address": "{{ address }}", + "netmask": "{{ netmask }}", + "advertise": "{{ True if advertise is defined }}", + "cost": "{{ cost.split(" ")[1]|int }}", + "not_advertise": "{{ True if not_advertise is defined }}", + } + ], + } + } + } + } + }, + }, + { + "name": "area.sham_link", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \ssham-link + \s(?P<source>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + \s(?P<destination>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<cost>cost\s\d+)* + \s*(?P<ttl_security>ttl-security\shops\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_sham_link, + "compval": "sham_link", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "sham_link": { + "source": "{{ source }}", + "destination": "{{ destination }}", + "cost": "{{ cost.split(" ")[1]|int }}", + "ttl_security": '{{ ttl_security.split("hops ")[1] }}', + }, + } + } + } + } + }, + }, + { + "name": "area.stub", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \s(?P<stub>stub)* + \s*(?P<no_ext>no-ext-capability)* + \s*(?P<no_sum>no-summary) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_stub_link, + "compval": "stub", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "stub": { + "set": "{{ True if stub is defined and no_ext is undefined and no_sum is undefined }}", + "no_ext_capability": "{{ True if no_ext is defined }}", + "no_summary": "{{ True if no_sum is defined }}", + }, + } + } + } + } + }, + }, + { + "name": "auto_cost", + "getval": re.compile( + r"""\s+(?P<auto_cost>auto-cost)* + \s*(?P<ref_band>reference-bandwidth\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_auto_cost, + "result": { + "processes": { + "{{ pid }}": { + "auto_cost": { + "set": "{{ True if auto_cost is defined and ref_band is undefined }}", + "reference_bandwidth": '{{ ref_band.split(" ")[1] }}', + } + } + } + }, + }, + { + "name": "bfd", + "getval": re.compile( + r"""\s+bfd* + \s*(?P<bfd>all-interfaces) + *$""", + re.VERBOSE, + ), + "setval": "bfd all-interfaces", + "result": { + "processes": { + "{{ pid }}": {"bfd": "{{ True if bfd is defined }}"} + } + }, + }, + { + "name": "capability", + "getval": re.compile( + r"""\s+capability* + \s*((?P<lls>lls)|(?P<opaque>opaque)|(?P<transit>transit)|(?P<vrf_lite>vrf-lite)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_capability, + "result": { + "processes": { + "{{ pid }}": { + "capability": { + "lls": "{{ True if lls is defined }}", + "opaque": "{{ True if opaque is defined }}", + "transit": "{{ True if transit is defined }}", + "vrf_lite": "{{ True if vrf_lite is defined }}", + } + } + } + }, + }, + { + "name": "compatible", + "getval": re.compile( + r"""\s+compatible* + \s*((?P<rfc1583>rfc1583)|(?P<rfc1587>rfc1587)|(?P<rfc5243>rfc5243)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_compatible, + "result": { + "processes": { + "{{ pid }}": { + "compatible": { + "rfc1583": "{{ True if rfc1583 is defined }}", + "rfc1587": "{{ True if rfc1587 is defined }}", + "rfc5243": "{{ True if rfc5243 is defined }}", + } + } + } + }, + }, + { + "name": "default_information", + "getval": re.compile( + r"""\s+default-information* + \s*(?P<originate>originate)* + \s*(?P<always>always)* + \s*(?P<metric>metric\s\d+)* + \s*(?P<metric_type>metric-type\s\d+)* + \s*(?P<route_map>route-map\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_default_information, + "result": { + "processes": { + "{{ pid }}": { + "default_information": { + "originate": "{{ True if originate is defined }}", + "always": "{{ True if always is defined }}", + "metric": "{{ metric.split(" ")[1]|int }}", + "metric_type": "{{ metric_type.split(" + ")[1]|int }}", + "route_map": "{{ route_map.split(" ")[1] }}", + } + } + } + }, + }, + { + "name": "default_metric", + "getval": re.compile( + r"""\s+default-metric(?P<default_metric>\s\d+) + *$""", + re.VERBOSE, + ), + "setval": "default-metric {{ default_metric }}", + "result": { + "processes": { + "{{ pid }}": {"default_metric": "{{ default_metric| int}}"} + } + }, + }, + { + "name": "discard_route", + "getval": re.compile( + r"""\s+(?P<discard_route>discard-route)* + \s*(?P<external>external\s\d+)* + \s*(?P<internal>internal\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_discard_route, + "result": { + "processes": { + "{{ pid }}": { + "discard_route": { + "set": "{{ True if discard_route is defined and external is undefined and internal is undefined }}", + "external": "{{ external.split(" ")[1]|int }}", + "internal": "{{ internal.split(" ")[1]|int }}", + } + } + } + }, + }, + { + "name": "distance.admin_distance", + "getval": re.compile( + r"""\s+distance + \s(?P<admin_dist>\S+)* + \s*(?P<source>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<wildcard>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<acl>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_admin_distance, + "compval": "admin_distance", + "result": { + "processes": { + "{{ pid }}": { + "distance": { + "admin_distance": { + "distance": "{{ admin_dist }}", + "address": "{{ source }}", + "wildcard_bits": "{{ wildcard }}", + "acl": "{{ acl }}", + } + } + } + } + }, + }, + { + "name": "distance.ospf", + "getval": re.compile( + r"""\s+distance + \sospf* + \s*(?P<intra>intra-area\s\d+)* + \s*(?P<inter>inter-area\s\d+)* + \s*(?P<external>external\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_ospf, + "compval": "ospf", + "result": { + "processes": { + "{{ pid }}": { + "distance": { + "ospf": { + "inter_area": "{{ inter.split(" ")[1]|int }}", + "intra_area": "{{ intra.split(" ")[1]|int }}", + "external": "{{ external.split(" ")[1]|int }}", + } + } + } + } + }, + }, + { + "name": "distribute_list.acls", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<name>\S+)* + \s*(?P<dir>\S+)* + \s*(?P<int_pro>\S+\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distribute_list_acls, + "compval": "distribute_list.acls", + "result": { + "processes": { + "{{ pid }}": { + "distribute_list": { + "acls": [ + { + "name": "{{ name }}", + "direction": "{{ dir }}", + "interface": '{{ int_pro if dir == "in" }}', + "protocol": '{{ int_pro if dir == "out" }}', + } + ] + } + } + } + }, + }, + { + "name": "distribute_list.prefix", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<prefix>prefix\s\S+)* + \s*(?P<gateway>gateway\s\S+)* + \s*(?P<dir>\S+)* + \s*(?P<int_pro>\S+\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distribute_list_prefix, + "compval": "distribute_list.prefix", + "result": { + "processes": { + "{{ pid }}": { + "distribute_list": { + "prefix": { + "name": "{{ prefix.split(" ")[1] }}", + "gateway_name": "{{ gateway.split(" + ")[1] if prefix is defined }}", + "direction": "{{ dir if gateway is undefined }}", + "interface": '{{ int_pro if dir == "in" }}', + "protocol": '{{ int_pro if dir == "out" }}', + } + } + } + } + }, + }, + { + "name": "distribute_list.route_map", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<route_map>route-map\s\S+)* + \s*(?P<dir>\S+) + *$""", + re.VERBOSE, + ), + "setval": "distribute-list route-map {{ distribute_list.route_map.name }} in", + "compval": "distribute_list.route_map", + "result": { + "processes": { + "{{ pid }}": { + "distribute_list": { + "route_map": { + "name": "{{ route_map.split(" ")[1] }}" + } + } + } + } + }, + }, + { + "name": "domain_id", + "getval": re.compile( + r"""\s+domain-id + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<secondary>secondary)* + \s*(?P<null>null) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_domain_id, + "result": { + "processes": { + "{{ pid }}": { + "domain_id": { + "ip_address": { + "address": "{{ address }}", + "secondary": "{{ True if secondary is defined }}", + }, + "null": "{{ True if null is defined }}", + } + } + } + }, + }, + { + "name": "domain_tag", + "getval": re.compile( + r"""\s+domain-tag + \s(?P<tag>\d+) + *$""", + re.VERBOSE, + ), + "setval": "domain-tag {{ domain_tag }}", + "result": { + "processes": {"{{ pid }}": {"domain_tag": "{{ tag|int }}"}} + }, + }, + { + "name": "event_log", + "getval": re.compile( + r"""\s+(?P<event_log>event-log)* + \s*(?P<one_shot>one-shot)* + \s*(?P<pause>pause)* + \s*(?P<size>size\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_event_log, + "result": { + "processes": { + "{{ pid }}": { + "event_log": { + "enable": "{{ True if event_log is defined and one_shot is undefined and pause is undefined and size is undefined }}", + "one_shot": "{{ True if one_shot is defined }}", + "pause": "{{ True if pause is defined }}", + "size": "{{ size.split(" ")[1]|int }}", + } + } + } + }, + }, + { + "name": "help", + "getval": re.compile( + r"""\s+(?P<help>help) + *$""", + re.VERBOSE, + ), + "setval": "help", + "result": { + "processes": { + "{{ pid }}": {"help": "{{ True if help is defined }}"} + } + }, + }, + { + "name": "ignore", + "getval": re.compile( + r"""\s+(?P<ignore>ignore) + *$""", + re.VERBOSE, + ), + "setval": "ignore lsa mospf", + "result": { + "processes": { + "{{ pid }}": {"ignore": "{{ True if ignore is defined }}"} + } + }, + }, + { + "name": "interface_id", + "getval": re.compile( + r"""\s+(?P<interface_id>interface-id\ssnmp-if-index) + *$""", + re.VERBOSE, + ), + "setval": "interface-id snmp-if-index", + "result": { + "processes": { + "{{ pid }}": { + "interface_id": "{{ True if interface_id is defined }}" + } + } + }, + }, + { + "name": "ispf", + "getval": re.compile( + r"""\s+(?P<ispf>ispf) + *$""", + re.VERBOSE, + ), + "setval": "ispf", + "result": { + "processes": { + "{{ pid }}": {"ispf": "{{ True if ispf is defined }}"} + } + }, + }, + { + "name": "limit", + "getval": re.compile( + r"""\s+limit\sretransmissions + \s((?P<dc_num>dc\s\d+)|(?P<dc_disable>dc\sdisable))* + \s*((?P<non_dc_num>non-dc\s\d+)|(?P<non_dc_disable>non-dc\sdisable)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_limit, + "result": { + "processes": { + "{{ pid }}": { + "limit": { + "dc": { + "number": "{{ dc_num.split(" ")[1]|int }}", + "disable": "{{ True if dc_disable is defined }}", + }, + "non_dc": { + "number": "{{ non_dc_num.split(" ")[1]|int }}", + "disable": "{{ True if dc_disable is defined }}", + }, + } + } + } + }, + }, + { + "name": "local_rib_criteria", + "getval": re.compile( + r"""\s+(?P<local>local-rib-criteria)* + \s*(?P<forward>forwarding-address)* + \s*(?P<inter>inter-area-summary)* + \s*(?P<nssa>nssa-translation) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_vrf_local_rib_criteria, + "result": { + "processes": { + "{{ pid }}": { + "local_rib_criteria": { + "enable": "{{ True if local is defined and forward is undefined and inter is undefined and nssa is undefined }}", + "forwarding_address": "{{ True if forward is defined }}", + "inter_area_summary": "{{ True if inter is defined }}", + "nssa_translation": "{{ True if nssa is defined }}", + } + } + } + }, + }, + { + "name": "log_adjacency_changes", + "getval": re.compile( + r"""\s+(?P<log>log-adjacency-changes)* + \s*(?P<detail>detail) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_log_adjacency_changes, + "result": { + "processes": { + "{{ pid }}": { + "log_adjacency_changes": { + "set": "{{ True if log is defined and detail is undefined }}", + "detail": "{{ True if detail is defined }}", + } + } + } + }, + }, + { + "name": "max_lsa", + "getval": re.compile( + r"""\s+max-lsa + \s(?P<number>\d+)* + \s*(?P<threshold>\d+)* + \s*(?P<ignore_count>ignore-count\s\d+)* + \s*(?P<ignore_time>ignore-time\s\d+)* + \s*(?P<reset_time>reset-time\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_lsa, + "result": { + "processes": { + "{{ pid }}": { + "max_lsa": { + "number": "{{ number }}", + "threshold_value": "{{ threshold }}", + "ignore_count": "{{ ignore_count.split(" ")[1] }}", + "ignore_time": "{{ ignore_time.split(" ")[1] }}", + "reset_time": "{{ reset_time.split(" ")[1] }}", + "warning_only": "{{ True if warning is defined }}", + } + } + } + }, + }, + { + "name": "max_metric", + "getval": re.compile( + r"""\s+max-metric* + \s*(?P<router_lsa>router-lsa)* + \s*(?P<include_stub>include-stub)* + \s*(?P<external_lsa>external-lsa\s\d+)* + \s*(?P<startup_time>on-startup\s\d+)* + \s*(?P<startup_wait>on-startup\s\S+)* + \s*(?P<summary_lsa>summary-lsa\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_metric, + "result": { + "processes": { + "{{ pid }}": { + "max_metric": { + "router_lsa": "{{ True if router_lsa is defined }}", + "external_lsa": "{{ external_lsa.split(" ")[1] }}", + "include_stub": "{{ ignore_count.split(" ")[1] }}", + "on_startup": { + "time": "{{ startup_time.split(" ")[1] }}", + "wait_for_bgp": "{{ True if startup_wait is defined }}", + }, + "summary_lsa": "{{ summary_lsa.split(" ")[1] }}", + } + } + } + }, + }, + { + "name": "maximum_paths", + "getval": re.compile( + r"""\s+maximum-paths* + \s+(?P<paths>\d+) + *$""", + re.VERBOSE, + ), + "setval": "maximum-paths {{ maximum_paths }}", + "result": { + "processes": {"{{ pid }}": {"maximum_paths": "{{ paths }}"}} + }, + }, + { + "name": "mpls.ldp", + "getval": re.compile( + r"""\s+mpls + \sldp* + \s*(?P<autoconfig>autoconfig*\s*(?P<area>area\s\S+))* + \s*(?P<sync>sync) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_mpls_ldp, + "compval": "ldp", + "result": { + "processes": { + "{{ pid }}": { + "mpls": { + "ldp": { + "autoconfig": { + "set": "{{ True if autoconfig is defined and area is undefined }}", + "area": "{{ area.split(" ")[1] }}", + }, + "sync": "{{ True if sync is defined }}", + } + } + } + } + }, + }, + { + "name": "mpls.traffic_eng", + "getval": re.compile( + r"""\s+mpls + \straffic-eng* + \s*(?P<area>area\s\S+)* + \s*(?P<autoroute>autoroute-exclude\s\S+\s\S+)* + \s*(?P<interface>interface\s(?P<int_type>\S+\s\S+)\s(?P<int_area>area\s\S+))* + \s*(?P<mesh>mesh-group\s\d+\s(?P<mesh_int>\S+\s\S+)\s(?P<mesh_area>area\s\d+))* + \s*(?P<multicast>multicast-intact)* + \s*(?P<router>router-id\s(?P<router_int>\S+\s\S+)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_mpls_traffic_eng, + "compval": "traffic_eng", + "result": { + "processes": { + "{{ pid }}": { + "mpls": { + "traffic_eng": { + "area": "{{ area.split(" ")[1] }}", + "autoroute_exclude": "{{ autoroute.split(" + ")[2] }}", + "interface": { + "interface_type": "{{ int_type }}", + "area": "{{ int_area.split(" ")[1] }}", + }, + "mesh_group": { + "id": "{{ mesh.split(" ")[1] }}", + "interface": "{{ mest_int }}", + "area": "{{ mesh_area.split(" ")[1] }}", + }, + "multicast_intact": "{{ True if multicast is defined }}", + "router_id_interface": "{{ router_int }}", + } + } + } + } + }, + }, + { + "name": "neighbor", + "getval": re.compile( + r"""\s+neighbor + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<cost>cost\s\d+)* + \s*(?P<db_filter>database-filter\sall\sout)* + \s*(?P<poll>poll-interval\s\d+)* + \s*(?P<priority>priority\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_neighbor, + "result": { + "processes": { + "{{ pid }}": { + "neighbor": { + "address": "{{ address }}", + "cost": "{{ cost.split(" ")[1] }}", + "database_filter": "{{ True if db_filter is defined }}", + "poll_interval": "{{ poll.split(" ")[1] }}", + "priority": "{{ priority.split(" ")[1] }}", + } + } + } + }, + }, + { + "name": "network", + "getval": re.compile( + r"""\s+network + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<wildcard>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<area>area\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_network, + "result": { + "processes": { + "{{ pid }}": { + "network": [ + { + "address": "{{ address }}", + "wildcard_bits": "{{ wildcard }}", + "area": "{{ area.split(" ")[1] }}", + } + ] + } + } + }, + }, + { + "name": "nsf.cisco", + "getval": re.compile( + r"""\s+nsf + \s(?P<cisco>cisco)* + \s*(?P<helper>helper)* + \s*(?P<disable>disable) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_nsf_cisco, + "compval": "cisco", + "result": { + "processes": { + "{{ pid }}": { + "nsf": { + "cisco": { + "helper": "{{ True if helper is defined }}", + "disable": "{{ True if disable is defined }}", + } + } + } + } + }, + }, + { + "name": "nsf.ietf", + "getval": re.compile( + r"""\s+nsf + \s(?P<ietf>ietf)* + \s*(?P<helper>helper)* + \s*(?P<disable>disable)* + \s*(?P<strict>strict-lsa-checking) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_nsf_ietf, + "compval": "ietf", + "result": { + "processes": { + "{{ pid }}": { + "nsf": { + "ietf": { + "helper": "{{ True if helper is defined }}", + "disable": "{{ True if disable is defined }}", + "strict_lsa_checking": "{{ True if strict is defined }}", + } + } + } + } + }, + }, + { + "name": "passive_interface", + "getval": re.compile( + r"""\s+passive-interface + \s(?P<interface>\S+\s\S+) + *$""", + re.VERBOSE, + ), + "setval": "passive-interface {{ passive_interface }}", + "result": { + "processes": { + "{{ pid }}": {"passive_interface": "{{ interface }}"} + } + }, + }, + { + "name": "prefix_suppression", + "getval": re.compile( + r"""\s+(?P<prefix_sup>prefix-suppression) + *$""", + re.VERBOSE, + ), + "setval": "prefix-suppression", + "result": { + "processes": { + "{{ pid }}": { + "prefix_suppression": "{{ True if prefix_sup is defined }}" + } + } + }, + }, + { + "name": "priority", + "getval": re.compile( + r"""\s+priority + \s(?P<priority>\d+) + *$""", + re.VERBOSE, + ), + "setval": "priority {{ priority }}", + "result": { + "processes": {"{{ pid }}": {"priority": "{{ priority }}"}} + }, + }, + { + "name": "queue_depth.hello", + "getval": re.compile( + r"""\s+queue-depth + \shello* + \s*(?P<max_packets>\d+)* + \s*(?P<unlimited>unlimited) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_queue_depth_hello, + "compval": "hello", + "result": { + "processes": { + "{{ pid }}": { + "queue_depth": { + "hello": { + "max_packets": "{{ max_packets }}", + "unlimited": "{{ True if unlimited is defined }}", + } + } + } + } + }, + }, + { + "name": "queue_depth.update", + "getval": re.compile( + r"""\s+queue-depth + \supdate* + \s*(?P<max_packets>\d+)* + \s*(?P<unlimited>unlimited) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_queue_depth_update, + "compval": "update", + "result": { + "processes": { + "{{ pid }}": { + "queue_depth": { + "update": { + "max_packets": "{{ max_packets }}", + "unlimited": "{{ True if unlimited is defined }}", + } + } + } + } + }, + }, + { + "name": "router_id", + "getval": re.compile( + r"""\s+router-id + \s(?P<id>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + *$""", + re.VERBOSE, + ), + "setval": "router-id {{ router_id }}", + "result": {"processes": {"{{ pid }}": {"router_id": "{{ id }}"}}}, + }, + { + "name": "shutdown", + "getval": re.compile( + r"""\s+(?P<shutdown>shutdown) + *$""", + re.VERBOSE, + ), + "setval": "shutdown", + "result": { + "processes": { + "{{ pid }}": { + "shutdown": "{{ True if shutdown is defined }}" + } + } + }, + }, + { + "name": "summary_address", + "getval": re.compile( + r"""\s+summary-address + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<mask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<not_adv>not-advertise)* + \s*(?P<nssa>nssa-only)* + \s*(?P<tag>tag\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_summary_address, + "result": { + "processes": { + "{{ pid }}": { + "summary_address": { + "address": "{{ address }}", + "mask": "{{ mask }}", + "not_advertise": "{{ True if not_adv is defined }}", + "nssa_only": "{{ True if nssa is defined }}", + "tag": "{{ tag.split(" ")[1] }}", + } + } + } + }, + }, + { + "name": "timers.lsa", + "getval": re.compile( + r"""\s+timers + \slsa + \sarrival + \s(?P<lsa>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers lsa arrival {{ timers.lsa }}", + "compval": "lsa", + "result": { + "processes": {"{{ pid }}": {"timers": {"lsa": "{{ lsa }}"}}} + }, + }, + { + "name": "timers.pacing", + "getval": re.compile( + r"""\s+timers + \spacing + \s(?P<flood>flood\s\d+)* + \s*(?P<lsa_group>lsa-group\s\d+)* + \s*(?P<retransmission>retransmission\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_timers_pacing, + "compval": "pacing", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "pacing": { + "flood": "{{ flood.split(" ")[1] }}", + "lsa_group": "{{ lsa_group.split(" ")[1] }}", + "retransmission": "{{ retransmission.split(" + ")[1] }}", + } + } + } + } + }, + }, + { + "name": "timers.throttle.lsa", + "getval": re.compile( + r"""\s+timers + \sthrottle + \s*(?P<lsa>lsa)* + \s*(?P<first_delay>\d+)* + \s*(?P<min_delay>\d+)* + \s*(?P<max_delay>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers throttle lsa {{ throttle.lsa.first_delay }} {{ throttle.lsa.min_delay }} {{ throttle.lsa.max_delay }}", + "compval": "throttle.lsa", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "lsa": { + "first_delay": "{{ first_delay }}", + "min_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + } + } + } + } + } + }, + }, + { + "name": "timers.throttle.spf", + "getval": re.compile( + r"""\s+timers + \sthrottle + \s*(?P<spf>spf)* + \s*(?P<first_delay>\d+)* + \s*(?P<min_delay>\d+)* + \s*(?P<max_delay>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers throttle spf {{ throttle.spf.receive_delay }} {{ throttle.spf.between_delay }} {{ throttle.spf.max_delay }}", + "compval": "throttle.spf", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "spf": { + "receive_delay": "{{ first_delay }}", + "between_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + } + } + } + } + } + }, + }, + { + "name": "traffic_share", + "getval": re.compile( + r"""\s+(?P<traffic>traffic-share\smin\sacross-interfaces) + *$""", + re.VERBOSE, + ), + "setval": "traffic-share min across-interfaces", + "result": { + "processes": { + "{{ pid }}": { + "traffic_share": "{{ True if traffic is defined }}" + } + } + }, + }, + { + "name": "ttl_security", + "getval": re.compile( + r"""\s+ttl-security + \s(?P<interfaces>all-interfaces)* + \s*(?P<hops>hops\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_ttl_security, + "result": { + "processes": { + "{{ pid }}": { + "ttl_security": { + "set": "{{ True if interfaces is defined and hops is undefined }}", + "hops": "{{ hops.split(" ")[1] }}", + } + } + } + }, + }, + ] diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv3.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv3.py new file mode 100644 index 00000000..08830a61 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv3.py @@ -0,0 +1,3132 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import re +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import ( + NetworkTemplate, +) + + +def _tmplt_ospfv3_cmd(process): + + command = "router ospfv3 {process_id}".format(**process) + if "vrf" in process: + command += " vrf {vrf}".format(**process) + return command + + +def _tmplt_ospf_adjacency_cmd(config_data): + if "adjacency" in config_data: + command = "adjacency stagger" + if "none" in config_data["adjacency"]: + command += " none" + else: + command += " {min_adjacency}".format(**config_data["adjacency"]) + if "max_adjacency" in config_data["adjacency"]: + command += " {min_adjacency}".format(**config_data["adjacency"]) + return command + + +def _tmplt_ospf_address_family_cmd(config_data): + if "address_family" in config_data: + command = [] + # for config_data in config_data["address_family"]: + cmd = "address-family {afi}".format(**config_data["address_family"]) + if config_data["address_family"].get("unicast"): + cmd += " unicast" + if config_data["address_family"].get("vrf"): + cmd += " vrf {vrf}".format(**config_data["address_family"]) + command.append(cmd) + if command: + command.insert(len(command), "exit-address-family") + return command + + +def _tmplt_address_family_graceful_restart(config_data): + if "graceful_restart" in config_data: + command = "graceful_restart {enable}".format( + **config_data["graceful_restart"] + ) + if "disable" in config_data["graceful_restart"]: + command += " disable" + elif "strict_lsa_checking" in config_data["graceful_restart"]: + command += " strict-lsa-checking" + return command + + +def _tmplt_ospf_area_authentication(config_data): + if "authentication" in config_data: + command = "area {area_id} authentication".format(**config_data) + if config_data["authentication"].get("message_digest"): + command += " message-digest" + return command + + +def _tmplt_ospf_area_filter(config_data): + if "filter_list" in config_data: + command = [] + for key, value in iteritems(config_data.get("filter_list")): + cmd = "area {area_id}".format(**config_data) + if value.get("name") and value.get("direction"): + cmd += " filter-list prefix {name} {direction}".format(**value) + command.append(cmd) + return command + + +def _tmplt_ospf_area_nssa(config_data): + if "nssa" in config_data: + command = "area {area_id} nssa".format(**config_data) + if "default_information_originate" in config_data["nssa"]: + command += " default-information-originate" + if ( + "metric" + in config_data["nssa"]["default_information_originate"] + ): + command += " metric {metric}".format( + **config_data["nssa"]["default_information_originate"] + ) + if ( + "metric_type" + in config_data["nssa"]["default_information_originate"] + ): + command += " metric-type {metric_type}".format( + **config_data["nssa"]["default_information_originate"] + ) + if ( + "nssa_only" + in config_data["nssa"]["default_information_originate"] + ): + command += " nssa-only" + if config_data["nssa"].get("no_ext_capability"): + command += " no-ext-capability" + if config_data["nssa"].get("no_redistribution"): + command += " no-redistribution" + if config_data["nssa"].get("no_summary"): + command += " no-summary" + return command + + +def _tmplt_ospf_area_nssa_translate(config_data): + if "nssa" in config_data and "translate" in config_data["nssa"]: + command = "area {area_id} nssa".format(**config_data) + if "translate" in config_data["nssa"]: + command += " translate type7 {translate}".format( + **config_data["nssa"] + ) + return command + + +def _tmplt_ospf_area_ranges(config_data): + if "ranges" in config_data: + commands = [] + for k, v in iteritems(config_data["ranges"]): + cmd = "area {area_id} range".format(**config_data) + temp_cmd = " {address} {netmask}".format(**v) + if "advertise" in v: + temp_cmd += " advertise" + elif "not_advertise" in v: + temp_cmd += " not-advertise" + if "cost" in v: + temp_cmd += " cost {cost}".format(**v) + cmd += temp_cmd + commands.append(cmd) + return commands + + +def _tmplt_ospf_area_sham_link(config_data): + if "sham_link" in config_data: + command = "area {area_id} sham-link".format(**config_data) + if "source" in config_data["sham_link"]: + command += " {source} {destination}".format( + **config_data["sham_link"] + ) + if "cost" in config_data["sham_link"]: + command += " cost {cost}".format(**config_data["sham_link"]) + if "ttl_security" in config_data["sham_link"]: + command += " ttl-security hops {ttl_security}".format( + **config_data["sham_link"] + ) + return command + + +def _tmplt_ospf_area_stub_link(config_data): + if "stub" in config_data: + command = "area {area_id} stub".format(**config_data) + if "no_ext_capability" in config_data["stub"]: + command += " no-ext-capability" + if "no_summary" in config_data["stub"]: + command += " no-summary" + return command + + +def _tmplt_ospf_auto_cost(config_data): + if "auto_cost" in config_data: + command = "auto-cost" + if "reference_bandwidth" in config_data["auto_cost"]: + command += " reference-bandwidth {reference_bandwidth}".format( + **config_data["auto_cost"] + ) + return command + + +def _tmplt_ospf_capability(config_data): + if "capability" in config_data: + if "lls" in config_data["capability"]: + command = "capability lls" + elif "opaque" in config_data["capability"]: + command = "capability opaque" + elif "transit" in config_data["capability"]: + command = "capability transit" + elif "vrf_lite" in config_data["capability"]: + command = "capability vrf_lite" + return command + + +def _tmplt_ospf_compatible(config_data): + if "compatible" in config_data: + if "rfc1583" in config_data["compatible"]: + command = "compatible rfc1583" + elif "rfc1587" in config_data["compatible"]: + command = "compatible rfc1587" + elif "rfc5243" in config_data["compatible"]: + command = "compatible rfc5243" + return command + + +def _tmplt_ospf_default_information(config_data): + if "default_information" in config_data: + command = "default-information" + if "originate" in config_data["default_information"]: + command += " originate" + if "always" in config_data["default_information"]: + command += " always" + if "metric" in config_data["default_information"]: + command += " metric {metric}".format( + **config_data["default_information"] + ) + if "metric_type" in config_data["default_information"]: + command += " metric-type {metric_type}".format( + **config_data["default_information"] + ) + if "metric" in config_data["default_information"]: + command += " route-map {route_map}".format( + **config_data["default_information"] + ) + return command + + +def _tmplt_ospf_discard_route(config_data): + if "discard_route" in config_data: + command = "discard-route" + if "external" in config_data["discard_route"]: + command += " external {external}".format( + **config_data["discard_route"] + ) + if "internal" in config_data["discard_route"]: + command += " internal {internal}".format( + **config_data["discard_route"] + ) + return command + + +def _tmplt_ospf_distance_admin_distance(config_data): + if "admin_distance" in config_data["distance"]: + command = "distance {distance}".format( + **config_data["distance"]["admin_distance"] + ) + if "address" in config_data["distance"]["admin_distance"]: + command += " {address} {wildcard_bits}".format( + **config_data["distance"]["admin_distance"] + ) + if "acl" in config_data["distance"]["admin_distance"]: + command += " {acl}".format( + **config_data["distance"]["admin_distance"] + ) + return command + + +def _tmplt_ospf_distance_ospf(config_data): + if "ospf" in config_data["distance"]: + command = "distance ospf" + if "inter_area" in config_data["distance"]["ospf"]: + command += " inter-area {inter_area}".format( + **config_data["distance"]["ospf"] + ) + if config_data["distance"].get("ospf").get("intra_area"): + command += " intra-area {intra_area}".format( + **config_data["distance"]["ospf"] + ) + if config_data["distance"].get("ospf").get("external"): + command += " external {external}".format( + **config_data["distance"]["ospf"] + ) + return command + + +def _tmplt_ospf_distribute_list_acls(config_data): + if "acls" in config_data.get("distribute_list"): + command = [] + for k, v in iteritems(config_data["distribute_list"]["acls"]): + cmd = "distribute-list {name} {direction}".format(**v) + if "interface" in v: + cmd += " {interface}".format(**v) + if "protocol" in v: + cmd += " {protocol}".format(**v) + command.append(cmd) + return command + + +def _tmplt_ospf_distribute_list_prefix(config_data): + if "prefix" in config_data.get("distribute_list"): + command = "distribute-list prefix {name}".format( + **config_data["distribute_list"]["prefix"] + ) + if "gateway_name" in config_data["distribute_list"]["prefix"]: + command += " gateway {gateway_name}".format( + **config_data["distribute_list"]["prefix"] + ) + if "direction" in config_data["distribute_list"]["prefix"]: + command += " {direction}".format( + **config_data["distribute_list"]["prefix"] + ) + if "interface" in config_data["distribute_list"]["prefix"]: + command += " {interface}".format( + **config_data["distribute_list"]["prefix"] + ) + if "protocol" in config_data["distribute_list"]["prefix"]: + command += " {protocol}".format( + **config_data["distribute_list"]["prefix"] + ) + return command + + +def _tmplt_ospf_domain_id(config_data): + if "domain_id" in config_data: + command = "domain-id" + if "ip_address" in config_data["domain_id"]: + if "address" in config_data["domain_id"]["ip_address"]: + command += " {address}".format( + **config_data["domain_id"]["ip_address"] + ) + if "secondary" in config_data["domain_id"]["ip_address"]: + command += " {secondary}".format( + **config_data["domain_id"]["ip_address"] + ) + elif "null" in config_data["domain_id"]: + command += " null" + return command + + +def _tmplt_ospf_event_log(config_data): + if "event_log" in config_data: + command = "event-log" + if "one_shot" in config_data["event_log"]: + command += " one-shot" + if "pause" in config_data["event_log"]: + command += " pause" + if "size" in config_data["event_log"]: + command += " size {size}".format(**config_data["event_log"]) + return command + + +def _tmplt_ospf_manet(config_data): + if "manet" in config_data: + command = [] + if "cache" in config_data["manet"]: + cmd = "manet cache" + if "acknowledgement" in config_data["manet"]["cache"]: + cmd += " acknowledgement {acknowledgement}".format( + **config_data["manet"]["cache"] + ) + elif "redundancy" in config_data["manet"]["cache"]: + cmd += " redundancy {redundancy}".format( + **config_data["manet"]["cache"] + ) + command.append(cmd) + if "hello" in config_data["manet"] and config_data["manet"]["hello"]: + command.append("manet hello") + if "peering" in config_data["manet"]: + cmd = "manet peering selective" + if "per_interface" in config_data["manet"]["peering"]: + cmd += " per-interface" + if "redundancy" in config_data["manet"]["peering"]: + cmd += " redundancy {redundancy}".format( + **config_data["manet"]["peering"] + ) + command.append(cmd) + if "willingness" in config_data["manet"]: + command.append( + "manet willingness".format( + **config_data["manet"]["willingness"] + ) + ) + return command + + +def _tmplt_ospf_limit(config_data): + if "limit" in config_data: + command = "limit retransmissions" + if "dc" in config_data["limit"]: + if "number" in config_data["limit"]["dc"]: + command += " dc {number}".format(**config_data["limit"]["dc"]) + if "disable" in config_data["limit"]["dc"]: + command += " dc disable" + if "non_dc" in config_data["limit"]: + if "number" in config_data["limit"]["non_dc"]: + command += " non-dc {number}".format( + **config_data["limit"]["non_dc"] + ) + if "disable" in config_data["limit"]["dc"]: + command += " non-dc disable" + return command + + +def _tmplt_ospf_vrf_local_rib_criteria(config_data): + if "local_rib_criteria" in config_data: + command = "local-rib-criteria" + if "forwarding_address" in config_data["local_rib_criteria"]: + command += " forwarding-address" + if "inter_area_summary" in config_data["local_rib_criteria"]: + command += " inter-area-summary" + if "nssa_translation" in config_data["local_rib_criteria"]: + command += " nssa-translation" + return command + + +def _tmplt_ospf_log_adjacency_changes(config_data): + if "log_adjacency_changes" in config_data: + command = "log-adjacency-changes" + if "detail" in config_data["log_adjacency_changes"]: + command += " detail" + return command + + +def _tmplt_ospf_max_lsa(config_data): + if "max_lsa" in config_data: + command = "max-lsa {number}".format(**config_data["max_lsa"]) + if "threshold_value" in config_data["max_lsa"]: + command += " {threshold_value}".format(**config_data["max_lsa"]) + if "ignore_count" in config_data["max_lsa"]: + command += " ignore-count {ignore_count}".format( + **config_data["max_lsa"] + ) + if "ignore_time" in config_data["max_lsa"]: + command += " ignore-time {ignore_time}".format( + **config_data["max_lsa"] + ) + if "reset_time" in config_data["max_lsa"]: + command += " reset-time {reset_time}".format( + **config_data["max_lsa"] + ) + if "warning_only" in config_data["max_lsa"]: + command += " warning-only" + return command + + +def _tmplt_ospf_max_metric(config_data): + if "max_metric" in config_data: + command = "max-metric" + if "router_lsa" in config_data["max_metric"]: + command += " router-lsa" + if "external_lsa" in config_data["max_metric"]: + command += " external-lsa {external_lsa}".format( + **config_data["max_metric"] + ) + if "include_stub" in config_data["max_metric"]: + command += " include-stub" + if "on_startup" in config_data["max_metric"]: + if "time" in config_data["max_metric"]["on_startup"]: + command += " on-startup {time}".format( + **config_data["max_metric"]["on_startup"] + ) + elif "wait_for_bgp" in config_data["max_metric"]["on_startup"]: + command += " on-startup wait-for-bgp" + if "summary_lsa" in config_data["max_metric"]: + command += " summary-lsa {summary_lsa}".format( + **config_data["max_metric"] + ) + return command + + +def _tmplt_ospf_mpls_ldp(config_data): + if "ldp" in config_data["mpls"]: + command = "mpls ldp" + if "autoconfig" in config_data["mpls"]["ldp"]: + command += " autoconfig" + if "area" in config_data["mpls"]["ldp"]["autoconfig"]: + command += " area {area}".format( + **config_data["mpls"]["ldp"]["autoconfig"] + ) + elif "sync" in config_data["mpls"]["ldp"]: + command += " sync" + return command + + +def _tmplt_ospf_mpls_traffic_eng(config_data): + if "traffic_eng" in config_data["mpls"]: + command = "mpls traffic-eng" + if "area" in config_data["mpls"]["traffic_eng"]: + command += " area {area}".format( + **config_data["mpls"]["traffic_eng"] + ) + elif "autoroute_exclude" in config_data["mpls"]["traffic_eng"]: + command += " autoroute-exclude prefix-list {autoroute_exclude}".format( + **config_data["mpls"]["traffic_eng"] + ) + elif "interface" in config_data["mpls"]["traffic_eng"]: + command += " interface {int_type}".format( + **config_data["mpls"]["traffic_eng"]["interface"] + ) + if "area" in config_data["mpls"]["traffic_eng"]["interface"]: + command += " area {area}".format( + **config_data["mpls"]["traffic_eng"]["interface"] + ) + elif "mesh_group" in config_data["mpls"]["traffic_eng"]: + command += " mesh-group {id} {interface}".format( + **config_data["mpls"]["traffic_eng"]["mesh_group"] + ) + if "area" in config_data["mpls"]["traffic_eng"]["mesh_group"]: + command += " area {area}".format( + **config_data["mpls"]["traffic_eng"]["mesh_group"] + ) + elif "multicast_intact" in config_data["mpls"]["traffic_eng"]: + command += " multicast-intact" + elif "router_id_interface" in config_data["mpls"]["traffic_eng"]: + command += " router-id {router_id_interface}".format( + **config_data["mpls"]["traffic_eng"] + ) + return command + + +def _tmplt_ospf_neighbor(config_data): + if "neighbor" in config_data: + command = "neighbor" + if "address" in config_data["neighbor"]: + command += " {address}".format(**config_data["neighbor"]) + if "cost" in config_data["neighbor"]: + command += " cost {cost}".format(**config_data["neighbor"]) + if "database_filter" in config_data["neighbor"]: + command += " database-filter all out" + if "poll_interval" in config_data["neighbor"]: + command += " poll-interval {poll_interval}".format( + **config_data["neighbor"] + ) + if "priority" in config_data["neighbor"]: + command += " priority {priority}".format(**config_data["neighbor"]) + return command + + +def _tmplt_ospf_network(config_data): + if "network" in config_data: + command = "network" + if "address" in config_data["network"]: + command += " {address} {wildcard_bits}".format( + **config_data["network"] + ) + if "area" in config_data["network"]: + command += " area {area}".format(**config_data["network"]) + return command + + +def _tmplt_ospf_nsf_cisco(config_data): + if "cisco" in config_data["nsf"]: + command = "nsf cisco helper" + if "disable" in config_data["nsf"]["cisco"]: + command += " disable" + return command + + +def _tmplt_ospf_nsf_ietf(config_data): + if "ietf" in config_data["nsf"]: + command = "nsf ietf helper" + if "disable" in config_data["nsf"]["ietf"]: + command += " disable" + elif "strict_lsa_checking" in config_data["nsf"]["ietf"]: + command += " strict-lsa-checking" + return command + + +def _tmplt_ospf_queue_depth_hello(config_data): + if "hello" in config_data["queue_depth"]: + command = "queue-depth hello" + if "max_packets" in config_data["queue_depth"]["hello"]: + command += " {max_packets}".format( + **config_data["queue_depth"]["hello"] + ) + elif "unlimited" in config_data["queue_depth"]["hello"]: + command += " unlimited" + return command + + +def _tmplt_ospf_queue_depth_update(config_data): + if "update" in config_data["queue_depth"]: + command = "queue-depth update" + if "max_packets" in config_data["queue_depth"]["update"]: + command += " {max_packets}".format( + **config_data["queue_depth"]["update"] + ) + elif "unlimited" in config_data["queue_depth"]["update"]: + command += " unlimited" + return command + + +def _tmplt_ospf_summary_prefix(config_data): + if "summary_prefix" in config_data: + command = "summary-prefix {address} {mask}".format( + **config_data["summary_prefix"] + ) + if "not_advertise" in config_data["summary_prefix"]: + command += " not-advertise" + elif "nssa_only" in config_data["summary_prefix"]: + command += " nssa-only" + if "tag" in config_data["summary_prefix"]: + command += " tag {tag}".format(**config_data["summary_prefix"]) + return command + + +def _tmplt_ospf_timers_pacing(config_data): + if "pacing" in config_data["timers"]: + command = "timers pacing" + if "flood" in config_data["timers"]["pacing"]: + command += " flood {flood}".format( + **config_data["timers"]["pacing"] + ) + elif "lsa_group" in config_data["timers"]["pacing"]: + command += " lsa-group {lsa_group}".format( + **config_data["timers"]["pacing"] + ) + elif "retransmission" in config_data["timers"]["pacing"]: + command += " retransmission {retransmission}".format( + **config_data["timers"]["pacing"] + ) + return command + + +def _tmplt_ospf_ttl_security(config_data): + if "ttl_security" in config_data: + command = "ttl-security all-interfaces" + if "hops" in config_data["ttl_security"]: + command += " hops {hops}".format(**config_data["ttl_security"]) + return command + + +class Ospfv3Template(NetworkTemplate): + def __init__(self, lines=None): + super(Ospfv3Template, self).__init__(lines=lines, tmplt=self) + + PARSERS = [ + { + "name": "pid", + "getval": re.compile( + r""" + ^router\s + ospfv3* + \s(?P<pid>\S+) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospfv3_cmd, + "result": { + "processes": {"{{ pid }}": {"process_id": "{{ pid|int }}"}} + }, + "shared": True, + }, + { + "name": "adjacency", + "getval": re.compile( + r"""\s+adjacency + \sstagger* + \s*((?P<min>\d+)|(?P<none_adj>none))* + \s*(?P<max>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_adjacency_cmd, + "result": { + "processes": { + "{{ pid }}": { + "adjacency": { + "min_adjacency": "{{ min|int }}", + "max_adjacency": "{{ max|int }}", + "none": "{{ True if none_adj is defined else None }}", + } + } + } + }, + }, + { + "name": "area.authentication", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \s*(?P<auth>authentication)* + \s*(?P<md>message-digest) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_authentication, + "compval": "authentication", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "authentication": { + "enable": "{{ True if auth is defined and md is undefined }}", + "message_digest": "{{ not not md }}", + }, + } + } + } + } + }, + }, + { + "name": "area.capability", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \s*(?P<capability>capability)* + \s*(?P<df>default-exclusion) + *$""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} capability default-exclusion", + "compval": "capability", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "capability": "{{ not not capability }}", + } + } + } + } + }, + }, + { + "name": "area.default_cost", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \sdefault-cost* + \s*(?P<default_cost>\S+) + *$""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} default-cost {{ default_cost }}", + "compval": "default_cost", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "default_cost": "{{ default_cost|int }}", + } + } + } + } + }, + }, + { + "name": "area.filter_list", + "getval": re.compile( + r"""\s+area + \s*(?P<area_id>\S+)* + \s*filter-list\sprefix* + \s*(?P<name>\S+)* + \s*(?P<dir>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_filter, + "compval": "filter_list", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "filter_list": [ + { + "name": "{{ name }}", + "direction": "{{ dir }}", + } + ], + } + } + } + } + }, + }, + { + "name": "area.nssa", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \s(?P<nssa>nssa)* + \s*(?P<no_redis>no-redistribution)* + \s*(?P<def_origin>default-information-originate)* + \s*(?P<metric>metric\s\d+)* + \s*(?P<metric_type>metric-type\s\d+)* + \s*(?P<no_summary>no-summary)* + \s*(?P<no_ext>no-ext-capability)*$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa, + "compval": "nssa", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "set": "{{ True if nssa is defined and def_origin is undefined and " + "no_ext is undefined and no_redis is undefined and nssa_only is undefined }}", + "default_information_originate": { + "set": "{{ True if def_origin is defined and metric is undefined and " + "metric_type is undefined and nssa_only is undefined }}", + "metric": "{{ metric.split(" + ")[1]|int }}", + "metric_type": "{{ metric_type.split(" + ")[1]|int }}", + "nssa_only": "{{ True if nssa_only is defined }}", + }, + "no_ext_capability": "{{ True if no_ext is defined }}", + "no_redistribution": "{{ True if no_redis is defined }}", + "no_summary": "{{ True if no_summary is defined }}", + }, + } + } + } + } + }, + }, + { + "name": "area.nssa.translate", + "getval": re.compile( + r"""\s+area* + \s*(?P<area_id>\S+)* + \s*(?P<nssa>nssa)* + \stranslate\stype7* + \s(?P<translate_always>always)* + \s* (?P<translate_supress>suppress-fa) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa_translate, + "compval": "nssa.translate", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "translate": "{{ translate_always if translate_always is defined else translate_supress if translate_supress is defined }}" + }, + } + } + } + } + }, + }, + { + "name": "area.ranges", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \srange + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + \s(?P<netmask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*((?P<advertise>advertise)|(?P<not_advertise>not-advertise))* + \s*(?P<cost>cost\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_ranges, + "compval": "ranges", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "ranges": [ + { + "address": "{{ address }}", + "netmask": "{{ netmask }}", + "advertise": "{{ True if advertise is defined }}", + "cost": "{{ cost.split(" ")[1]|int }}", + "not_advertise": "{{ True if not_advertise is defined }}", + } + ], + } + } + } + } + }, + }, + { + "name": "area.sham_link", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \ssham-link + \s(?P<source>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + \s(?P<destination>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<cost>cost\s\d+)* + \s*(?P<ttl_security>ttl-security\shops\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_sham_link, + "compval": "sham_link", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "sham_link": { + "source": "{{ source }}", + "destination": "{{ destination }}", + "cost": "{{ cost.split(" ")[1]|int }}", + "ttl_security": '{{ ttl_security.split("hops ")[1] }}', + }, + } + } + } + } + }, + }, + { + "name": "area.stub", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \s(?P<stub>stub)* + \s*(?P<no_ext>no-ext-capability)* + \s*(?P<no_sum>no-summary) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_stub_link, + "compval": "stub", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "stub": { + "set": "{{ True if stub is defined and no_ext is undefined and no_sum is undefined }}", + "no_ext_capability": "{{ True if no_ext is defined }}", + "no_summary": "{{ True if no_sum is defined }}", + }, + } + } + } + } + }, + }, + { + "name": "auto_cost", + "getval": re.compile( + r"""\s+(?P<auto_cost>auto-cost)* + \s*(?P<ref_band>reference-bandwidth\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_auto_cost, + "result": { + "processes": { + "{{ pid }}": { + "auto_cost": { + "set": "{{ True if auto_cost is defined and ref_band is undefined }}", + "reference_bandwidth": '{{ ref_band.split(" ")[1] }}', + } + } + } + }, + }, + { + "name": "bfd", + "getval": re.compile( + r"""\s+bfd* + \s*(?P<bfd>all-interfaces) + *$""", + re.VERBOSE, + ), + "setval": "bfd all-interfaces", + "result": { + "processes": { + "{{ pid }}": {"bfd": "{{ True if bfd is defined }}"} + } + }, + }, + { + "name": "capability", + "getval": re.compile( + r"""\s+capability* + \s*((?P<lls>lls)|(?P<opaque>opaque)|(?P<transit>transit)|(?P<vrf_lite>vrf-lite)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_capability, + "result": { + "processes": { + "{{ pid }}": { + "capability": { + "lls": "{{ True if lls is defined }}", + "opaque": "{{ True if opaque is defined }}", + "transit": "{{ True if transit is defined }}", + "vrf_lite": "{{ True if vrf_lite is defined }}", + } + } + } + }, + }, + { + "name": "compatible", + "getval": re.compile( + r"""\s+compatible* + \s*((?P<rfc1583>rfc1583)|(?P<rfc1587>rfc1587)|(?P<rfc5243>rfc5243)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_compatible, + "result": { + "processes": { + "{{ pid }}": { + "compatible": { + "rfc1583": "{{ True if rfc1583 is defined }}", + "rfc1587": "{{ True if rfc1587 is defined }}", + "rfc5243": "{{ True if rfc5243 is defined }}", + } + } + } + }, + }, + { + "name": "default_information", + "getval": re.compile( + r"""\s+default-information* + \s*(?P<originate>originate)* + \s*(?P<always>always)* + \s*(?P<metric>metric\s\d+)* + \s*(?P<metric_type>metric-type\s\d+)* + \s*(?P<route_map>route-map\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_default_information, + "result": { + "processes": { + "{{ pid }}": { + "default_information": { + "originate": "{{ True if originate is defined }}", + "always": "{{ True if always is defined }}", + "metric": "{{ metric.split(" ")[1]|int }}", + "metric_type": "{{ metric_type.split(" + ")[1]|int }}", + "route_map": "{{ route_map.split(" ")[1] }}", + } + } + } + }, + }, + { + "name": "default_metric", + "getval": re.compile( + r"""\s+default-metric(?P<default_metric>\s\d+) + *$""", + re.VERBOSE, + ), + "setval": "default-metric {{ default_metric }}", + "result": { + "processes": { + "{{ pid }}": {"default_metric": "{{ default_metric| int}}"} + } + }, + }, + { + "name": "discard_route", + "getval": re.compile( + r"""\s+(?P<discard_route>discard-route)* + \s*(?P<external>external\s\d+)* + \s*(?P<internal>internal\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_discard_route, + "result": { + "processes": { + "{{ pid }}": { + "discard_route": { + "set": "{{ True if discard_route is defined and external is undefined and internal is undefined }}", + "external": "{{ external.split(" ")[1]|int }}", + "internal": "{{ internal.split(" ")[1]|int }}", + } + } + } + }, + }, + { + "name": "distance.admin_distance", + "getval": re.compile( + r"""\s+distance + \s(?P<admin_dist>\S+)* + \s*(?P<source>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<wildcard>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<acl>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_admin_distance, + "compval": "admin_distance", + "result": { + "processes": { + "{{ pid }}": { + "distance": { + "admin_distance": { + "distance": "{{ admin_dist }}", + "address": "{{ source }}", + "wildcard_bits": "{{ wildcard }}", + "acl": "{{ acl }}", + } + } + } + } + }, + }, + { + "name": "distance.ospf", + "getval": re.compile( + r"""\s+distance + \sospf* + \s*(?P<intra>intra-area\s\d+)* + \s*(?P<inter>inter-area\s\d+)* + \s*(?P<external>external\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_ospf, + "compval": "ospf", + "result": { + "processes": { + "{{ pid }}": { + "distance": { + "ospf": { + "inter_area": "{{ inter.split(" ")[1]|int }}", + "intra_area": "{{ intra.split(" ")[1]|int }}", + "external": "{{ external.split(" ")[1]|int }}", + } + } + } + } + }, + }, + { + "name": "distribute_list.acls", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<name>\S+)* + \s*(?P<dir>\S+)* + \s*(?P<int_pro>\S+\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distribute_list_acls, + "compval": "distribute_list.acls", + "result": { + "processes": { + "{{ pid }}": { + "distribute_list": { + "acls": [ + { + "name": "{{ name }}", + "direction": "{{ dir }}", + "interface": '{{ int_pro if dir == "in" }}', + "protocol": '{{ int_pro if dir == "out" }}', + } + ] + } + } + } + }, + }, + { + "name": "distribute_list.prefix", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<prefix>prefix\s\S+)* + \s*(?P<gateway>gateway\s\S+)* + \s*(?P<dir>\S+)* + \s*(?P<int_pro>\S+\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distribute_list_prefix, + "compval": "distribute_list.prefix", + "result": { + "processes": { + "{{ pid }}": { + "distribute_list": { + "prefix": { + "name": "{{ prefix.split(" ")[1] }}", + "gateway_name": "{{ gateway.split(" + ")[1] if prefix is defined }}", + "direction": "{{ dir if gateway is undefined }}", + "interface": '{{ int_pro if dir == "in" }}', + "protocol": '{{ int_pro if dir == "out" }}', + } + } + } + } + }, + }, + { + "name": "distribute_list.route_map", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<route_map>route-map\s\S+)* + \s*(?P<dir>\S+) + *$""", + re.VERBOSE, + ), + "setval": "distribute-list route-map {{ distribute_list.route_map.name }} in", + "compval": "distribute_list.route_map", + "result": { + "processes": { + "{{ pid }}": { + "distribute_list": { + "route_map": { + "name": "{{ route_map.split(" ")[1] }}" + } + } + } + } + }, + }, + { + "name": "domain_id", + "getval": re.compile( + r"""\s+domain-id + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<secondary>secondary)* + \s*(?P<null>null) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_domain_id, + "result": { + "processes": { + "{{ pid }}": { + "domain_id": { + "ip_address": { + "address": "{{ address }}", + "secondary": "{{ True if secondary is defined }}", + }, + "null": "{{ True if null is defined }}", + } + } + } + }, + }, + { + "name": "domain_tag", + "getval": re.compile( + r"""\s+domain-tag + \s(?P<tag>\d+) + *$""", + re.VERBOSE, + ), + "setval": "domain-tag {{ domain_tag }}", + "result": { + "processes": {"{{ pid }}": {"domain_tag": "{{ tag|int }}"}} + }, + }, + { + "name": "event_log", + "getval": re.compile( + r"""\s+(?P<event_log>event-log)* + \s*(?P<one_shot>one-shot)* + \s*(?P<pause>pause)* + \s*(?P<size>size\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_event_log, + "result": { + "processes": { + "{{ pid }}": { + "event_log": { + "enable": "{{ True if event_log is defined and one_shot is undefined and pause is undefined and size is undefined }}", + "one_shot": "{{ True if one_shot is defined }}", + "pause": "{{ True if pause is defined }}", + "size": "{{ size.split(" ")[1]|int }}", + } + } + } + }, + }, + { + "name": "help", + "getval": re.compile( + r"""\s+(?P<help>help) + *$""", + re.VERBOSE, + ), + "setval": "help", + "result": { + "processes": { + "{{ pid }}": {"help": "{{ True if help is defined }}"} + } + }, + }, + { + "name": "ignore", + "getval": re.compile( + r"""\s+(?P<ignore>ignore) + *$""", + re.VERBOSE, + ), + "setval": "ignore lsa mospf", + "result": { + "processes": { + "{{ pid }}": {"ignore": "{{ True if ignore is defined }}"} + } + }, + }, + { + "name": "interface_id", + "getval": re.compile( + r"""\s+(?P<interface_id>interface-id\ssnmp-if-index) + *$""", + re.VERBOSE, + ), + "setval": "interface-id snmp-if-index", + "result": { + "processes": { + "{{ pid }}": { + "interface_id": "{{ True if interface_id is defined }}" + } + } + }, + }, + { + "name": "ispf", + "getval": re.compile( + r"""\s+(?P<ispf>ispf) + *$""", + re.VERBOSE, + ), + "setval": "ispf", + "result": { + "processes": { + "{{ pid }}": {"ispf": "{{ True if ispf is defined }}"} + } + }, + }, + { + "name": "limit", + "getval": re.compile( + r"""\s+limit\sretransmissions + \s((?P<dc_num>dc\s\d+)|(?P<dc_disable>dc\sdisable))* + \s*((?P<non_dc_num>non-dc\s\d+)|(?P<non_dc_disable>non-dc\sdisable)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_limit, + "result": { + "processes": { + "{{ pid }}": { + "limit": { + "dc": { + "number": "{{ dc_num.split(" ")[1]|int }}", + "disable": "{{ True if dc_disable is defined }}", + }, + "non_dc": { + "number": "{{ non_dc_num.split(" ")[1]|int }}", + "disable": "{{ True if dc_disable is defined }}", + }, + } + } + } + }, + }, + { + "name": "local_rib_criteria", + "getval": re.compile( + r"""\s+(?P<local>local-rib-criteria)* + \s*(?P<forward>forwarding-address)* + \s*(?P<inter>inter-area-summary)* + \s*(?P<nssa>nssa-translation) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_vrf_local_rib_criteria, + "result": { + "processes": { + "{{ pid }}": { + "local_rib_criteria": { + "enable": "{{ True if local is defined and forward is undefined and inter is undefined and nssa is undefined }}", + "forwarding_address": "{{ True if forward is defined }}", + "inter_area_summary": "{{ True if inter is defined }}", + "nssa_translation": "{{ True if nssa is defined }}", + } + } + } + }, + }, + { + "name": "log_adjacency_changes", + "getval": re.compile( + r"""\s+(?P<log>log-adjacency-changes)* + \s*(?P<detail>detail) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_log_adjacency_changes, + "result": { + "processes": { + "{{ pid }}": { + "log_adjacency_changes": { + "set": "{{ True if log is defined and detail is undefined }}", + "detail": "{{ True if detail is defined }}", + } + } + } + }, + }, + { + "name": "manet", + "getval": re.compile( + r"""\s+manet* + \s*(?P<cache>cache)* + \s*(?P<acknowledgement>acknowledgement\s\d+)* + \s*(?P<update>update\s\d+)* + \s*(?P<hello>hello)* + \s*(?P<unicast>unicast)* + \s*(?P<multicast>multicast)* + \s*(?P<peering>peering\sselective)* + \s*(?P<disable>disable)* + \s*(?P<per_interface>per-interface)* + \s*(?P<redundancy>redundancy\s\d+)* + \s*(?P<willingness>willingness\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_manet, + "compval": "manet", + "result": { + "processes": { + "{{ pid }}": { + "manet": { + "cache": { + "acknowledgement": "{{ acknowledgement.split(" + ")[1] }}", + "update": "{{ update.split(" ")[1] }}", + }, + "hello": { + "unicast": "{{ True if unicast is defined }}", + "multicast": "{{ True if multicast is defined }}", + }, + "peering": { + "set": "{{ True if peering is defined }}", + "disable": "{{ True if disable is defined }}", + "per_interface": "{{ True if per_interface is defined }}", + "redundancy": "{{ redundancy.split(" ")[1] }}", + }, + "willingness": "{{ willingness.split(" ")[1] }}", + } + } + } + }, + }, + { + "name": "max_lsa", + "getval": re.compile( + r"""\s+max-lsa + \s(?P<number>\d+)* + \s*(?P<threshold>\d+)* + \s*(?P<ignore_count>ignore-count\s\d+)* + \s*(?P<ignore_time>ignore-time\s\d+)* + \s*(?P<reset_time>reset-time\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_lsa, + "result": { + "processes": { + "{{ pid }}": { + "max_lsa": { + "number": "{{ number }}", + "threshold_value": "{{ threshold }}", + "ignore_count": "{{ ignore_count.split(" ")[1] }}", + "ignore_time": "{{ ignore_time.split(" ")[1] }}", + "reset_time": "{{ reset_time.split(" ")[1] }}", + "warning_only": "{{ True if warning is defined }}", + } + } + } + }, + }, + { + "name": "max_metric", + "getval": re.compile( + r"""\s+max-metric* + \s*(?P<router_lsa>router-lsa)* + \s*(?P<include_stub>include-stub)* + \s*(?P<external_lsa>external-lsa\s\d+)* + \s*(?P<startup_time>on-startup\s\d+)* + \s*(?P<startup_wait>on-startup\s\S+)* + \s*(?P<summary_lsa>summary-lsa\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_metric, + "result": { + "processes": { + "{{ pid }}": { + "max_metric": { + "router_lsa": "{{ True if router_lsa is defined }}", + "external_lsa": "{{ external_lsa.split(" ")[1] }}", + "include_stub": "{{ ignore_count.split(" ")[1] }}", + "on_startup": { + "time": "{{ startup_time.split(" ")[1] }}", + "wait_for_bgp": "{{ True if startup_wait is defined }}", + }, + "summary_lsa": "{{ summary_lsa.split(" ")[1] }}", + } + } + } + }, + }, + { + "name": "maximum_paths", + "getval": re.compile( + r"""\s+maximum-paths* + \s+(?P<paths>\d+) + *$""", + re.VERBOSE, + ), + "setval": "maximum-paths {{ maximum_paths }}", + "result": { + "processes": {"{{ pid }}": {"maximum_paths": "{{ paths }}"}} + }, + }, + { + "name": "mpls.ldp", + "getval": re.compile( + r"""\s+mpls + \sldp* + \s*(?P<autoconfig>autoconfig*\s*(?P<area>area\s\S+))* + \s*(?P<sync>sync) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_mpls_ldp, + "compval": "ldp", + "result": { + "processes": { + "{{ pid }}": { + "mpls": { + "ldp": { + "autoconfig": { + "set": "{{ True if autoconfig is defined and area is undefined }}", + "area": "{{ area.split(" ")[1] }}", + }, + "sync": "{{ True if sync is defined }}", + } + } + } + } + }, + }, + { + "name": "mpls.traffic_eng", + "getval": re.compile( + r"""\s+mpls + \straffic-eng* + \s*(?P<area>area\s\S+)* + \s*(?P<autoroute>autoroute-exclude\s\S+\s\S+)* + \s*(?P<interface>interface\s(?P<int_type>\S+\s\S+)\s(?P<int_area>area\s\S+))* + \s*(?P<mesh>mesh-group\s\d+\s(?P<mesh_int>\S+\s\S+)\s(?P<mesh_area>area\s\d+))* + \s*(?P<multicast>multicast-intact)* + \s*(?P<router>router-id\s(?P<router_int>\S+\s\S+)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_mpls_traffic_eng, + "compval": "traffic_eng", + "result": { + "processes": { + "{{ pid }}": { + "mpls": { + "traffic_eng": { + "area": "{{ area.split(" ")[1] }}", + "autoroute_exclude": "{{ autoroute.split(" + ")[2] }}", + "interface": { + "interface_type": "{{ int_type }}", + "area": "{{ int_area.split(" ")[1] }}", + }, + "mesh_group": { + "id": "{{ mesh.split(" ")[1] }}", + "interface": "{{ mest_int }}", + "area": "{{ mesh_area.split(" ")[1] }}", + }, + "multicast_intact": "{{ True if multicast is defined }}", + "router_id_interface": "{{ router_int }}", + } + } + } + } + }, + }, + { + "name": "neighbor", + "getval": re.compile( + r"""\s+neighbor + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<cost>cost\s\d+)* + \s*(?P<db_filter>database-filter\sall\sout)* + \s*(?P<poll>poll-interval\s\d+)* + \s*(?P<priority>priority\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_neighbor, + "result": { + "processes": { + "{{ pid }}": { + "neighbor": { + "address": "{{ address }}", + "cost": "{{ cost.split(" ")[1] }}", + "database_filter": "{{ True if db_filter is defined }}", + "poll_interval": "{{ poll.split(" ")[1] }}", + "priority": "{{ priority.split(" ")[1] }}", + } + } + } + }, + }, + { + "name": "network", + "getval": re.compile( + r"""\s+network + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<wildcard>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<area>area\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_network, + "result": { + "processes": { + "{{ pid }}": { + "network": { + "address": "{{ address }}", + "wildcard_bits": "{{ wildcard }}", + "area": "{{ area.split(" ")[1] }}", + } + } + } + }, + }, + { + "name": "nsf.cisco", + "getval": re.compile( + r"""\s+nsf + \s(?P<cisco>cisco)* + \s*(?P<helper>helper)* + \s*(?P<disable>disable) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_nsf_cisco, + "compval": "cisco", + "result": { + "processes": { + "{{ pid }}": { + "nsf": { + "cisco": { + "helper": "{{ True if helper is defined }}", + "disable": "{{ True if disable is defined }}", + } + } + } + } + }, + }, + { + "name": "nsf.ietf", + "getval": re.compile( + r"""\s+nsf + \s(?P<ietf>ietf)* + \s*(?P<helper>helper)* + \s*(?P<disable>disable)* + \s*(?P<strict>strict-lsa-checking) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_nsf_ietf, + "compval": "ietf", + "result": { + "processes": { + "{{ pid }}": { + "nsf": { + "ietf": { + "helper": "{{ True if helper is defined }}", + "disable": "{{ True if disable is defined }}", + "strict_lsa_checking": "{{ True if strict is defined }}", + } + } + } + } + }, + }, + { + "name": "passive_interface", + "getval": re.compile( + r"""\s+passive-interface + \s(?P<interface>\S+\s\S+) + *$""", + re.VERBOSE, + ), + "setval": "passive-interface {{ passive_interface }}", + "result": { + "processes": { + "{{ pid }}": {"passive_interface": "{{ interface }}"} + } + }, + }, + { + "name": "prefix_suppression", + "getval": re.compile( + r"""\s+(?P<prefix_sup>prefix-suppression) + *$""", + re.VERBOSE, + ), + "setval": "prefix-suppression", + "result": { + "processes": { + "{{ pid }}": { + "prefix_suppression": "{{ True if prefix_sup is defined }}" + } + } + }, + }, + { + "name": "priority", + "getval": re.compile( + r"""\s+priority + \s(?P<priority>\d+) + *$""", + re.VERBOSE, + ), + "setval": "priority {{ priority }}", + "result": { + "processes": {"{{ pid }}": {"priority": "{{ priority }}"}} + }, + }, + { + "name": "queue_depth.hello", + "getval": re.compile( + r"""\s+queue-depth + \shello* + \s*(?P<max_packets>\d+)* + \s*(?P<unlimited>unlimited) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_queue_depth_hello, + "compval": "hello", + "result": { + "processes": { + "{{ pid }}": { + "queue_depth": { + "hello": { + "max_packets": "{{ max_packets }}", + "unlimited": "{{ True if unlimited is defined }}", + } + } + } + } + }, + }, + { + "name": "queue_depth.update", + "getval": re.compile( + r"""\s+queue-depth + \supdate* + \s*(?P<max_packets>\d+)* + \s*(?P<unlimited>unlimited) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_queue_depth_update, + "compval": "update", + "result": { + "processes": { + "{{ pid }}": { + "queue_depth": { + "update": { + "max_packets": "{{ max_packets }}", + "unlimited": "{{ True if unlimited is defined }}", + } + } + } + } + }, + }, + { + "name": "router_id", + "getval": re.compile( + r"""\s+router-id + \s(?P<id>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + *$""", + re.VERBOSE, + ), + "setval": "router-id {{ router_id }}", + "result": {"processes": {"{{ pid }}": {"router_id": "{{ id }}"}}}, + }, + { + "name": "shutdown", + "getval": re.compile( + r"""\s+(?P<shutdown>shutdown) + *$""", + re.VERBOSE, + ), + "setval": "shutdown", + "result": { + "processes": { + "{{ pid }}": { + "shutdown": "{{ True if shutdown is defined }}" + } + } + }, + }, + { + "name": "timers.lsa", + "getval": re.compile( + r"""\s+timers + \slsa + \sarrival + \s(?P<lsa>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers lsa arrival {{ timers.lsa }}", + "compval": "lsa", + "result": { + "processes": {"{{ pid }}": {"timers": {"lsa": "{{ lsa }}"}}} + }, + }, + { + "name": "timers.pacing", + "getval": re.compile( + r"""\s+timers + \spacing + \s(?P<flood>flood\s\d+)* + \s*(?P<lsa_group>lsa-group\s\d+)* + \s*(?P<retransmission>retransmission\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_timers_pacing, + "compval": "pacing", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "pacing": { + "flood": "{{ flood.split(" ")[1] }}", + "lsa_group": "{{ lsa_group.split(" ")[1] }}", + "retransmission": "{{ retransmission.split(" + ")[1] }}", + } + } + } + } + }, + }, + { + "name": "timers.throttle.lsa", + "getval": re.compile( + r"""\s+timers + \sthrottle + \s*(?P<lsa>lsa)* + \s*(?P<first_delay>\d+)* + \s*(?P<min_delay>\d+)* + \s*(?P<max_delay>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers throttle lsa {{ throttle.lsa.first_delay }} {{ throttle.lsa.min_delay }} {{ throttle.lsa.max_delay }}", + "compval": "throttle.lsa", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "lsa": { + "first_delay": "{{ first_delay }}", + "min_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + } + } + } + } + } + }, + }, + { + "name": "timers.throttle.spf", + "getval": re.compile( + r"""\s+timers + \sthrottle + \s*(?P<spf>spf)* + \s*(?P<first_delay>\d+)* + \s*(?P<min_delay>\d+)* + \s*(?P<max_delay>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers throttle spf {{ throttle.spf.receive_delay }} {{ throttle.spf.between_delay }} {{ throttle.spf.max_delay }}", + "compval": "throttle.spf", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "spf": { + "receive_delay": "{{ first_delay }}", + "between_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + } + } + } + } + } + }, + }, + { + "name": "traffic_share", + "getval": re.compile( + r"""\s+(?P<traffic>traffic-share\smin\sacross-interfaces) + *$""", + re.VERBOSE, + ), + "setval": "traffic-share min across-interfaces", + "result": { + "processes": { + "{{ pid }}": { + "traffic_share": "{{ True if traffic is defined }}" + } + } + }, + }, + { + "name": "ttl_security", + "getval": re.compile( + r"""\s+ttl-security + \s(?P<interfaces>all-interfaces)* + \s*(?P<hops>hops\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_ttl_security, + "result": { + "processes": { + "{{ pid }}": { + "ttl_security": { + "set": "{{ True if interfaces is defined and hops is undefined }}", + "hops": "{{ hops.split(" ")[1] }}", + } + } + } + }, + }, + { + "name": "address_family", + "getval": re.compile( + r"""\s+address-family* + \s*(?P<afi>\S+)* + \s*(?P<unicast>unicast)* + \s*(?P<vrf>vrf\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_address_family_cmd, + # "compval": "afi", + "result": { + "address_family": [ + { + "afi": "{{ afi }}", + "unicast": "{{ True if unicast is defined }}", + "vrf": "{{ vrf.split(" ")[1] }}", + } + ] + }, + "shared": True, + }, + { + "name": "address_family.exit", + "getval": re.compile( + r"""\s+exit-address-family + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_address_family_cmd, + "result": { + "address_family": [ + { + "exit": { + "pid": "{{ id }}", + "afi": "{{ afi }}", + "unicast": "{{ True if unicast is defined }}", + "vrf": "{{ vrf.split(" ")[1] }}", + } + } + ] + }, + }, + { + "name": "address_family.adjacency", + "getval": re.compile( + r"""\s+adjacency + \sstagger* + \s*((?P<min>\d+)|(?P<none_adj>none))* + \s*(?P<max>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_adjacency_cmd, + "compval": "adjacency", + "result": { + "address_family": [ + { + "adjacency": { + "min_adjacency": "{{ min|int }}", + "max_adjacency": "{{ max|int }}", + "none": "{{ True if none_adj is defined else None }}", + } + } + ] + }, + }, + { + "name": "address_family.area.authentication", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \s*(?P<auth>authentication)* + \s*(?P<md>message-digest) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_authentication, + "compval": "authentication", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "authentication": { + "enable": "{{ True if auth is defined and md is undefined }}", + "message_digest": "{{ not not md }}", + }, + } + } + } + ] + }, + }, + { + "name": "address_family.area.capability", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \s*(?P<capability>capability)* + \s*(?P<df>default-exclusion) + *$""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} capability default-exclusion", + "compval": "capability", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "capability": "{{ not not capability }}", + } + } + } + ] + }, + }, + { + "name": "address_family.area.default_cost", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \sdefault-cost* + \s*(?P<default_cost>\S+) + *$""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} default-cost {{ default_cost }}", + "compval": "default_cost", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "default_cost": "{{ default_cost|int }}", + } + } + } + ] + }, + }, + { + "name": "address_family.area.filter_list", + "getval": re.compile( + r"""\s+area + \s*(?P<area_id>\S+)* + \s*filter-list\sprefix* + \s*(?P<name>\S+)* + \s*(?P<dir>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_filter, + "compval": "filter_list", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "filter_list": [ + { + "name": "{{ name }}", + "direction": "{{ dir }}", + } + ], + } + } + } + ] + }, + }, + { + "name": "address_family.area.nssa", + "getval": re.compile( + r"""\s+area* + \s*(?P<area_id>\S+)* + \s*(?P<nssa>nssa)* + \s*(?P<no_redis>no-redistribution)* + \s*(?P<def_origin>default-information-originate)* + \s*(?P<metric>metric\s\d+)* + \s*(?P<metric_type>metric-type\s\d+)* + \s*(?P<nssa_only>nssa-only)* + \s*(?P<no_summary>no-summary) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa, + "compval": "nssa", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "set": "{{ True if nssa is defined and def_origin is undefined and " + "no_ext is undefined and no_redis is undefined and nssa_only is undefined }}", + "default_information_originate": { + "set": "{{ True if def_origin is defined and metric is undefined and " + "metric_type is undefined and nssa_only is undefined }}", + "metric": "{{ metric.split(" + ")[1]|int }}", + "metric_type": "{{ metric_type.split(" + ")[1]|int }}", + "nssa_only": "{{ True if nssa_only is defined }}", + }, + "no_ext_capability": "{{ True if no_ext is defined }}", + "no_redistribution": "{{ True if no_redis is defined }}", + "no_summary": "{{ True if no_summary is defined }}", + }, + } + } + } + ] + }, + }, + { + "name": "address_family.area.nssa.translate", + "getval": re.compile( + r"""\s+area* + \s*(?P<area_id>\S+)* + \s*(?P<nssa>nssa)* + \stranslate\stype7* + \s(?P<translate_always>always)* + \s* (?P<translate_supress>suppress-fa) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa_translate, + "compval": "nssa.translate", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "translate": "{{ translate_always if translate_always is defined else translate_supress if translate_supress is defined }}" + }, + } + } + } + ] + }, + }, + { + "name": "address_family.area.ranges", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \srange + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + \s(?P<netmask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*((?P<advertise>advertise)|(?P<not_advertise>not-advertise))* + \s*(?P<cost>cost\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_ranges, + "compval": "ranges", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "ranges": [ + { + "address": "{{ address }}", + "netmask": "{{ netmask }}", + "advertise": "{{ True if advertise is defined }}", + "cost": "{{ cost.split(" ")[1]|int }}", + "not_advertise": "{{ True if not_advertise is defined }}", + } + ], + } + } + } + ] + }, + }, + { + "name": "address_family.area.sham_link", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \ssham-link + \s(?P<source>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + \s(?P<destination>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<cost>cost\s\d+)* + \s*(?P<ttl_security>ttl-security\shops\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_sham_link, + "compval": "sham_link", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "sham_link": { + "source": "{{ source }}", + "destination": "{{ destination }}", + "cost": "{{ cost.split(" ")[1]|int }}", + "ttl_security": '{{ ttl_security.split("hops ")[1] }}', + }, + } + } + } + ] + }, + }, + { + "name": "address_family.area.stub", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \s(?P<stub>stub)* + \s*(?P<no_ext>no-ext-capability)* + \s*(?P<no_sum>no-summary) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_stub_link, + "compval": "stub", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "stub": { + "set": "{{ True if stub is defined and no_ext is undefined and no_sum is undefined }}", + "no_ext_capability": "{{ True if no_ext is defined }}", + "no_summary": "{{ True if no_sum is defined }}", + }, + } + } + } + ] + }, + }, + { + "name": "address_family.auto_cost", + "getval": re.compile( + r"""\s+(?P<auto_cost>auto-cost)* + \s*(?P<ref_band>reference-bandwidth\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_auto_cost, + "compval": "auto_cost", + "result": { + "address_family": [ + { + "auto_cost": { + "set": "{{ True if auto_cost is defined and ref_band is undefined }}", + "reference_bandwidth": '{{ ref_band.split(" ")[1] }}', + } + } + ] + }, + }, + { + "name": "address_family.bfd", + "getval": re.compile( + r"""\s+bfd* + \s*(?P<bfd>all-interfaces) + *$""", + re.VERBOSE, + ), + "setval": "bfd all-interfaces", + "compval": "bfd", + "result": { + "address_family": [{"bfd": "{{ True if bfd is defined }}"}] + }, + }, + { + "name": "address_family.capability", + "getval": re.compile( + r"""\s+capability* + \s*((?P<lls>lls)|(?P<opaque>opaque)|(?P<transit>transit)|(?P<vrf_lite>vrf-lite)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_capability, + "compval": "capability", + "result": { + "address_family": [ + { + "capability": { + "lls": "{{ True if lls is defined }}", + "opaque": "{{ True if opaque is defined }}", + "transit": "{{ True if transit is defined }}", + "vrf_lite": "{{ True if vrf_lite is defined }}", + } + } + ] + }, + }, + { + "name": "address_family.compatible", + "getval": re.compile( + r"""\s+compatible* + \s*((?P<rfc1583>rfc1583)|(?P<rfc1587>rfc1587)|(?P<rfc5243>rfc5243)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_compatible, + "compval": "compatible", + "result": { + "address_family": [ + { + "compatible": { + "rfc1583": "{{ True if rfc1583 is defined }}", + "rfc1587": "{{ True if rfc1587 is defined }}", + "rfc5243": "{{ True if rfc5243 is defined }}", + } + } + ] + }, + }, + { + "name": "address_family.default_information", + "getval": re.compile( + r"""\s+default-information* + \s*(?P<originate>originate)* + \s*(?P<always>always)* + \s*(?P<metric>metric\s\d+)* + \s*(?P<metric_type>metric-type\s\d+)* + \s*(?P<route_map>route-map\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_default_information, + "compval": "default_information", + "result": { + "address_family": [ + { + "default_information": { + "originate": "{{ True if originate is defined }}", + "always": "{{ True if always is defined }}", + "metric": "{{ metric.split(" ")[1]|int }}", + "metric_type": "{{ metric_type.split(" + ")[1]|int }}", + "route_map": "{{ route_map.split(" ")[1] }}", + } + } + ] + }, + }, + { + "name": "address_family.default_metric", + "getval": re.compile( + r"""\s+default-metric(?P<default_metric>\s\d+) + *$""", + re.VERBOSE, + ), + "setval": "default-metric {{ default_metric }}", + "compval": "default_metric", + "result": { + "address_family": [ + {"default_metric": "{{ default_metric| int}}"} + ] + }, + }, + { + "name": "address_family.discard_route", + "getval": re.compile( + r"""\s+(?P<discard_route>discard-route)* + \s*(?P<external>external\s\d+)* + \s*(?P<internal>internal\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_discard_route, + "compval": "discard_route", + "result": { + "address_family": [ + { + "discard_route": { + "set": "{{ True if discard_route is defined and external is undefined and internal is undefined }}", + "external": "{{ external.split(" ")[1]|int }}", + "internal": "{{ internal.split(" ")[1]|int }}", + } + } + ] + }, + }, + { + "name": "address_family.distance.admin_distance", + "getval": re.compile( + r"""\s+distance + \s(?P<admin_dist>\S+)* + \s*(?P<source>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<wildcard>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<acl>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_admin_distance, + "compval": "admin_distance", + "result": { + "address_family": [ + { + "distance": { + "admin_distance": { + "distance": "{{ admin_dist }}", + "address": "{{ source }}", + "wildcard_bits": "{{ wildcard }}", + "acl": "{{ acl }}", + } + } + } + ] + }, + }, + { + "name": "address_family.distance.ospf", + "getval": re.compile( + r"""\s+distance + \sospf* + \s*(?P<intra>intra-area\s\d+)* + \s*(?P<inter>inter-area\s\d+)* + \s*(?P<external>external\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_ospf, + "compval": "ospf", + "result": { + "address_family": [ + { + "distance": { + "ospf": { + "inter_area": "{{ inter.split(" ")[1]|int }}", + "intra_area": "{{ intra.split(" ")[1]|int }}", + "external": "{{ external.split(" ")[1]|int }}", + } + } + } + ] + }, + }, + { + "name": "address_family.distribute_list.acls", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<name>\S+)* + \s*(?P<dir>\S+)* + \s*(?P<int_pro>\S+\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distribute_list_acls, + "compval": "distribute_list.acls", + "result": { + "address_family": [ + { + "distribute_list": { + "acls": [ + { + "name": "{{ name }}", + "direction": "{{ dir }}", + "interface": '{{ int_pro if dir == "in" }}', + "protocol": '{{ int_pro if dir == "out" }}', + } + ] + } + } + ] + }, + }, + { + "name": "address_family.distribute_list.prefix", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<prefix>prefix\s\S+)* + \s*(?P<gateway>gateway\s\S+)* + \s*(?P<dir>\S+)* + \s*(?P<int_pro>\S+\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distribute_list_prefix, + "compval": "distribute_list.prefix", + "result": { + "address_family": [ + { + "distribute_list": { + "prefix": { + "name": "{{ prefix.split(" ")[1] }}", + "gateway_name": "{{ gateway.split(" + ")[1] if prefix is defined }}", + "direction": "{{ dir if gateway is undefined }}", + "interface": '{{ int_pro if dir == "in" }}', + "protocol": '{{ int_pro if dir == "out" }}', + } + } + } + ] + }, + }, + { + "name": "address_family.distribute_list.route_map", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<route_map>route-map\s\S+)* + \s*(?P<dir>\S+) + *$""", + re.VERBOSE, + ), + "setval": "distribute-list route-map {{ distribute_list.route_map.name }} in", + "compval": "distribute_list.route_map", + "result": { + "address_family": [ + { + "distribute_list": { + "route_map": { + "name": "{{ route_map.split(" ")[1] }}" + } + } + } + ] + }, + }, + { + "name": "address_family.domain_id", + "getval": re.compile( + r"""\s+domain-id + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<secondary>secondary)* + \s*(?P<null>null) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_domain_id, + "compval": "domain_id", + "result": { + "address_family": [ + { + "domain_id": { + "ip_address": { + "address": "{{ address }}", + "secondary": "{{ True if secondary is defined }}", + }, + "null": "{{ True if null is defined }}", + } + } + ] + }, + }, + { + "name": "address_family.domain_tag", + "getval": re.compile( + r"""\s+domain-tag + \s(?P<tag>\d+) + *$""", + re.VERBOSE, + ), + "setval": "domain-tag {{ domain_tag }}", + "compval": "domain_tag", + "result": {"address_family": [{"domain_tag": "{{ tag|int }}"}]}, + }, + { + "name": "address_family.graceful_restart", + "getval": re.compile( + r"""\s+graceful-restart* + \s*(?P<enable>helper)* + \s*(?P<disable>disable)* + \s*(?P<lsa>strict-lsa-checking) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_address_family_graceful_restart, + "compval": "graceful_restart", + "result": { + "address_family": [ + { + "event_log": { + "enable": "{{ True if is enable defined }}", + "disable": "{{ True if disable is defined }}", + "strict_lsa_checking": "{{ True if lsa is defined }}", + } + } + ] + }, + }, + { + "name": "address_family.event_log", + "getval": re.compile( + r"""\s+(?P<event_log>event-log)* + \s*(?P<one_shot>one-shot)* + \s*(?P<pause>pause)* + \s*(?P<size>size\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_event_log, + "compval": "event_log", + "result": { + "address_family": [ + { + "event_log": { + "enable": "{{ True if event_log is defined and one_shot is undefined and pause is undefined and size is undefined }}", + "one_shot": "{{ True if one_shot is defined }}", + "pause": "{{ True if pause is defined }}", + "size": "{{ size.split(" ")[1]|int }}", + } + } + ] + }, + }, + { + "name": "address_family.help", + "getval": re.compile( + r"""\s+(?P<help>help) + *$""", + re.VERBOSE, + ), + "setval": "help", + "compval": "help", + "result": { + "address_family": [{"help": "{{ True if help is defined }}"}] + }, + }, + { + "name": "address_family.interface_id", + "getval": re.compile( + r"""\s+(?P<interface_id>interface-id\ssnmp-if-index) + *$""", + re.VERBOSE, + ), + "setval": "interface-id snmp-if-index", + "compval": "interface_id", + "result": { + "address_family": [ + {"interface_id": "{{ True if interface_id is defined }}"} + ] + }, + }, + { + "name": "address_family.limit", + "getval": re.compile( + r"""\s+limit\sretransmissions + \s((?P<dc_num>dc\s\d+)|(?P<dc_disable>dc\sdisable))* + \s*((?P<non_dc_num>non-dc\s\d+)|(?P<non_dc_disable>non-dc\sdisable)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_limit, + "compval": "limit", + "result": { + "address_family": [ + { + "limit": { + "dc": { + "number": "{{ dc_num.split(" ")[1]|int }}", + "disable": "{{ True if dc_disable is defined }}", + }, + "non_dc": { + "number": "{{ non_dc_num.split(" ")[1]|int }}", + "disable": "{{ True if dc_disable is defined }}", + }, + } + } + ] + }, + }, + { + "name": "address_family.local_rib_criteria", + "getval": re.compile( + r"""\s+(?P<local>local-rib-criteria)* + \s*(?P<forward>forwarding-address)* + \s*(?P<inter>inter-area-summary)* + \s*(?P<nssa>nssa-translation) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_vrf_local_rib_criteria, + "compval": "local_rib_criteria", + "result": { + "address_family": [ + { + "local_rib_criteria": { + "enable": "{{ True if local is defined and forward is undefined and inter is undefined and nssa is undefined }}", + "forwarding_address": "{{ True if forward is defined }}", + "inter_area_summary": "{{ True if inter is defined }}", + "nssa_translation": "{{ True if nssa is defined }}", + } + } + ] + }, + }, + { + "name": "address_family.log_adjacency_changes", + "getval": re.compile( + r"""\s+(?P<log>log-adjacency-changes)* + \s*(?P<detail>detail) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_log_adjacency_changes, + "compval": "log_adjacency_changes", + "result": { + "address_family": [ + { + "log_adjacency_changes": { + "set": "{{ True if log is defined and detail is undefined }}", + "detail": "{{ True if detail is defined }}", + } + } + ] + }, + }, + { + "name": "address_family.manet", + "getval": re.compile( + r"""\s+manet* + \s*(?P<cache>cache)* + \s*(?P<acknowledgement>acknowledgement\s\d+)* + \s*(?P<update>update\s\d+)* + \s*(?P<hello>hello)* + \s*(?P<unicast>unicast)* + \s*(?P<multicast>multicast)* + \s*(?P<peering>peering\sselective)* + \s*(?P<disable>disable)* + \s*(?P<per_interface>per-interface)* + \s*(?P<redundancy>redundancy\s\d+)* + \s*(?P<willingness>willingness\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_lsa, + "compval": "manet", + "result": { + "address_family": [ + { + "manet": { + "cache": { + "acknowledgement": "{{ acknowledgement.split(" + ")[1] }}", + "update": "{{ update.split(" ")[1] }}", + }, + "hello": { + "unicast": "{{ True if unicast is defined }}", + "multicast": "{{ True if multicast is defined }}", + }, + "peering": { + "set": "{{ True if peering is defined }}", + "disable": "{{ True if disable is defined }}", + "per_interface": "{{ True if per_interface is defined }}", + "redundancy": "{{ redundancy.split(" ")[1] }}", + }, + "willingness": "{{ willingness.split(" ")[1] }}", + } + } + ] + }, + }, + { + "name": "address_family.max_lsa", + "getval": re.compile( + r"""\s+max-lsa + \s(?P<number>\d+)* + \s*(?P<threshold>\d+)* + \s*(?P<ignore_count>ignore-count\s\d+)* + \s*(?P<ignore_time>ignore-time\s\d+)* + \s*(?P<reset_time>reset-time\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_lsa, + "compval": "max_lsa", + "result": { + "address_family": [ + { + "max_lsa": { + "number": "{{ number }}", + "threshold_value": "{{ threshold }}", + "ignore_count": "{{ ignore_count.split(" ")[1] }}", + "ignore_time": "{{ ignore_time.split(" ")[1] }}", + "reset_time": "{{ reset_time.split(" ")[1] }}", + "warning_only": "{{ True if warning is defined }}", + } + } + ] + }, + }, + { + "name": "address_family.max_metric", + "getval": re.compile( + r"""\s+max-metric* + \s*(?P<router_lsa>router-lsa)* + \s*(?P<include_stub>include-stub)* + \s*(?P<external_lsa>external-lsa\s\d+)* + \s*(?P<startup_time>on-startup\s\d+)* + \s*(?P<startup_wait>on-startup\s\S+)* + \s*(?P<summary_lsa>summary-lsa\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_metric, + "compval": "max_metric", + "result": { + "address_family": [ + { + "max_metric": { + "router_lsa": "{{ True if router_lsa is defined }}", + "external_lsa": "{{ external_lsa.split(" ")[1] }}", + "include_stub": "{{ ignore_count.split(" ")[1] }}", + "on_startup": { + "time": "{{ startup_time.split(" ")[1] }}", + "wait_for_bgp": "{{ True if startup_wait is defined }}", + }, + "summary_lsa": "{{ summary_lsa.split(" ")[1] }}", + } + } + ] + }, + }, + { + "name": "address_family.maximum_paths", + "getval": re.compile( + r"""\s+maximum-paths* + \s+(?P<paths>\d+) + *$""", + re.VERBOSE, + ), + "setval": "maximum-paths {{ maximum_paths }}", + "compval": "maximum_paths", + "result": {"address_family": [{"maximum_paths": "{{ paths }}"}]}, + }, + { + "name": "address_family.passive_interface", + "getval": re.compile( + r"""\s+passive-interface + \s(?P<interface>\S+\s\S+) + *$""", + re.VERBOSE, + ), + "setval": "passive-interface {{ passive_interface }}", + "compval": "passive_interface", + "result": { + "address_family": [{"passive_interface": "{{ interface }}"}] + }, + }, + { + "name": "address_family.prefix_suppression", + "getval": re.compile( + r"""\s+(?P<prefix_sup>prefix-suppression) + *$""", + re.VERBOSE, + ), + "setval": "prefix-suppression", + "compval": "prefix_suppression", + "result": { + "address_family": [ + { + "prefix_suppression": "{{ True if prefix_sup is defined }}" + } + ] + }, + }, + { + "name": "address_family.queue_depth.hello", + "getval": re.compile( + r"""\s+queue-depth + \shello* + \s*(?P<max_packets>\d+)* + \s*(?P<unlimited>unlimited) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_queue_depth_hello, + "compval": "hello", + "result": { + "address_family": [ + { + "queue_depth": { + "hello": { + "max_packets": "{{ max_packets }}", + "unlimited": "{{ True if unlimited is defined }}", + } + } + } + ] + }, + }, + { + "name": "address_family.queue_depth.update", + "getval": re.compile( + r"""\s+queue-depth + \supdate* + \s*(?P<max_packets>\d+)* + \s*(?P<unlimited>unlimited) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_queue_depth_update, + "compval": "update", + "result": { + "address_family": [ + { + "queue_depth": { + "update": { + "max_packets": "{{ max_packets }}", + "unlimited": "{{ True if unlimited is defined }}", + } + } + } + ] + }, + }, + { + "name": "address_family.router_id", + "getval": re.compile( + r"""\s+router-id + \s(?P<id>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + *$""", + re.VERBOSE, + ), + "setval": "router-id {{ router_id }}", + "compval": "router_id", + "result": {"address_family": [{"router_id": "{{ id }}"}]}, + }, + { + "name": "address_family.summary_prefix", + "getval": re.compile( + r"""\s+summary-address + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<mask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<not_adv>not-advertise)* + \s*(?P<nssa>nssa-only)* + \s*(?P<tag>tag\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_summary_prefix, + "compval": "summary_prefix", + "result": { + "address_family": [ + { + "summary_prefix": { + "address": "{{ address }}", + "mask": "{{ mask }}", + "not_advertise": "{{ True if not_adv is defined }}", + "nssa_only": "{{ True if nssa is defined }}", + "tag": "{{ tag.split(" ")[1] }}", + } + } + ] + }, + }, + { + "name": "address_family.shutdown", + "getval": re.compile( + r"""\s+(?P<shutdown>shutdown) + *$""", + re.VERBOSE, + ), + "setval": "shutdown", + "compval": "shutdown", + "result": { + "address_family": [ + {"shutdown": "{{ True if shutdown is defined }}"} + ] + }, + }, + { + "name": "address_family.timers.lsa", + "getval": re.compile( + r"""\s+timers + \slsa + \sarrival + \s(?P<lsa>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers lsa arrival {{ timers.lsa }}", + "compval": "lsa", + "result": {"address_family": [{"timers": {"lsa": "{{ lsa }}"}}]}, + }, + { + "name": "address_family.timers.pacing", + "getval": re.compile( + r"""\s+timers + \spacing + \s(?P<flood>flood\s\d+)* + \s*(?P<lsa_group>lsa-group\s\d+)* + \s*(?P<retransmission>retransmission\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_timers_pacing, + "compval": "pacing", + "result": { + "address_family": [ + { + "timers": { + "pacing": { + "flood": "{{ flood.split(" ")[1] }}", + "lsa_group": "{{ lsa_group.split(" ")[1] }}", + "retransmission": "{{ retransmission.split(" + ")[1] }}", + } + } + } + ] + }, + }, + { + "name": "address_family.timers.throttle.lsa", + "getval": re.compile( + r"""\s+timers + \sthrottle + \s*(?P<lsa>lsa)* + \s*(?P<first_delay>\d+)* + \s*(?P<min_delay>\d+)* + \s*(?P<max_delay>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers throttle lsa {{ throttle.lsa.first_delay }} {{ throttle.lsa.min_delay }} {{ throttle.lsa.max_delay }}", + "compval": "throttle.lsa", + "result": { + "address_family": [ + { + "timers": { + "throttle": { + "lsa": { + "first_delay": "{{ first_delay }}", + "min_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + } + } + } + } + ] + }, + }, + { + "name": "address_family.timers.throttle.spf", + "getval": re.compile( + r"""\s+timers + \sthrottle + \s*(?P<spf>spf)* + \s*(?P<first_delay>\d+)* + \s*(?P<min_delay>\d+)* + \s*(?P<max_delay>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers throttle spf {{ throttle.spf.receive_delay }} {{ throttle.spf.between_delay }} {{ throttle.spf.max_delay }}", + "compval": "throttle.spf", + "result": { + "address_family": [ + { + "timers": { + "throttle": { + "spf": { + "receive_delay": "{{ first_delay }}", + "between_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + } + } + } + } + ] + }, + }, + ] diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/utils.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/utils.py new file mode 100644 index 00000000..61da2db6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/utils.py @@ -0,0 +1,385 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# utils + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import socket +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + is_masklen, + to_netmask, +) + + +def remove_command_from_config_list(interface, cmd, commands): + # To delete the passed config + if interface not in commands: + commands.insert(0, interface) + commands.append("no %s" % cmd) + return commands + + +def add_command_to_config_list(interface, cmd, commands): + # To set the passed config + if interface not in commands: + commands.insert(0, interface) + commands.append(cmd) + + +def reverify_diff_py35(want, have): + """ Function to re-verify the set diff for py35 as it doesn't maintains dict order which results + into unexpected set diff + :param config: want and have set config + :returns: True/False post checking if there's any actual diff b/w want and have sets + """ + if not have: + return True + for each_want in want: + diff = True + for each_have in have: + if each_have == sorted(each_want) or sorted(each_have) == sorted( + each_want + ): + diff = False + if diff: + return True + return False + + +def check_n_return_valid_ipv6_addr(module, input_list, filtered_ipv6_list): + # To verify the valid ipv6 address + try: + for each in input_list: + if "::" in each: + if "/" in each: + each = each.split("/")[0] + if socket.inet_pton(socket.AF_INET6, each): + filtered_ipv6_list.append(each) + return filtered_ipv6_list + except socket.error: + module.fail_json(msg="Incorrect IPV6 address!") + + +def new_dict_to_set(input_dict, temp_list, test_set, count=0): + # recursive function to convert input dict to set for comparision + test_dict = dict() + if isinstance(input_dict, dict): + input_dict_len = len(input_dict) + for k, v in sorted(iteritems(input_dict)): + count += 1 + if isinstance(v, list): + temp_list.append(k) + for each in v: + if isinstance(each, dict): + if [True for i in each.values() if type(i) == list]: + new_dict_to_set(each, temp_list, test_set, count) + else: + new_dict_to_set(each, temp_list, test_set, 0) + else: + if v is not None: + test_dict.update({k: v}) + try: + if ( + tuple(iteritems(test_dict)) not in test_set + and count == input_dict_len + ): + test_set.add(tuple(iteritems(test_dict))) + count = 0 + except TypeError: + temp_dict = {} + + def expand_dict(dict_to_expand): + temp = dict() + for k, v in iteritems(dict_to_expand): + if isinstance(v, dict): + expand_dict(v) + else: + if v is not None: + temp.update({k: v}) + temp_dict.update(tuple(iteritems(temp))) + + new_dict = {k: v} + expand_dict(new_dict) + if tuple(iteritems(temp_dict)) not in test_set: + test_set.add(tuple(iteritems(temp_dict))) + + +def dict_to_set(sample_dict): + # Generate a set with passed dictionary for comparison + test_dict = dict() + if isinstance(sample_dict, dict): + for k, v in iteritems(sample_dict): + if v is not None: + if isinstance(v, list): + if isinstance(v[0], dict): + li = [] + for each in v: + for key, value in iteritems(each): + if isinstance(value, list): + each[key] = tuple(value) + li.append(tuple(iteritems(each))) + v = tuple(li) + else: + v = tuple(v) + elif isinstance(v, dict): + li = [] + for key, value in iteritems(v): + if isinstance(value, list): + v[key] = tuple(value) + li.extend(tuple(iteritems(v))) + v = tuple(li) + test_dict.update({k: v}) + return_set = set(tuple(iteritems(test_dict))) + else: + return_set = set(sample_dict) + return return_set + + +def filter_dict_having_none_value(want, have): + # Generate dict with have dict value which is None in want dict + test_dict = dict() + name = want.get("name") + if name: + test_dict["name"] = name + diff_ip = False + for k, v in iteritems(want): + if isinstance(v, dict): + for key, value in iteritems(v): + test_key_dict = dict() + if value is None: + if have.get(k): + dict_val = have.get(k).get(key) + test_key_dict.update({key: dict_val}) + elif ( + k == "ipv6" + and value.lower() != have.get(k)[0].get(key).lower() + ): + # as multiple IPV6 address can be configured on same + # interface, for replace state in place update will + # actually create new entry, which isn't as expected + # for replace state, so in case of IPV6 address + # every time 1st delete the existing IPV6 config and + # then apply the new change + dict_val = have.get(k)[0].get(key) + test_key_dict.update({key: dict_val}) + if test_key_dict: + test_dict.update({k: test_key_dict}) + if isinstance(v, list): + for key, value in iteritems(v[0]): + test_key_dict = dict() + if value is None: + if have.get(k) and key in have.get(k): + dict_val = have.get(k)[0].get(key) + test_key_dict.update({key: dict_val}) + elif have.get(k): + if ( + k == "ipv6" + and value.lower() != have.get(k)[0].get(key).lower() + ): + dict_val = have.get(k)[0].get(key) + test_key_dict.update({key: dict_val}) + if test_key_dict: + test_dict.update({k: test_key_dict}) + # below conditions checks are added to check if + # secondary IP is configured, if yes then delete + # the already configured IP if want and have IP + # is different else if it's same no need to delete + for each in v: + if each.get("secondary"): + want_ip = each.get("address").split("/") + have_ip = have.get("ipv4") + if ( + len(want_ip) > 1 + and have_ip + and have_ip[0].get("secondary") + ): + have_ip = have_ip[0]["address"].split(" ")[0] + if have_ip != want_ip[0]: + diff_ip = True + if each.get("secondary") and diff_ip is True: + test_key_dict.update({"secondary": True}) + test_dict.update({"ipv4": test_key_dict}) + if v is None: + val = have.get(k) + test_dict.update({k: val}) + return test_dict + + +def remove_duplicate_interface(commands): + # Remove duplicate interface from commands + set_cmd = [] + for each in commands: + if "interface" in each: + if each not in set_cmd: + set_cmd.append(each) + else: + set_cmd.append(each) + + return set_cmd + + +def validate_ipv4(value, module): + if value: + address = value.split("/") + if len(address) != 2: + module.fail_json( + msg="address format is <ipv4 address>/<mask>, got invalid format {0}".format( + value + ) + ) + + if not is_masklen(address[1]): + module.fail_json( + msg="invalid value for mask: {0}, mask should be in range 0-32".format( + address[1] + ) + ) + + +def validate_ipv6(value, module): + if value: + address = value.split("/") + if len(address) != 2: + module.fail_json( + msg="address format is <ipv6 address>/<mask>, got invalid format {0}".format( + value + ) + ) + else: + if not 0 <= int(address[1]) <= 128: + module.fail_json( + msg="invalid value for mask: {0}, mask should be in range 0-128".format( + address[1] + ) + ) + + +def validate_n_expand_ipv4(module, want): + # Check if input IPV4 is valid IP and expand IPV4 with its subnet mask + ip_addr_want = want.get("address") + if len(ip_addr_want.split(" ")) > 1: + return ip_addr_want + validate_ipv4(ip_addr_want, module) + ip = ip_addr_want.split("/") + if len(ip) == 2: + ip_addr_want = "{0} {1}".format(ip[0], to_netmask(ip[1])) + + return ip_addr_want + + +def netmask_to_cidr(netmask): + # convert netmask to cidr and returns the cidr notation + return str(sum([bin(int(x)).count("1") for x in netmask.split(".")])) + + +def is_valid_ip(ip_str): + valid = True + try: + if "::" in ip_str: + socket.inet_pton(socket.AF_INET6, ip_str) # for IPv6 + else: + socket.inet_pton(socket.AF_INET, ip_str) # for IPv4 + except socket.error: + valid = False + return valid + + +def normalize_interface(name): + """Return the normalized interface name + """ + if not name: + return + + def _get_number(name): + digits = "" + for char in name: + if char.isdigit() or char in "/.": + digits += char + return digits + + if name.lower().startswith("gi"): + if_type = "GigabitEthernet" + elif name.lower().startswith("te"): + if_type = "TenGigabitEthernet" + elif name.lower().startswith("fa"): + if_type = "FastEthernet" + elif name.lower().startswith("fo"): + if_type = "FortyGigabitEthernet" + elif name.lower().startswith("long"): + if_type = "LongReachEthernet" + elif name.lower().startswith("et"): + if_type = "Ethernet" + elif name.lower().startswith("vl"): + if_type = "Vlan" + elif name.lower().startswith("lo"): + if_type = "loopback" + elif name.lower().startswith("po"): + if_type = "Port-channel" + elif name.lower().startswith("nv"): + if_type = "nve" + elif name.lower().startswith("twe"): + if_type = "TwentyFiveGigE" + elif name.lower().startswith("hu"): + if_type = "HundredGigE" + elif name.lower().startswith("virtual-te"): + if_type = "Virtual-Template" + elif name.lower().startswith("tu"): + if_type = "Tunnel" + else: + if_type = None + + number_list = name.split(" ") + if len(number_list) == 2: + number = number_list[-1].strip() + else: + number = _get_number(name) + + if if_type: + proper_interface = if_type + number + else: + proper_interface = name + + return proper_interface + + +def get_interface_type(interface): + """Gets the type of interface + """ + + if interface.upper().startswith("GI"): + return "GigabitEthernet" + elif interface.upper().startswith("TE"): + return "TenGigabitEthernet" + elif interface.upper().startswith("FA"): + return "FastEthernet" + elif interface.upper().startswith("FO"): + return "FortyGigabitEthernet" + elif interface.upper().startswith("LON"): + return "LongReachEthernet" + elif interface.upper().startswith("ET"): + return "Ethernet" + elif interface.upper().startswith("VL"): + return "Vlan" + elif interface.upper().startswith("LO"): + return "loopback" + elif interface.upper().startswith("PO"): + return "Port-channel" + elif interface.upper().startswith("NV"): + return "nve" + elif interface.upper().startswith("TWE"): + return "TwentyFiveGigE" + elif interface.upper().startswith("HU"): + return "HundredGigE" + elif interface.upper().startswith("VIRTUAL-TE"): + return "Virtual-Template" + elif interface.upper().startswith("TU"): + return "Tunnel" + else: + return "unknown" diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_acl_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_acl_interfaces.py new file mode 100644 index 00000000..75094a92 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_acl_interfaces.py @@ -0,0 +1,598 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +""" +The module file for ios_acl_interfaces +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_acl_interfaces +short_description: ACL interfaces resource module +description: This module configures and manages the access-control (ACL) attributes + of interfaces on IOS platforms. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL +options: + config: + description: A dictionary of ACL interfaces options + type: list + elements: dict + suboptions: + name: + description: Full name of the interface excluding any logical unit number, + i.e. GigabitEthernet0/1. + type: str + required: true + access_groups: + description: Specify access-group for IP access list (standard or extended). + type: list + elements: dict + suboptions: + afi: + description: Specifies the AFI for the ACLs to be configured on this interface. + type: str + required: true + choices: + - ipv4 + - ipv6 + acls: + description: Specifies the ACLs for the provided AFI. + type: list + elements: dict + suboptions: + name: + description: Specifies the name of the IPv4/IPv4 ACL for the interface. + type: str + required: true + direction: + description: + - Specifies the direction of packets that the ACL will be applied + on. + - With one direction already assigned, other acl direction cannot + be same. + type: str + required: true + choices: + - in + - out + 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. + type: str + state: + 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged +""" +EXAMPLES = """ +# Using Merged + +# Before state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# interface GigabitEthernet0/2 +# ip access-group 123 out + +- name: Merge module attributes of given access-groups + cisco.ios.ios_acl_interfaces: + config: + - name: GigabitEthernet0/1 + access_groups: + - afi: ipv4 + acls: + - name: 110 + direction: in + - name: 123 + direction: out + - afi: ipv6 + acls: + - name: test_v6 + direction: out + - name: temp_v6 + direction: in + - name: GigabitEthernet0/2 + access_groups: + - afi: ipv4 + acls: + - name: 100 + direction: in + state: merged + +# Commands Fired: +# --------------- +# +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 100 in +# ip access-group 123 out + + +# After state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +# Using Replaced + +# Before state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +- name: Replace module attributes of given access-groups + cisco.ios.ios_acl_interfaces: + config: + - name: GigabitEthernet0/1 + access_groups: + - afi: ipv4 + acls: + - name: 100 + direction: out + - name: 110 + direction: in + state: replaced + +# Commands Fired: +# --------------- +# +# interface GigabitEthernet0/1 +# no ip access-group 123 out +# no ipv6 traffic-filter temp_v6 in +# no ipv6 traffic-filter test_v6 out +# ip access-group 100 out + +# After state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 100 out +# ip access-group 110 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +# Using Overridden + +# Before state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +- name: Overridden module attributes of given access-groups + cisco.ios.ios_acl_interfaces: + config: + - name: GigabitEthernet0/1 + access_groups: + - afi: ipv4 + acls: + - name: 100 + direction: out + - name: 110 + direction: in + state: overridden + +# Commands Fired: +# --------------- +# +# interface GigabitEthernet0/1 +# no ip access-group 123 out +# no ipv6 traffic-filter test_v6 out +# no ipv6 traffic-filter temp_v6 in +# ip access-group 100 out +# interface GigabitEthernet0/2 +# no ip access-group 110 in +# no ip access-group 123 out + +# After state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 100 out +# ip access-group 110 in +# interface GigabitEthernet0/2 + +# Using Deleted + +# Before state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +- name: Delete module attributes of given Interface + cisco.ios.ios_acl_interfaces: + config: + - name: GigabitEthernet0/1 + state: deleted + +# Commands Fired: +# --------------- +# +# interface GigabitEthernet0/1 +# no ip access-group 110 in +# no ip access-group 123 out +# no ipv6 traffic-filter test_v6 out +# no ipv6 traffic-filter temp_v6 in + +# After state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +# Using DELETED without any config passed +#"(NOTE: This will delete all of configured resource module attributes from each configured interface)" + +# Before state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +- name: Delete module attributes of given access-groups from ALL Interfaces + cisco.ios.ios_acl_interfaces: + config: + state: deleted + +# Commands Fired: +# --------------- +# +# interface GigabitEthernet0/1 +# no ip access-group 110 in +# no ip access-group 123 out +# no ipv6 traffic-filter test_v6 out +# no ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# no ip access-group 110 out +# no ip access-group 123 out + +# After state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# interface GigabitEthernet0/2 + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +- name: Gather listed acl interfaces with provided configurations + cisco.ios.ios_acl_interfaces: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "name": "Loopback888" +# }, +# { +# "name": "GigabitEthernet0/0" +# }, +# { +# "access_groups": [ +# { +# "acls": [ +# { +# "direction": "in", +# "name": "110" +# }, +# { +# "direction": "out", +# "name": "123" +# } +# ], +# "afi": "ipv4" +# }, +# { +# "acls": [ +# { +# "direction": "in", +# "name": "temp_v6" +# }, +# { +# "direction": "out", +# "name": "test_v6" +# } +# ], +# "afi": "ipv6" +# } +# ], +# "name": "GigabitEthernet0/1" +# }, +# { +# "access_groups": [ +# { +# "acls": [ +# { +# "direction": "in", +# "name": "100" +# }, +# { +# "direction": "out", +# "name": "123" +# } +# ], +# "afi": "ipv4" +# } +# ], +# "name": "GigabitEthernet0/2" +# } +# ] + +# After state: +# ------------ +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_acl_interfaces: + config: + - name: GigabitEthernet0/1 + access_groups: + - afi: ipv4 + acls: + - name: 110 + direction: in + - name: 123 + direction: out + - afi: ipv6 + acls: + - name: test_v6 + direction: out + - name: temp_v6 + direction: in + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "interface GigabitEthernet0/1", +# "ip access-group 110 in", +# "ip access-group 123 out", +# "ipv6 traffic-filter temp_v6 in", +# "ipv6 traffic-filter test_v6 out" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter temp_v6 in +# ipv6 traffic-filter test_v6 out + +- name: Parse the commands for provided configuration + cisco.ios.ios_acl_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "access_groups": [ +# { +# "acls": [ +# { +# "direction": "in", +# "name": "110" +# } +# ], +# "afi": "ipv4" +# }, +# { +# "acls": [ +# { +# "direction": "in", +# "name": "temp_v6" +# } +# ], +# "afi": "ipv6" +# } +# ], +# "name": "GigabitEthernet0/1" +# } +# ] +""" +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: ['interface GigabitEthernet0/1', 'ip access-group 110 in', 'ipv6 traffic-filter test_v6 out'] +""" +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.acl_interfaces.acl_interfaces import ( + Acl_InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.acl_interfaces.acl_interfaces import ( + Acl_Interfaces, +) + + +def main(): + """ + Main entry point for module execution + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + module = AnsibleModule( + argument_spec=Acl_InterfacesArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Acl_Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_acls.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_acls.py new file mode 100644 index 00000000..1f1b2831 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_acls.py @@ -0,0 +1,1415 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +""" +The module file for ios_acls +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_acls +short_description: ACLs resource module +description: This module configures and manages the named or numbered ACLs on IOS + platforms. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL +options: + config: + description: A dictionary of ACL options. + type: list + elements: dict + suboptions: + afi: + description: + - The Address Family Indicator (AFI) for the Access Control Lists (ACL). + required: true + type: str + choices: + - ipv4 + - ipv6 + acls: + description: + - A list of Access Control Lists (ACL). + type: list + elements: dict + suboptions: + name: + description: The name or the number of the ACL. + required: true + type: str + acl_type: + description: + - ACL type + - Note, it's mandatory and required for Named ACL, but for Numbered ACL + it's not mandatory. + type: str + choices: + - extended + - standard + aces: + description: The entries within the ACL. + elements: dict + type: list + suboptions: + grant: + description: Specify the action. + type: str + choices: + - permit + - deny + sequence: + description: + - Sequence Number for the Access Control Entry(ACE). + - Refer to vendor documentation for valid values. + type: int + evaluate: + description: Evaluate an access list + type: str + protocol: + description: + - Specify the protocol to match. + - Refer to vendor documentation for valid values. + type: str + protocol_options: + description: protocol type. + type: dict + suboptions: + protocol_number: + description: An IP protocol number + type: int + ahp: + description: Authentication Header Protocol. + type: bool + eigrp: + description: Cisco's EIGRP routing protocol. + type: bool + esp: + description: Encapsulation Security Payload. + type: bool + gre: + description: Cisco's GRE tunneling. + type: bool + hbh: + description: Hop by Hop options header. Valid for IPV6 + type: bool + icmp: + description: Internet Control Message Protocol. + type: dict + suboptions: + administratively_prohibited: + description: Administratively prohibited + type: bool + alternate_address: + description: Alternate address + type: bool + conversion_error: + description: Datagram conversion + type: bool + dod_host_prohibited: + description: Host prohibited + type: bool + dod_net_prohibited: + description: Net prohibited + type: bool + echo: + description: Echo (ping) + type: bool + echo_reply: + description: Echo reply + type: bool + general_parameter_problem: + description: Parameter problem + type: bool + host_isolated: + description: Host isolated + type: bool + host_precedence_unreachable: + description: Host unreachable for precedence + type: bool + host_redirect: + description: Host redirect + type: bool + host_tos_redirect: + description: Host redirect for TOS + type: bool + host_tos_unreachable: + description: Host unreachable for TOS + type: bool + host_unknown: + description: Host unknown + type: bool + host_unreachable: + description: Host unreachable + type: bool + information_reply: + description: Information replies + type: bool + information_request: + description: Information requests + type: bool + mask_reply: + description: Mask replies + type: bool + mask_request: + description: mask_request + type: bool + mobile_redirect: + description: Mobile host redirect + type: bool + net_redirect: + description: Network redirect + type: bool + net_tos_redirect: + description: Net redirect for TOS + type: bool + net_tos_unreachable: + description: Network unreachable for TOS + type: bool + net_unreachable: + description: Net unreachable + type: bool + network_unknown: + description: Network unknown + type: bool + no_room_for_option: + description: Parameter required but no room + type: bool + option_missing: + description: Parameter required but not present + type: bool + packet_too_big: + description: Fragmentation needed and DF set + type: bool + parameter_problem: + description: All parameter problems + type: bool + port_unreachable: + description: Port unreachable + type: bool + precedence_unreachable: + description: Precedence cutoff + type: bool + protocol_unreachable: + description: Protocol unreachable + type: bool + reassembly_timeout: + description: Reassembly timeout + type: bool + redirect: + description: All redirects + type: bool + router_advertisement: + description: Router discovery advertisements + type: bool + router_solicitation: + description: Router discovery solicitations + type: bool + source_quench: + description: Source quenches + type: bool + source_route_failed: + description: Source route failed + type: bool + time_exceeded: + description: All time exceededs + type: bool + timestamp_reply: + description: Timestamp replies + type: bool + timestamp_request: + description: Timestamp requests + type: bool + traceroute: + description: Traceroute + type: bool + ttl_exceeded: + description: TTL exceeded + type: bool + unreachable: + description: All unreachables + type: bool + igmp: + description: Internet Gateway Message Protocol. + type: dict + suboptions: + dvmrp: + description: Distance Vector Multicast Routing Protocol(2) + type: bool + host_query: + description: IGMP Membership Query(0) + type: bool + mtrace_resp: + description: Multicast Traceroute Response(7) + type: bool + mtrace_route: + description: Multicast Traceroute(8) + type: bool + pim: + description: Protocol Independent Multicast(3) + type: bool + trace: + description: Multicast trace(4) + type: bool + v1host_report: + description: IGMPv1 Membership Report(1) + type: bool + v2host_report: + description: IGMPv2 Membership Report(5) + type: bool + v2leave_group: + description: IGMPv2 Leave Group(6) + type: bool + v3host_report: + description: IGMPv3 Membership Report(9) + type: bool + ip: + description: Any Internet Protocol. + type: bool + ipv6: + description: Any IPv6. + type: bool + ipinip: + description: IP in IP tunneling. + type: bool + nos: + description: KA9Q NOS compatible IP over IP tunneling. + type: bool + ospf: + description: OSPF routing protocol. + type: bool + pcp: + description: Payload Compression Protocol. + type: bool + pim: + description: Protocol Independent Multicast. + type: bool + sctp: + description: Stream Control Transmission Protocol. + type: bool + udp: + description: User Datagram Protocol. + type: bool + tcp: + description: Match TCP packet flags + type: dict + suboptions: + ack: + description: Match on the ACK bit + type: bool + established: + description: Match established connections + type: bool + fin: + description: Match on the FIN bit + type: bool + psh: + description: Match on the PSH bit + type: bool + rst: + description: Match on the RST bit + type: bool + syn: + description: Match on the SYN bit + type: bool + urg: + description: Match on the URG bit + type: bool + source: + description: Specify the packet source. + type: dict + suboptions: + address: + description: Source network address. + type: str + wildcard_bits: + description: Destination wildcard bits, valid with IPV4 address. + type: str + any: + description: Match any source address. + type: bool + host: + description: A single source host + type: str + port_protocol: + description: + - Specify the destination port along with protocol. + - Note, Valid with TCP/UDP protocol_options + type: dict + suboptions: + eq: + description: Match only packets on a given port number. + type: str + gt: + description: Match only packets with a greater port number. + type: str + lt: + description: Match only packets with a lower port number. + type: str + neq: + description: Match only packets not on a given port number. + type: str + range: + description: Port group. + type: dict + suboptions: + start: + description: Specify the start of the port range. + type: int + end: + description: Specify the end of the port range. + type: int + destination: + description: Specify the packet destination. + type: dict + suboptions: + address: + description: Host address to match, or any single host address. + type: str + wildcard_bits: + description: Destination wildcard bits, valid with IPV4 address. + type: str + any: + description: Match any source address. + type: bool + host: + description: A single destination host + type: str + port_protocol: + description: + - Specify the destination port along with protocol. + - Note, Valid with TCP/UDP protocol_options + type: dict + suboptions: + eq: + description: Match only packets on a given port number. + type: str + gt: + description: Match only packets with a greater port number. + type: str + lt: + description: Match only packets with a lower port number. + type: str + neq: + description: Match only packets not on a given port number. + type: str + range: + description: Port group. + type: dict + suboptions: + start: + description: Specify the start of the port range. + type: int + end: + description: Specify the end of the port range. + type: int + dscp: + description: Match packets with given dscp value. + type: str + fragments: + description: Check non-initial fragments. + type: str + log: + description: Log matches against this entry. + type: str + log_input: + description: Log matches against this entry, including input interface. + type: str + option: + description: + - Match packets with given IP Options value. + - Valid only for named acls. + type: dict + suboptions: + add_ext: + description: Match packets with Address Extension Option (147). + type: bool + any_options: + description: Match packets with ANY Option. + type: bool + com_security: + description: Match packets with Commercial Security Option (134). + type: bool + dps: + description: Match packets with Dynamic Packet State Option (151). + type: bool + encode: + description: Match packets with Encode Option (15). + type: bool + eool: + description: Match packets with End of Options (0). + type: bool + ext_ip: + description: Match packets with Extended IP Option (145). + type: bool + ext_security: + description: Match packets with Extended Security Option (133). + type: bool + finn: + description: Match packets with Experimental Flow Control Option + (205). + type: bool + imitd: + description: Match packets with IMI Traffic Desriptor Option (144). + type: bool + lsr: + description: Match packets with Loose Source Route Option (131). + type: bool + mtup: + description: Match packets with MTU Probe Option (11). + type: bool + mtur: + description: Match packets with MTU Reply Option (12). + type: bool + no_op: + description: Match packets with No Operation Option (1). + type: bool + nsapa: + description: Match packets with NSAP Addresses Option (150). + type: bool + record_route: + description: Match packets with Record Route Option (7). + type: bool + router_alert: + description: Match packets with Router Alert Option (148). + type: bool + sdb: + description: Match packets with Selective Directed Broadcast Option + (149). + type: bool + security: + description: Match packets with Basic Security Option (130). + type: bool + ssr: + description: Match packets with Strict Source Routing Option (137). + type: bool + stream_id: + description: Match packets with Stream ID Option (136). + type: bool + timestamp: + description: Match packets with Time Stamp Option (68). + type: bool + traceroute: + description: Match packets with Trace Route Option (82). + type: bool + ump: + description: Match packets with Upstream Multicast Packet Option + (152). + type: bool + visa: + description: Match packets with Experimental Access Control Option + (142). + type: bool + zsu: + description: Match packets with Experimental Measurement Option + (10). + type: bool + precedence: + description: Match packets with given precedence value. + type: int + time_range: + description: Specify a time-range. + type: str + tos: + description: + - Match packets with given TOS value. + - Note, DSCP and TOS are mutually exclusive + type: dict + suboptions: + service_value: + description: Type of service value + type: int + max_reliability: + description: Match packets with max reliable TOS (2). + type: bool + max_throughput: + description: Match packets with max throughput TOS (4). + type: bool + min_delay: + description: Match packets with min delay TOS (8). + type: bool + min_monetary_cost: + description: Match packets with min monetary cost TOS (1). + type: bool + normal: + description: Match packets with normal TOS (0). + type: bool + ttl: + description: Match packets with given TTL value. + type: dict + suboptions: + eq: + description: Match only packets on a given TTL number. + type: int + gt: + description: Match only packets with a greater TTL number. + type: int + lt: + description: Match only packets with a lower TTL number. + type: int + neq: + description: Match only packets not on a given TTL number. + type: int + range: + description: Match only packets in the range of TTLs. + type: dict + suboptions: + start: + description: Specify the start of the port range. + type: int + end: + description: Specify the end of the port range. + 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 IOS + device by executing the command B(sh access-list). + - 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 + - rendered + - parsed + 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str +""" +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# vios#sh access-lists +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 echo dscp ef ttl eq 10 + +- name: Merge provided configuration with device configuration + cisco.ios.ios_acls: + config: + - afi: ipv4 + acls: + - name: std_acl + acl_type: standard + aces: + - grant: deny + source: + address: 192.168.1.200 + - grant: deny + source: + address: 192.168.2.0 + wildcard_bits: 0.0.0.255 + - name: 110 + aces: + - sequence: 10 + protocol_options: + icmp: + traceroute: true + - grant: deny + protocol_options: + tcp: + ack: true + source: + host: 198.51.100.0 + destination: + host: 198.51.110.0 + port_protocol: + eq: telnet + - name: test + acl_type: extended + aces: + - grant: deny + protocol_options: + tcp: + fin: true + source: + address: 192.0.2.0 + wildcard_bits: 0.0.0.255 + destination: + address: 192.0.3.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: www + option: + traceroute: true + ttl: + eq: 10 + - name: 123 + aces: + - grant: deny + protocol_options: + tcp: + ack: true + source: + address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + destination: + address: 198.51.101.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + tos: + service_value: 12 + - grant: deny + protocol_options: + tcp: + ack: true + source: + address: 192.0.3.0 + wildcard_bits: 0.0.0.255 + destination: + address: 192.0.4.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: www + dscp: ef + ttl: + lt: 20 + - afi: ipv6 + acls: + - name: R1_TRAFFIC + aces: + - grant: deny + protocol_options: + tcp: + ack: true + source: + any: true + port_protocol: + eq: www + destination: + any: true + port_protocol: + eq: telnet + dscp: af11 + state: merged + +# Commands fired: +# --------------- +# +# - ip access-list standard std_acl +# - deny 192.168.1.200 +# - deny 192.168.2.0 0.0.0.255 +# - ip access-list extended 110 +# - no 10 +# - 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# - deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# - ip access-list extended test +# - deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# - ip access-list extended 123 +# - deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# - deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# - ipv6 access-list R1_TRAFFIC +# - deny tcp any eq www any eq telnet ack dscp af11 + +# After state: +# ------------ +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + + +# Using replaced + +# Before state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + + +- name: Replaces device configuration of listed acls with provided configuration + cisco.ios.ios_acls: + config: + - afi: ipv4 + acls: + - name: 110 + aces: + - grant: deny + protocol_options: + tcp: + syn: true + source: + address: 192.0.2.0 + wildcard_bits: 0.0.0.255 + destination: + address: 192.0.3.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: www + dscp: ef + ttl: + eq: 10 + - name: 150 + aces: + - grant: deny + sequence: 20 + protocol_options: + tcp: + syn: true + source: + address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + destination: + address: 198.51.110.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + dscp: ef + ttl: + eq: 10 + state: replaced + +# Commands fired: +# --------------- +# +# - no ip access-list extended 110 +# - ip access-list extended 110 +# - deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www syn dscp ef ttl eq 10 +# - ip access-list extended 150 +# - 20 deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq telnet syn dscp ef ttl eq 10 + +# After state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www syn dscp ef ttl eq 10 +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list 150 +# 20 deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq telnet syn dscp ef ttl eq 10 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +# Using overridden + +# Before state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +- name: Override device configuration of all acls with provided configuration + cisco.ios.ios_acls: + config: + - afi: ipv4 + acls: + - name: 110 + aces: + - grant: deny + sequence: 20 + protocol_options: + tcp: + ack: true + source: + address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + destination: + address: 198.51.110.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: www + dscp: ef + ttl: + eq: 10 + - name: 150 + aces: + - grant: deny + sequence: 10 + protocol_options: + tcp: + syn: true + source: + address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + destination: + address: 198.51.110.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + dscp: ef + ttl: + eq: 10 + state: overridden + +# Commands fired: +# --------------- +# +# - no ip access-list standard std_acl +# - no ip access-list extended 110 +# - no ip access-list extended 123 +# - no ip access-list extended 150 +# - no ip access-list extended test +# - no ipv6 access-list R1_TRAFFIC +# - ip access-list extended 150 +# - 10 deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq telnet syn dscp ef ttl eq 10 +# - ip access-list extended 110 +# - 20 deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq www ack dscp ef ttl eq 10 + +# After state: +# ------------- +# +# vios#sh access-lists +# Extended IP access list 110 +# 20 deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq www ack dscp ef ttl eq 10 +# Extended IP access list 150 +# 10 deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq telnet syn dscp ef ttl eq 10 + +# Using Deleted + +# Before state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +- name: "Delete ACLs (Note: This won't delete the all configured ACLs)" + cisco.ios.ios_acls: + config: + - afi: ipv4 + acls: + - name: test + acl_type: extended + - name: 110 + - afi: ipv6 + acls: + - name: R1_TRAFFIC + state: deleted + +# Commands fired: +# --------------- +# +# - no ip access-list extended test +# - no ip access-list extended 110 +# - no ipv6 access-list R1_TRAFFIC + +# After state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 + +# Before state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +- name: "Delete ACLs based on AFI (Note: This won't delete the all configured ACLs)" + cisco.ios.ios_acls: + config: + - afi: ipv4 + state: deleted + +# Commands fired: +# --------------- +# +# - no ip access-list standard std_acl +# - no ip access-list extended test +# - no ip access-list extended 110 +# - no ip access-list extended 123 + +# After state: +# ------------- +# +# vios#sh access-lists +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured ACLs)" + +# Before state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +- name: 'Delete ALL of configured ACLs (Note: This WILL delete the all configured + ACLs)' + cisco.ios.ios_acls: + state: deleted + +# Commands fired: +# --------------- +# +# - no ip access-list extended test +# - no ip access-list extended 110 +# - no ip access-list extended 123 +# - no ip access-list extended test +# - no ipv6 access-list R1_TRAFFIC + +# After state: +# ------------- +# +# vios#sh access-lists + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +- name: Gather listed acls with provided configurations + cisco.ios.ios_acls: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "acls": [ +# { +# "aces": [ +# { +# "destination": { +# "address": "192.0.3.0", +# "wildcard_bits": "0.0.0.255" +# }, +# "dscp": "ef", +# "grant": "deny", +# "protocol_options": { +# "icmp": { +# "echo": true +# } +# }, +# "sequence": 10, +# "source": { +# "address": "192.0.2.0", +# "wildcard_bits": "0.0.0.255" +# }, +# "ttl": { +# "eq": 10 +# } +# } +# ], +# "acl_type": "extended", +# "name": "110" +# }, +# { +# "aces": [ +# { +# "destination": { +# "address": "198.51.101.0", +# "port_protocol": { +# "eq": "telnet" +# }, +# "wildcard_bits": "0.0.0.255" +# }, +# "grant": "deny", +# "protocol_options": { +# "tcp": { +# "ack": true +# } +# }, +# "sequence": 10, +# "source": { +# "address": "198.51.100.0", +# "wildcard_bits": "0.0.0.255" +# }, +# "tos": { +# "service_value": 12 +# } +# }, +# { +# "destination": { +# "address": "192.0.4.0", +# "port_protocol": { +# "eq": "www" +# }, +# "wildcard_bits": "0.0.0.255" +# }, +# "dscp": "ef", +# "grant": "deny", +# "protocol_options": { +# "tcp": { +# "ack": true +# } +# }, +# "sequence": 20, +# "source": { +# "address": "192.0.3.0", +# "wildcard_bits": "0.0.0.255" +# }, +# "ttl": { +# "lt": 20 +# } +# } +# ], +# "acl_type": "extended", +# "name": "123" +# }, +# { +# "aces": [ +# { +# "destination": { +# "address": "192.0.3.0", +# "port_protocol": { +# "eq": "www" +# }, +# "wildcard_bits": "0.0.0.255" +# }, +# "grant": "deny", +# "option": { +# "traceroute": true +# }, +# "protocol_options": { +# "tcp": { +# "fin": true +# } +# }, +# "sequence": 10, +# "source": { +# "address": "192.0.2.0", +# "wildcard_bits": "0.0.0.255" +# }, +# "ttl": { +# "eq": 10 +# } +# } +# ], +# "acl_type": "extended", +# "name": "test_acl" +# } +# ], +# "afi": "ipv4" +# }, +# { +# "acls": [ +# { +# "aces": [ +# { +# "destination": { +# "any": true, +# "port_protocol": { +# "eq": "telnet" +# } +# }, +# "dscp": "af11", +# "grant": "deny", +# "protocol_options": { +# "tcp": { +# "ack": true +# } +# }, +# "sequence": 10, +# "source": { +# "any": true, +# "port_protocol": { +# "eq": "www" +# } +# } +# } +# ], +# "name": "R1_TRAFFIC" +# } +# ], +# "afi": "ipv6" +# } +# ] + +# Using Rendered + +- name: Rendered the provided configuration with the exisiting running configuration + cisco.ios.ios_acls: + config: + - afi: ipv4 + acls: + - name: 110 + aces: + - grant: deny + sequence: 10 + protocol_options: + tcp: + syn: true + source: + address: 192.0.2.0 + wildcard_bits: 0.0.0.255 + destination: + address: 192.0.3.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: www + dscp: ef + ttl: + eq: 10 + - name: 150 + aces: + - grant: deny + protocol_options: + tcp: + syn: true + source: + address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + destination: + address: 198.51.110.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + dscp: ef + ttl: + eq: 10 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "ip access-list extended 110", +# "10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www syn dscp ef ttl eq 10", +# "ip access-list extended 150", +# "deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq telnet syn dscp ef ttl eq 10" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# ipv6 access-list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 + +- name: Parse the commands for provided configuration + cisco.ios.ios_acls: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "acls": [ +# { +# "aces": [ +# { +# "destination": { +# "any": true, +# "port_protocol": { +# "eq": "telnet" +# } +# }, +# "dscp": "af11", +# "grant": "deny", +# "protocol_options": { +# "tcp": { +# "ack": true +# } +# }, +# "source": { +# "any": true, +# "port_protocol": { +# "eq": "www" +# } +# } +# } +# ], +# "name": "R1_TRAFFIC" +# } +# ], +# "afi": "ipv6" +# } +# ] +""" +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: ['ip access-list extended 110', 'deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 echo dscp ef ttl eq 10'] +""" +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.acls.acls import ( + AclsArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.acls.acls import ( + Acls, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + module = AnsibleModule( + argument_spec=AclsArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Acls(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_banner.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_banner.py new file mode 100644 index 00000000..4370857b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_banner.py @@ -0,0 +1,188 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_banner +author: Ricardo Carrillo Cruz (@rcarrillocruz) +short_description: Manage multiline banners on Cisco IOS devices +description: +- This will configure both login and motd banners on remote devices running Cisco + IOS. It allows playbooks to add or remote banner text from the active running configuration. +version_added: 1.0.0 +extends_documentation_fragment: +- cisco.ios.ios +notes: +- Tested against IOS 15.6 +options: + banner: + description: + - Specifies which banner should be configured on the remote device. In Ansible + 2.4 and earlier only I(login) and I(motd) were supported. + required: true + choices: + - login + - motd + - exec + - incoming + - slip-ppp + 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 +""" +EXAMPLES = """ +- name: configure the login banner + cisco.ios.ios_banner: + banner: login + text: | + this is my login banner + that contains a multiline + string + state: present + +- name: remove the motd banner + cisco.ios.ios_banner: + banner: motd + state: absent + +- name: Configure banner from file + cisco.ios.ios_banner: + banner: motd + text: "{{ lookup('file', './config_partial/raw_banner.cfg') }}" + state: present +""" +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - banner login + - this is my login banner + - that contains a multiline + - string +""" +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) +from re import search, M + + +def map_obj_to_commands(updates, module): + commands = list() + want, have = updates + state = module.params["state"] + if state == "absent" and "text" in have.keys() and have["text"]: + commands.append("no banner %s" % module.params["banner"]) + elif state == "present": + if want["text"] and (want["text"] != have.get("text")): + banner_cmd = "banner %s" % module.params["banner"] + banner_cmd += " @\n" + banner_cmd += want["text"].strip("\n") + banner_cmd += "\n@" + commands.append(banner_cmd) + return commands + + +def map_config_to_obj(module): + """ + This function gets the banner config without stripping any whitespaces, + and then fetches the required banner from it. + :param module: + :return: banner config dict object. + """ + out = get_config( + module, flags="| begin banner %s" % module.params["banner"] + ) + if out: + regex = "banner " + module.params["banner"] + " ^C\n" + if search("banner " + module.params["banner"], out, M): + output = str((out.split(regex))[1].split("^C\n")[0]) + else: + output = None + else: + output = None + obj = {"banner": module.params["banner"], "state": "absent"} + if output: + obj["text"] = output + obj["state"] = "present" + return obj + + +def map_params_to_obj(module): + text = module.params["text"] + 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=["login", "motd", "exec", "incoming", "slip-ppp"], + ), + text=dict(), + state=dict(default="present", choices=["present", "absent"]), + ) + argument_spec.update(ios_argument_spec) + 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 = map_config_to_obj(module) + commands = map_obj_to_commands((want, have), module) + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_bgp.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_bgp.py new file mode 100644 index 00000000..bc189436 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_bgp.py @@ -0,0 +1,509 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_bgp +author: Nilashish Chakraborty (@NilashishC) +short_description: Configure global BGP protocol settings on Cisco IOS. +description: +- This module provides configuration management of global BGP parameters on devices + running Cisco IOS +version_added: 1.0.0 +notes: +- Tested against Cisco IOS Version 15.6(3)M2 +options: + config: + description: + - Specifies the BGP related configuration. + type: dict + suboptions: + bgp_as: + description: + - Specifies the BGP Autonomous System (AS) number to configure on the device. + type: int + required: true + router_id: + description: + - Configures the BGP routing process router-id value. + type: str + default: + log_neighbor_changes: + description: + - Enable/disable logging neighbor up/down and reset reason. + type: bool + neighbors: + description: + - Specifies BGP neighbor related configurations. + type: list + elements: dict + suboptions: + neighbor: + description: + - Neighbor router address. + required: true + type: str + remote_as: + description: + - Remote AS of the BGP neighbor to configure. + type: int + required: true + update_source: + description: + - Source of the routing updates. + type: str + password: + description: + - Password to authenticate the BGP peer connection. + type: str + enabled: + description: + - Administratively shutdown or enable a neighbor. + type: bool + description: + description: + - Neighbor specific description. + type: str + ebgp_multihop: + description: + - Specifies the maximum hop count for EBGP neighbors not on directly connected + networks. + - The range is from 1 to 255. + type: int + peer_group: + description: + - Name of the peer group that the neighbor is a member of. + type: str + timers: + description: + - Specifies BGP neighbor timer related configurations. + type: dict + suboptions: + keepalive: + description: + - Frequency (in seconds) with which the device sends keepalive messages + to its peer. + - The range is from 0 to 65535. + type: int + required: true + holdtime: + description: + - Interval (in seconds) after not receiving a keepalive message that + IOS declares a peer dead. + - The range is from 0 to 65535. + type: int + required: true + min_neighbor_holdtime: + description: + - Interval (in seconds) specifying the minimum acceptable hold-time + from a BGP neighbor. + - The minimum acceptable hold-time must be less than, or equal to, + the interval specified in the holdtime argument. + - The range is from 0 to 65535. + type: int + local_as: + description: + - The local AS number for the neighbor. + type: int + networks: + description: + - Specify Networks to announce via BGP. + - For operation replace, this option is mutually exclusive with networks option + under address_family. + - For operation replace, if the device already has an address family activated, + this option is not allowed. + type: list + elements: dict + suboptions: + prefix: + description: + - Network ID to announce via BGP. + required: true + type: str + masklen: + description: + - Subnet mask length for the Network to announce(e.g, 8, 16, 24, etc.). + type: int + route_map: + description: + - Route map to modify the attributes. + type: str + address_family: + description: + - Specifies BGP address family related configurations. + type: list + elements: dict + suboptions: + afi: + description: + - Type of address family to configure. + choices: + - ipv4 + - ipv6 + required: true + type: str + safi: + description: + - Specifies the type of cast for the address family. + choices: + - flowspec + - unicast + - multicast + - labeled-unicast + default: unicast + type: str + synchronization: + description: + - Enable/disable IGP synchronization. + type: bool + auto_summary: + description: + - Enable/disable automatic network number summarization. + type: bool + redistribute: + description: + - Specifies the redistribute information from another routing protocol. + type: list + elements: dict + suboptions: + protocol: + description: + - Specifies the protocol for configuring redistribute information. + choices: + - ospf + - ospfv3 + - eigrp + - isis + - static + - connected + - odr + - lisp + - mobile + - rip + required: true + type: str + id: + description: + - Identifier for the routing protocol for configuring redistribute + information. + - Valid for protocols 'ospf', 'ospfv3' and 'eigrp'. + type: str + metric: + description: + - Specifies the metric for redistributed routes. + type: int + route_map: + description: + - Specifies the route map reference. + type: str + networks: + description: + - Specify Networks to announce via BGP. + - For operation replace, this option is mutually exclusive with root level + networks option. + type: list + elements: dict + suboptions: + prefix: + description: + - Network ID to announce via BGP. + required: true + type: str + masklen: + description: + - Subnet mask length for the Network to announce(e.g, 8, 16, 24, etc.). + type: int + route_map: + description: + - Route map to modify the attributes. + type: str + neighbors: + description: + - Specifies BGP neighbor related configurations in Address Family configuration + mode. + type: list + elements: dict + suboptions: + neighbor: + description: + - Neighbor router address. + required: true + type: str + advertisement_interval: + description: + - Minimum interval between sending BGP routing updates for this neighbor. + type: int + route_reflector_client: + description: + - Specify a neighbor as a route reflector client. + type: bool + route_server_client: + description: + - Specify a neighbor as a route server client. + type: bool + activate: + description: + - Enable the Address Family for this Neighbor. + type: bool + remove_private_as: + description: + - Remove the private AS number from outbound updates. + type: bool + next_hop_self: + description: + - Enable/disable the next hop calculation for this neighbor. + type: bool + next_hop_unchanged: + description: + - Propagate next hop unchanged for iBGP paths to this neighbor. + type: bool + maximum_prefix: + description: + - Maximum number of prefixes to accept from this peer. + - The range is from 1 to 2147483647. + type: int + prefix_list_in: + description: + - Name of ip prefix-list to apply to incoming prefixes. + type: str + prefix_list_out: + description: + - Name of ip prefix-list to apply to outgoing prefixes. + type: str + operation: + description: + - Specifies the operation to be performed on the BGP process configured on the + device. + - In case of merge, the input configuration will be merged with the existing BGP + configuration on the device. + - In case of replace, if there is a diff between the existing configuration and + the input configuration, the existing configuration will be replaced by the + input configuration for every option that has the diff. + - In case of override, all the existing BGP configuration will be removed from + the device and replaced with the input configuration. + - In case of delete the existing BGP configuration will be removed from the device. + default: merge + type: str + choices: + - merge + - replace + - override + - delete +""" +EXAMPLES = """ +- name: configure global bgp as 64496 + cisco.ios.ios_bgp: + config: + bgp_as: 64496 + router_id: 192.0.2.1 + log_neighbor_changes: true + neighbors: + - neighbor: 203.0.113.5 + remote_as: 64511 + timers: + keepalive: 300 + holdtime: 360 + min_neighbor_holdtime: 360 + - neighbor: 198.51.100.2 + remote_as: 64498 + networks: + - prefix: 198.51.100.0 + route_map: RMAP_1 + - prefix: 192.0.2.0 + masklen: 23 + address_family: + - afi: ipv4 + safi: unicast + redistribute: + - protocol: ospf + id: 223 + metric: 10 + operation: merge + +- name: Configure BGP neighbors + cisco.ios.ios_bgp: + config: + bgp_as: 64496 + neighbors: + - neighbor: 192.0.2.10 + remote_as: 64496 + password: ansible + description: IBGP_NBR_1 + ebgp_multihop: 100 + timers: + keepalive: 300 + holdtime: 360 + min_neighbor_holdtime: 360 + - neighbor: 192.0.2.15 + remote_as: 64496 + description: IBGP_NBR_2 + ebgp_multihop: 150 + operation: merge + +- name: Configure root-level networks for BGP + cisco.ios.ios_bgp: + config: + bgp_as: 64496 + networks: + - prefix: 203.0.113.0 + masklen: 27 + route_map: RMAP_1 + - prefix: 203.0.113.32 + masklen: 27 + route_map: RMAP_2 + operation: merge + +- name: Configure BGP neighbors under address family mode + cisco.ios.ios_bgp: + config: + bgp_as: 64496 + address_family: + - afi: ipv4 + safi: unicast + neighbors: + - neighbor: 203.0.113.10 + activate: yes + maximum_prefix: 250 + advertisement_interval: 120 + - neighbor: 192.0.2.15 + activate: yes + route_reflector_client: true + operation: merge + +- name: remove bgp as 64496 from config + cisco.ios.ios_bgp: + config: + bgp_as: 64496 + operation: delete +""" +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - router bgp 64496 + - bgp router-id 192.0.2.1 + - bgp log-neighbor-changes + - neighbor 203.0.113.5 remote-as 64511 + - neighbor 203.0.113.5 timers 300 360 360 + - neighbor 198.51.100.2 remote-as 64498 + - network 198.51.100.0 route-map RMAP_1 + - network 192.0.2.0 mask 255.255.254.0 + - address-family ipv4 + - redistribute ospf 223 metric 70 + - exit-address-family +""" +from ansible.module_utils._text import to_text +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.module import ( + NetworkModule, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.cli.config.bgp.process import ( + REDISTRIBUTE_PROTOCOLS, +) + + +def main(): + """ main entry point for module execution + """ + network_spec = { + "prefix": dict(required=True), + "masklen": dict(type="int"), + "route_map": dict(), + } + redistribute_spec = { + "protocol": dict(choices=REDISTRIBUTE_PROTOCOLS, required=True), + "id": dict(), + "metric": dict(type="int"), + "route_map": dict(), + } + timer_spec = { + "keepalive": dict(type="int", required=True), + "holdtime": dict(type="int", required=True), + "min_neighbor_holdtime": dict(type="int"), + } + neighbor_spec = { + "neighbor": dict(required=True), + "remote_as": dict(type="int", required=True), + "local_as": dict(type="int"), + "update_source": dict(), + "password": dict(no_log=True), + "enabled": dict(type="bool"), + "description": dict(), + "ebgp_multihop": dict(type="int"), + "timers": dict(type="dict", options=timer_spec), + "peer_group": dict(), + } + af_neighbor_spec = { + "neighbor": dict(required=True), + "activate": dict(type="bool"), + "advertisement_interval": dict(type="int"), + "remove_private_as": dict(type="bool"), + "next_hop_self": dict(type="bool"), + "next_hop_unchanged": dict(type="bool"), + "route_reflector_client": dict(type="bool"), + "route_server_client": dict(type="bool"), + "maximum_prefix": dict(type="int"), + "prefix_list_in": dict(), + "prefix_list_out": dict(), + } + address_family_spec = { + "afi": dict(choices=["ipv4", "ipv6"], required=True), + "safi": dict( + choices=["flowspec", "labeled-unicast", "multicast", "unicast"], + default="unicast", + ), + "auto_summary": dict(type="bool"), + "synchronization": dict(type="bool"), + "networks": dict(type="list", elements="dict", options=network_spec), + "redistribute": dict( + type="list", elements="dict", options=redistribute_spec + ), + "neighbors": dict( + type="list", elements="dict", options=af_neighbor_spec + ), + } + config_spec = { + "bgp_as": dict(type="int", required=True), + "router_id": dict(), + "log_neighbor_changes": dict(type="bool"), + "neighbors": dict(type="list", elements="dict", options=neighbor_spec), + "address_family": dict( + type="list", elements="dict", options=address_family_spec + ), + "networks": dict(type="list", elements="dict", options=network_spec), + } + argument_spec = { + "config": dict(type="dict", options=config_spec), + "operation": dict( + default="merge", choices=["merge", "replace", "override", "delete"] + ), + } + module = NetworkModule( + argument_spec=argument_spec, supports_check_mode=True + ) + try: + result = module.edit_config(config_filter="| section ^router bgp") + except Exception as exc: + module.fail_json(msg=to_text(exc)) + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_bgp_global.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_bgp_global.py new file mode 100644 index 00000000..8e401986 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_bgp_global.py @@ -0,0 +1,2139 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +""" +The module file for ios_bgp_global +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_bgp_global +short_description: Global BGP resource module +description: This module configures and manages the attributes of global bgp on Cisco IOS. +version_added: 1.3.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL +options: + config: + description: A list of configurations for global bgp. + type: dict + suboptions: + as_number: + description: Autonomous system number. + type: str + required: true + aggregate_address: + description: Configure BGP aggregate entries + type: dict + suboptions: + address: + description: Aggregate address + type: str + netmask: + description: Aggregate mask + type: str + advertise_map: + description: Set condition to advertise attribute + type: str + as_confed_set: + description: Generate AS confed set path information + type: bool + as_set: + description: Generate AS set path information + type: bool + attribute_map: + description: Set attributes of aggregate + type: str + summary_only: + description: Filter more specific routes from updates + type: bool + suppress_map: + description: Conditionally filter more specific routes from updates + type: str + auto_summary: + description: Enable automatic network number summarization + type: bool + bgp: + description: Enable address family and enter its config mode + type: dict + suboptions: + additional_paths: + description: Additional paths in the BGP table + type: dict + suboptions: + install: + description: Additional paths to install into RIB + type: bool + receive: + description: Receive additional paths from neighbors + type: bool + select: + description: Selection criteria to pick the paths + type: dict + suboptions: + all: + description: Select all available paths + type: bool + best: + description: Select best N paths (2-3). + type: int + best_external: + description: Select best-external path + type: bool + group_best: + description: Select group-best path + type: bool + send: + description: Send additional paths to neighbors + type: bool + advertise_best_external: + description: Advertise best external path to internal peers + type: bool + aggregate_timer: + description: + - Configure Aggregation Timer + - Please refer vendor documentation for valid values + type: int + always_compare_med: + description: Allow comparing MED from different neighbors + type: bool + asnotation: + description: + - Change the default asplain notation + - asdot notation + type: bool + bestpath: + description: Change the default bestpath selection + type: list + elements: dict + suboptions: + aigp: + description: + - if both paths doesn't have aigp ignore on bestpath comparision + - ignore + type: bool + compare_routerid: + description: Compare router-id for identical EBGP paths + type: bool + cost_community: + description: cost community + type: bool + igp_metric: + description: + - igp metric + - Ignore igp metric in bestpath selection + type: bool + med: + description: MED attribute + type: dict + suboptions: + confed: + description: Compare MED among confederation paths + type: bool + missing_as_worst: + description: Treat missing MED as the least preferred one + type: bool + client_to_client: + description: Configure client to client route reflection + type: dict + suboptions: + set: + description: set reflection of routes allowed + type: bool + all: + description: inter-cluster and intra-cluster (default) + type: bool + intra_cluster: + description: + - intra cluster reflection + - intra-cluster reflection for cluster-id + type: str + cluster_id: + description: + - Configure Route-Reflector Cluster-id (peers may reset) + - A.B.C.D/Please refer vendor documentation for valid Route-Reflector Cluster-id + type: bool + confederation: + description: AS confederation parameters + type: dict + suboptions: + identifier: + description: + - Set routing domain confederation AS + - AS number + type: str + peers: + description: + - Peer ASs in BGP confederation + - AS number + type: str + consistency_checker: + description: Consistency-checker + type: dict + suboptions: + auto_repair: + description: Auto-Repair + type: dict + suboptions: + set: + description: Enable Auto-Repair + type: bool + interval: + description: + - Set the bgp consistency checker + - Please refer vendor documentation for valid values + type: int + error_message: + description: Log Error-Msg + type: dict + suboptions: + set: + description: Enable Error-Msg + type: bool + interval: + description: + - Set the bgp consistency checker + - Please refer vendor documentation for valid values + type: int + dampening: + description: Enable route-flap dampening + type: dict + suboptions: + penalty_half_time: + description: + - Half-life time for the penalty + - Please refer vendor documentation for valid values + type: int + reuse_route_val: + description: + - Value to start reusing a route + - Please refer vendor documentation for valid values + type: int + suppress_route_val: + description: + - Value to start suppressing a route + - Please refer vendor documentation for valid values + type: int + max_suppress: + description: + - Maximum duration to suppress a stable route + - Please refer vendor documentation for valid values + type: int + route_map: + description: Route-map to specify criteria for dampening + type: str + deterministic_med: + description: Pick the best-MED path among paths advertised from the neighboring AS + type: bool + dmzlink_bw: + description: Use DMZ Link Bandwidth as weight for BGP multipaths + type: bool + enforce_first_as: + description: Enforce the first AS for EBGP routes(default) + type: bool + enhanced_error: + description: Enabled BGP Enhanced error handling + type: bool + fast_external_fallover: + description: Immediately reset session if a link to a directly connected external peer goes down + type: bool + graceful_restart: + description: Graceful restart capability parameters + type: dict + suboptions: + set: + description: Set Graceful-Restart + type: bool + extended: + description: Enable Graceful-Restart Extension + type: bool + restart_time: + description: + - Set the max time needed to restart and come back up + - Please refer vendor documentation for valid values + type: int + stalepath_time: + description: + - Set the max time to hold onto restarting peer's stale paths + - Please refer vendor documentation for valid values + type: int + graceful_shutdown: + description: Graceful shutdown capability parameters + type: dict + suboptions: + neighbors: + description: Gracefully shut down all neigbors + type: dict + suboptions: + time: + description: + - time in seconds + - Please refer vendor documentation for valid values + type: int + activate: + description: Activate graceful shutdown of all neigbors + type: bool + vrfs: + description: Gracefully shut down all vrf neigbors + type: dict + suboptions: + time: + description: + - time in seconds + - Please refer vendor documentation for valid values + type: int + activate: + description: Activate graceful shutdown of all neigbors + type: bool + community: + description: + - Set Community for Gshut routes + - community number/community number in aa:nn format + type: str + local_preference: + description: + - Set Local Preference for Gshut routes + - Please refer vendor documentation for valid values + type: int + inject_map: + description: Routemap which specifies prefixes to inject + type: dict + suboptions: + name: + description: route-map name + type: str + exist_map_name: + description: route-map name + type: str + copy_attributes: + description: Copy attributes from aggregate + type: bool + listen: + description: Neighbor subnet range listener + type: dict + suboptions: + limit: + description: + - Set the max limit for the dynamic subnet range neighbors + - Please refer vendor documentation for valid values + type: int + range: + description: Subnet network range + type: dict + suboptions: + ipv4_with_subnet: + description: IPv4 subnet range(A.B.C.D/nn) + type: str + ipv6_with_subnet: + description: IPv6 subnet range(X:X:X:X::X/<0-128>) + type: str + peer_group: + description: Member of the peer-group + type: str + log_neighbor_changes: + description: Log neighbor up/down and reset reason + type: bool + maxas_limit: + description: + - Allow AS-PATH attribute from any neighbor imposing a limit on number of ASes + - Please refer vendor documentation for valid values + type: int + maxcommunity_limit: + description: + - Allow COMMUNITY attribute from any neighbor imposing a limit on number of communities + - Please refer vendor documentation for valid values + type: int + maxextcommunity_limit: + description: + - Allow EXTENDED COMMUNITY attribute from any neighbor imposing a limit on number of extended communities + - Please refer vendor documentation for valid values + type: int + nexthop: + description: Nexthop tracking commands + type: dict + suboptions: + route_map: + description: Route map for valid nexthops + type: str + trigger: + description: nexthop trackings + type: dict + suboptions: + delay: + description: + - Set the delay to tigger nexthop tracking + - Please refer vendor documentation for valid values + type: int + enable: + description: Enable nexthop tracking + type: bool + nopeerup_delay: + description: Set how long BGP will wait for the first peer to come up before beginning the update delay or + graceful restart timers (in seconds) + type: list + elements: dict + suboptions: + cold_boot: + description: + - How long to wait for the first peer to come up upon a cold boot + - Please refer vendor documentation for valid values + type: int + nsf_switchover: + description: + - How long to wait for the first peer, post NSF switchover + - Please refer vendor documentation for valid values + type: int + post_boot: + description: + - How long to wait for the first peer to come up once the system is already + booted and all peers go down + - Please refer vendor documentation for valid values + type: int + user_initiated: + description: + - How long to wait for the first peer, post a manual clear of BGP peers by the admin user + - Please refer vendor documentation for valid values + type: int + recursion: + description: + - recursion rule for the nexthops + - recursion via host for the nexthops + type: bool + redistribute_internal: + description: Allow redistribution of iBGP into IGPs (dangerous) + type: bool + refresh: + description: refresh + type: dict + suboptions: + max_eor_time: + description: + - Configure refresh max-eor time + - Please refer vendor documentation for valid values + type: int + stalepath_time: + description: + - Configure refresh stale-path time + - Please refer vendor documentation for valid values + type: int + regexp: + description: + - Select regular expression engine + - Enable bounded-execution-time regular expression engine + type: bool + route_map: + description: + - route-map control commands + - Have route-map set commands take priority over BGP commands such as next-hop unchanged + type: bool + router_id: + description: Override configured router identifier (peers will reset) + type: dict + suboptions: + address: + description: Manually configured router identifier(A.B.C.D) + type: str + interface: + description: Use IPv4 address on interface + type: str + vrf: + description: + - vrf-specific router id configuration + - Automatically assign per-vrf bgp router id + type: bool + scan_time: + description: + - Configure background scanner interval + - Please refer vendor documentation for valid values + type: int + slow_peer: + description: Configure slow-peer + type: dict + suboptions: + detection: + description: Slow-peer detection + type: dict + suboptions: + set: + description: Slow-peer detection + type: bool + threshold: + description: + - Set the slow-peer detection threshold + - Please refer vendor documentation for valid values + type: int + split_update_group: + description: Configure slow-peer split-update-group + type: dict + suboptions: + dynamic: + description: Dynamically split the slow peer to slow-update group + type: bool + permanent: + description: Keep the slow-peer permanently in slow-update group + type: int + snmp: + description: + - BGP SNMP options + - BGP SNMP trap options + - Use cbgpPeer2Type as part of index for traps + type: bool + sso: + description: + - Stateful Switchover + - Enable SSO only for Route-Refresh capable peers + type: bool + soft_reconfig_backup: + description: Use soft-reconfiguration inbound only when route-refresh is not negotiated + type: bool + suppress_inactive: + description: Suppress routes that are not in the routing table + type: bool + transport: + description: + - Global enable/disable transport session parameters + - Transport path MTU discovery + type: bool + update_delay: + description: + - Set the max initial delay for sending update + - Please refer vendor documentation for valid values + type: int + update_group: + description: + - Manage peers in bgp update groups + - Split update groups based on Policy + - Keep peers with as-override in different update groups + type: bool + upgrade_cli: + description: Upgrade to hierarchical AFI mode + type: dict + suboptions: + set: + description: enable upgrade to hierarchical AFI mode + type: bool + af_mode: + description: Upgrade to AFI mode + type: bool + bmp: + description: BGP Monitoring Protocol) + type: dict + suboptions: + buffer_size: + description: + - BMP Buffer Size + - Please refer vendor documentation for valid values + type: int + initial_refresh: + description: Initial Refresh options + type: dict + suboptions: + delay: + description: Delay before Initial Refresh + type: int + skip: + description: skip all refreshes + type: bool + server: + description: + - Server Information + - Please refer vendor documentation for valid values + type: int + default_information: + description: + - Control distribution of default information + - Distribute a default route + type: bool + default_metric: + description: + - Set metric of redistributed routes + - Please refer vendor documentation for valid values + type: int + distance: + description: Define an administrative distance + type: dict + suboptions: + admin: + description: Administrative distance + type: dict + suboptions: + distance: + description: + - Administrative distance + - Please refer vendor documentation for valid values + type: int + address: + description: IP Source address (A.B.C.D) + type: str + wildcard_bit: + description: Wildcard bits (A.B.C.D) + type: str + acl: + description: + - IP Standard access list number + - IP Standard expanded access list number + - Standard access-list name + type: str + bgp: + description: BGP distance + type: dict + suboptions: + routes_external: + description: + - Distance for routes external to the AS + - Please refer vendor documentation for valid values + type: int + routes_internal: + description: + - Distance for routes internal to the AS + - Please refer vendor documentation for valid values + type: int + routes_local: + description: + - Distance for local routes + - Please refer vendor documentation for valid values + type: int + mbgp: + description: MBGP distance + type: dict + suboptions: + routes_external: + description: + - Distance for routes external to the AS + - Please refer vendor documentation for valid values + type: int + routes_internal: + description: + - Distance for routes internal to the AS + - Please refer vendor documentation for valid values + type: int + routes_local: + description: + - Distance for local routes + - Please refer vendor documentation for valid values + type: int + distribute_list: + description: Filter networks in routing updates + type: dict + suboptions: + acl: + description: IP access list number/name + type: str + in: + description: Filter incoming routing updates + type: bool + out: + description: Filter outgoing routing updates + type: bool + interface: + description: interface details + type: str + maximum_paths: + description: Forward packets over multiple paths + type: dict + suboptions: + paths: + description: Number of paths + type: int + eibgp: + description: Both eBGP and iBGP paths as multipath + type: int + ibgp: + description: iBGP-multipath + type: int + maximum_secondary_paths: + description: Maximum secondary paths + type: dict + suboptions: + paths: + description: Number of secondary paths + type: int + eibgp: + description: Both eBGP and iBGP paths as secondary multipath + type: int + ibgp: + description: iBGP-secondary-multipath + type: int + neighbor: + description: Specify a neighbor router + type: list + elements: dict + suboptions: + address: + description: Neighbor address (A.B.C.D) + type: str + tag: + description: Neighbor tag + type: str + ipv6_adddress: + description: Neighbor ipv6 address (X:X:X:X::X) + type: str + activate: + description: Enable the Address Family for this Neighbor + type: bool + additional_paths: + description: Negotiate additional paths capabilities with this neighbor + type: dict + suboptions: + disable: + description: Disable additional paths for this neighbor + type: bool + receive: + description: Receive additional paths from neighbors + type: bool + send: + description: Send additional paths to neighbors + type: bool + advertise: + description: Advertise to this neighbor + type: dict + suboptions: + additional_paths: + description: Advertise additional paths + type: dict + suboptions: + all: + description: Select all available paths + type: bool + best: + description: Select best N paths (2-3). + type: int + group_best: + description: Select group-best path + type: bool + best_external: + description: Advertise best-external (at RRs best-internal) path + type: bool + diverse_path: + description: Advertise additional paths + type: dict + suboptions: + backup: + description: Diverse path can be backup path + type: bool + mpath: + description: Diverse path can be multipath + type: bool + advertise_map: + description: specify route-map for conditional advertisement + type: dict + suboptions: + name: + description: advertise route-map name + type: str + exist_map: + description: + - advertise prefix only if prefix is in the condition exists + - condition route-map name + type: str + non_exist_map: + description: + - advertise prefix only if prefix in the condition does not exist + - condition route-map name + type: str + advertisement_interval: + description: Minimum interval between sending BGP routing updates + type: int + aigp: + description: AIGP on neighbor + type: dict + suboptions: + enable: + description: Enable AIGP + type: bool + send: + description: Cost community or MED carrying AIGP VALUE + type: dict + suboptions: + cost_community: + description: Cost extended community carrying AIGP Value + type: dict + suboptions: + id: + description: + - Community ID + - Please refer vendor documentation for valid values + type: int + poi: + description: Point of Insertion + type: dict + suboptions: + igp_cost: + description: Point of Insertion After IGP + type: bool + pre_bestpath: + description: Point of Insertion At Beginning + type: bool + transitive: + description: Cost community is Transitive + type: bool + med: + description: Med carrying AIGP Value + type: bool + allow_policy: + description: Enable the policy support for this IBGP Neighbor + type: bool + allowas_in: + description: Accept as-path with my AS present in it + type: int + as_override: + description: + - Override matching AS-number while sending update + - Maintain Split Horizon while sending update + type: bool + bmp_activate: + description: Activate the BMP monitoring for a BGP peer + type: dict + suboptions: + all: + description: Activate BMP monitoring for all servers + type: bool + server: + description: + - Activate BMP for server + - BMP Server Number + - Please refer vendor documentation for valid values + type: int + capability: + description: + - Advertise capability to the peer + - Advertise ORF capability to the peer + - Advertise prefixlist ORF capability to this neighbor + type: dict + suboptions: + both: + description: Capability to SEND and RECEIVE the ORF to/from this neighbor + type: bool + receive: + description: Capability to RECEIVE the ORF from this neighbor + type: bool + send: + description: Capability to SEND the ORF to this neighbor + type: bool + cluster_id: + description: + - Configure Route-Reflector Cluster-id (peers may reset) + - Route-Reflector Cluster-id as 32 bit quantity, or + Route-Reflector Cluster-id in IP address format (A.B.C.D) + type: str + default_originate: + description: Originate default route to this neighbor + type: dict + suboptions: + set: + description: Originate default route to this neighbor + type: bool + route_map: + description: Route-map to specify criteria to originate default + type: str + description: + description: Neighbor specific description + type: str + disable_connected_check: + description: one-hop away EBGP peer using loopback address + type: bool + distribute_list: + description: Filter updates to/from this neighbor + type: dict + suboptions: + acl: + description: IP access list number/name + type: str + in: + description: Filter incoming updates + type: bool + out: + description: Filter outgoing updates + type: bool + dmzlink_bw: + description: Propagate the DMZ link bandwidth + type: bool + ebgp_multihop: + description: Allow EBGP neighbors not on directly connected networks + type: dict + suboptions: + enable: + description: Allow EBGP neighbors not on directly connected networks + type: bool + hop_count: + description: + - Maximum hop count + - Please refer vendor documentation for valid values + type: int + fall_over: + description: Session fall on peer route lost + type: dict + suboptions: + bfd: + description: Use BFD to detect failure + type: dict + suboptions: + set: + description: set bfd + type: bool + multi_hop: + description: Force BFD multi-hop to detect failure + type: bool + single_hop: + description: Force BFD single-hop to detect failure + type: bool + route_map: + description: Route map for peer route + type: str + filter_list: + description: Establish BGP filters + type: dict + suboptions: + path_acl: + description: AS path access list + type: str + in: + description: Filter incoming updates + type: bool + out: + description: Filter outgoing updates + type: bool + ha_mode: + description: high availability mode + type: dict + suboptions: + set: + description: set ha-mode and graceful-restart for this peer + type: bool + disable: + description: disable graceful-restart + type: bool + inherit: + description: + - Inherit a template + - Inherit a peer-session template and Template name + type: str + local_as: + description: Specify a local-as number + type: dict + suboptions: + set: + description: set local-as number + type: bool + number: + description: + - AS number used as local AS + - Please refer vendor documentation for valid values + type: int + dual_as: + description: Accept either real AS or local AS from the ebgp peer + type: bool + no_prepend: + description: Do not prepend local-as to updates from ebgp peers + type: dict + suboptions: + set: + description: Set prepend + type: bool + replace_as: + description: Replace real AS with local AS in the EBGP updates + type: bool + log_neighbor_changes: + description: Log neighbor up/down and reset reason + type: dict + suboptions: + set: + description: set Log neighbor up/down and reset + type: bool + disable: + description: disable Log neighbor up/down and reset + type: bool + maximum_prefix: + description: Maximum number of prefixes accepted from this peer + type: dict + suboptions: + max_no: + description: maximum no. of prefix limit + type: int + threshold_val: + description: Threshold value (%) at which to generate a warning msg + type: int + restart: + description: Restart bgp connection after limit is exceeded + type: int + warning_only: + description: Only give warning message when limit is exceeded + type: bool + next_hop_self: + description: Disable the next hop calculation for this neighbor + type: dict + suboptions: + set: + description: Enable next-hop-self + type: bool + all: + description: Enable next-hop-self for both eBGP and iBGP received paths + type: bool + next_hop_unchanged: + description: + - Propagate next hop unchanged for iBGP paths to this neighbor + - Propagate next hop unchanged for all paths (iBGP and eBGP) to this neighbor + type: dict + suboptions: + set: + description: Enable next-hop-unchanged + type: bool + allpaths: + description: Propagate next hop unchanged for all paths (iBGP and eBGP) to this neighbor + type: bool + password: + description: Set a password + type: str + path_attribute: + description: BGP optional attribute filtering + type: dict + suboptions: + discard: + description: Discard matching path-attribute for this neighbor + type: dict + suboptions: + type: + description: + - path attribute type + - Please refer vendor documentation for valid values + type: int + range: + description: path attribute range + type: dict + suboptions: + start: + description: + - path attribute range start value + - Please refer vendor documentation for valid values + type: int + end: + description: + - path attribute range end value + - Please refer vendor documentation for valid values + type: int + in: + description: Perform inbound path-attribute filtering + type: bool + treat_as_withdraw: + description: Treat-as-withdraw matching path-attribute for this neighbor + type: dict + suboptions: + type: + description: + - path attribute type + - Please refer vendor documentation for valid values + type: int + range: + description: path attribute range + type: dict + suboptions: + start: + description: + - path attribute range start value + - Please refer vendor documentation for valid values + type: int + end: + description: + - path attribute range end value + - Please refer vendor documentation for valid values + type: int + in: + description: Perform inbound path-attribute filtering + type: bool + peer_group: + description: Member of the peer-group + type: str + remote_as: + description: + - Specify a BGP neighbor + - AS of remote neighbor + type: int + remove_private_as: + description: Remove private AS number from outbound updates + type: dict + suboptions: + set: + description: Remove private AS number + type: bool + all: + description: Remove all private AS numbers + type: bool + replace_as: + description: Replace all private AS numbers with local AS + type: bool + route_map: + description: Apply route map to neighbor + type: dict + suboptions: + name: + description: Replace all private AS numbers with local AS + type: str + in: + description: Apply map to incoming routes + type: bool + out: + description: Apply map to outbound routes + type: bool + route_reflector_client: + description: Configure a neighbor as Route Reflector client + type: bool + route_server_client: + description: Configure a neighbor as Route Server client + type: dict + suboptions: + set: + description: Set Route Server client + type: bool + context: + description: + - Specify Route Server context for neighbor + - Route Server context name + type: str + send_community: + description: Send Community attribute to this neighbor + type: dict + suboptions: + set: + description: Set send Community attribute to this neighbor + type: bool + both: + description: Send Standard and Extended Community attributes + type: bool + extended: + description: Send Extended Community attribute + type: bool + standard: + description: Send Standard Community attribute + type: bool + send_label: + description: Send NLRI + MPLS Label to this peer + type: dict + suboptions: + set: + description: Set send NLRI + MPLS Label to this peer + type: bool + explicit_null: + description: Advertise Explicit Null label in place of Implicit Null + type: bool + shutdown: + description: Administratively shut down this neighbor + type: dict + suboptions: + set: + description: shut down + type: bool + graceful: + description: + - Gracefully shut down this neighbor + - time in seconds + - Please refer vendor documentation for valid values + type: int + slow_peer: + description: Configure slow-peer + type: dict + suboptions: + detection: + description: Configure slow-peer + type: dict + suboptions: + enable: + description: Enable slow-peer detection + type: bool + disable: + description: Disable slow-peer detection + type: bool + threshold: + description: Set the slow-peer detection threshold + type: int + split_update_group: + description: Configure slow-peer split-update-group + type: dict + suboptions: + dynamic: + description: Dynamically split the slow peer to slow-update group + type: dict + suboptions: + enable: + description: Enable slow-peer detection + type: bool + disable: + description: Disable slow-peer detection + type: bool + permanent: + description: Keep the slow-peer permanently in slow-update group + type: bool + static: + description: Static slow-peer + type: bool + soft_reconfiguration: + description: + - Per neighbor soft reconfiguration + - Allow inbound soft reconfiguration for this neighbor + type: bool + timers: + description: BGP per neighbor timers + type: dict + suboptions: + interval: + description: Keepalive interval + type: int + holdtime: + description: Holdtime + type: int + min_holdtime: + description: Minimum hold time from neighbor + type: int + translate_update: + description: Translate Update to MBGP format + type: dict + suboptions: + set: + description: Set Translate Update + type: bool + nlri: + description: Specify type of nlri to translate to + type: dict + suboptions: + multicast: + description: Translate Update to multicast nlri + type: bool + unicast: + description: Process Update as unicast nlri + type: bool + transport: + description: Transport options + type: dict + suboptions: + connection_mode: + description: Specify passive or active connection + type: dict + suboptions: + active: + description: Actively establish the TCP session + type: bool + passive: + description: Passively establish the TCP session + type: bool + multi_session: + description: Use Multi-session for transport + type: bool + path_mtu_discovery: + description: Use transport path MTU discovery + type: dict + suboptions: + set: + description: Use path MTU discovery + type: bool + disable: + description: disable + type: bool + ttl_security: + description: + - BGP ttl security check + - maximum number of hops + - Please refer vendor documentation for valid values + type: int + unsuppress_map: + description: + - Route-map to selectively unsuppress suppressed routes + - Name of route map + type: str + version: + description: + - Set the BGP version to match a neighbor + - Neighbor's BGP version + - Please refer vendor documentation for valid values + type: int + weight: + description: Set default weight for routes from this neighbor + type: int + redistribute: + description: Redistribute information from another routing protocol + type: list + elements: dict + suboptions: + application: + description: Application + type: dict + suboptions: + name: + description: Application name + type: str + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + bgp: + description: Border Gateway Protocol (BGP) + type: dict + suboptions: + as_number: + description: Autonomous system number + type: str + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + connected: + description: Connected + type: dict + suboptions: + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + eigrp: + description: Enhanced Interior Gateway Routing Protocol (EIGRP) + type: dict + suboptions: + as_number: + description: Autonomous system number + type: str + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + isis: + description: ISO IS-IS + type: dict + suboptions: + area_tag: + description: ISO routing area tag + type: str + clns: + description: Redistribution of OSI dynamic routes + type: bool + ip: + description: Redistribution of IP dynamic routes + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + iso_igrp: + description: IGRP for OSI networks + type: dict + suboptions: + area_tag: + description: ISO routing area tag + type: str + route_map: + description: Route map reference + type: str + lisp: + description: Locator ID Separation Protocol (LISP) + type: dict + suboptions: + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + mobile: + description: Mobile routes + type: dict + suboptions: + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + odr: + description: On Demand stub Routes + type: dict + suboptions: + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + ospf: + description: Open Shortest Path First (OSPF) + type: dict + suboptions: + process_id: + description: Process ID + type: int + match: + description: On Demand stub Routes + type: dict + suboptions: + external: + description: Redistribute OSPF external routes + type: bool + internal: + description: Redistribute OSPF internal routes + type: bool + nssa_external: + description: Redistribute OSPF NSSA external routes + type: bool + type_1: + description: Redistribute NSSA external type 1 routes + type: bool + type_2: + description: Redistribute NSSA external type 2 routes + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + vrf: + description: VPN Routing/Forwarding Instance + type: str + ospfv3: + description: OSPFv3 + type: dict + suboptions: + process_id: + description: Process ID + type: int + match: + description: On Demand stub Routes + type: dict + suboptions: + external: + description: Redistribute OSPF external routes + type: bool + internal: + description: Redistribute OSPF internal routes + type: bool + nssa_external: + description: Redistribute OSPF NSSA external routes + type: bool + type_1: + description: Redistribute NSSA external type 1 routes + type: bool + type_2: + description: Redistribute NSSA external type 2 routes + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + rip: + description: Routing Information Protocol (RIP) + type: dict + suboptions: + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + static: + description: Static routes + type: dict + suboptions: + clns: + description: Redistribution of OSI static routes + type: bool + ip: + description: Redistribution of IP static routes + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + vrf: + description: Specify a source VRF + type: dict + suboptions: + name: + description: Source VRF name + type: str + global: + description: global VRF + type: bool + route_server_context: + description: Enter route server context command mode + type: dict + suboptions: + name: + description: Name of route server context + type: str + address_family: + description: Enter address family command mode + type: dict + suboptions: + afi: + description: Address family + type: str + choices: ['ipv4', 'ipv6'] + modifier: + description: Address Family modifier + type: str + choices: ['multicast', 'unicast'] + import_map: + description: + - Import matching routes using a route map + - Name of route map + type: str + description: + description: Textual description of the router server context + type: str + scope: + description: Enter scope command mode + type: dict + suboptions: + global: + description: Global scope + type: bool + vrf: + description: + - VRF scope + - VPN Routing/Forwarding instance name + type: str + synchronization: + description: Perform IGP synchronization + type: bool + table_map: + description: Map external entry attributes into routing table + type: dict + suboptions: + name: + description: route-map name + type: str + filter: + description: Selective route download + type: bool + template: + description: Enter template command mode + type: dict + suboptions: + peer_policy: + description: Template configuration for policy parameters + type: str + peer_session: + description: Template configuration for session parameters + type: str + timers: + description: + - Adjust routing timers + - BGP timers + type: dict + suboptions: + keepalive: + description: Keepalive interval + type: int + holdtime: + description: Holdtime + type: int + min_holdtime: + description: Minimum hold time from neighbor + 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 IOS + device by executing the command B(sh running-config | section ^router 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 + state: + choices: + - merged + - replaced + - overridden + - deleted + - purged + - gathered + - rendered + - parsed + 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str +""" +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp + +- name: Merge provided configuration with device configuration + cisco.ios.ios_bgp_global: + config: + as_number: 65000 + bgp: + advertise_best_external: true + bestpath: + - compare_routerid: true + nopeerup_delay: + - post_boot: 10 + dampening: + penalty_half_time: 1 + reuse_route_val: 1 + suppress_route_val: 1 + max_suppress: 1 + graceful_shutdown: + neighbors: + time: 50 + community: 100 + local_preference: 100 + neighbor: + - address: 198.51.100.1 + description: merge neighbor + remote_as: 100 + aigp: + send: + cost_community: + id: 100 + poi: + igp_cost: true + transitive: true + route_map: + name: test-route + out: true + state: merged + +# Commands fired: +# --------------- +# +# "commands": [ +# "router bgp 65000", +# "bgp dampening 1 1 1 1", +# "bgp graceful-shutdown all neighbors 50 community 100 local-preference 100", +# "bgp advertise-best-external", +# "bgp nopeerup-delay post-boot 10", +# "bgp bestpath compare-routerid", +# "neighbor 198.51.100.1 remote-as 100", +# "neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive", +# "neighbor 198.51.100.1 description merge neighbor", +# "neighbor 198.51.100.1 route-map test-route out" +# ] + +# After state: +# ------------ +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp nopeerup-delay post-boot 10 +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# bgp bestpath compare-routerid +# bgp dampening 1 1 1 1 +# bgp advertise-best-external +# neighbor 198.51.100.1 remote-as 100 +# neighbor 198.51.100.1 description merge neighbor +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 route-map test-route out + + +# Using replaced + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp nopeerup-delay post-boot 10 +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# bgp bestpath compare-routerid +# bgp dampening 1 1 1 1 +# bgp advertise-best-external +# neighbor 198.51.100.1 remote-as 100 +# neighbor 198.51.100.1 description merge neighbor +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 route-map test-route out + + +- name: Replaces device configuration of listed global BGP with provided configuration + cisco.ios.ios_bgp_global: + config: + as_number: 65000 + bgp: + advertise_best_external: true + bestpath: + - med: + confed: true + log_neighbor_changes: true + nopeerup_delay: + - post_boot: 10 + cold_boot: 20 + neighbor: + - address: 192.0.2.1 + description: replace neighbor + remote_as: 100 + slow_peer: + detection: + disable: true + state: replaced + +# Commands fired: +# --------------- +# +# "commands": [ +# "router bgp 65000" +# "no bgp dampening 1 1 1 1" +# "no timers bgp 100 200 150" +# "no bgp bestpath compare-routerid" +# "bgp bestpath med confed" +# "bgp nopeerup-delay cold-boot 20" +# "no neighbor 198.51.100.1 remote-as 100" +# "neighbor 192.0.2.1 remote-as 100" +# "no bgp graceful-shutdown all neighbors 50 local-preference 100 community 100" +# "no neighbor 198.51.100.1 route-map test-route out" +# "no neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive" +# "no neighbor 198.51.100.1 description merge neighbor" +# "neighbor 192.0.2.1 slow-peer detection disable" +# "neighbor 192.0.2.1 description replace neighbor" +# ] + + +# After state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp nopeerup-delay cold-boot 20 +# bgp nopeerup-delay post-boot 10 +# bgp bestpath med confed +# bgp advertise-best-external +# redistribute connected metric 10 +# neighbor 192.0.2.1 remote-as 100 +# neighbor 192.0.2.1 description replace neighbor +# neighbor 192.0.2.1 slow-peer detection disable + +# Using Deleted + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp nopeerup-delay post-boot 10 +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# bgp bestpath compare-routerid +# bgp dampening 1 1 1 1 +# bgp advertise-best-external +# neighbor 198.51.100.1 remote-as 100 +# neighbor 198.51.100.1 description merge neighbor +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 route-map test-route out + +- name: "Delete global BGP (Note: This won't delete the configured global BGP)" + cisco.ios.ios_bgp_global: + config: + as_number: 65000 + state: deleted + +# Commands fired: +# --------------- +# "commands": [ +# "router bgp 65000", +# "no bgp dampening 1 1 1 1", +# "no bgp graceful-shutdown all neighbors 50 community 100 local-preference 100", +# "no bgp advertise-best-external", +# "no bgp bestpath compare-routerid", +# "no bgp nopeerup-delay post-boot 10", +# "no neighbor 198.51.100.1 remote-as 100", +# "no neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive", +# "no neighbor 198.51.100.1 description merge neighbor", +# "no neighbor 198.51.100.1 route-map test-route out" +# ] + + +# After state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 + + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured global BGP)" + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp nopeerup-delay post-boot 10 +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# bgp bestpath compare-routerid +# bgp dampening 1 1 1 1 +# bgp advertise-best-external +# neighbor 198.51.100.1 remote-as 100 +# neighbor 198.51.100.1 description merge neighbor +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 route-map test-route out + + +- name: "Delete global BGP without config" + cisco.ios.ios_bgp_global: + state: deleted + +# Commands fired: +# --------------- +# "commands": [ +# "router bgp 65000", +# "no bgp dampening 1 1 1 1", +# "no bgp graceful-shutdown all neighbors 50 community 100 local-preference 100", +# "no bgp advertise-best-external", +# "no bgp bestpath compare-routerid", +# "no bgp nopeerup-delay post-boot 10", +# "no neighbor 198.51.100.1 remote-as 100", +# "no neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive", +# "no neighbor 198.51.100.1 description merge neighbor", +# "no neighbor 198.51.100.1 route-map test-route out" +# ] + + +# After state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 + +# Using Purged +#"(NOTE: This WILL delete the configured global BGP)" + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp nopeerup-delay post-boot 10 +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# bgp bestpath compare-routerid +# bgp dampening 1 1 1 1 +# bgp advertise-best-external +# neighbor 198.51.100.1 remote-as 100 +# neighbor 198.51.100.1 description merge neighbor +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 route-map test-route out + + +- name: 'Delete the configured global BGP (Note: This WILL delete the the configured + global BGP)' + cisco.ios.ios_bgp_global: + state: purged + +# Commands fired: +# --------------- +# "commands": [ +# "no router bgp 65000", +# ] + +# After state: +# ------------- +# +# vios#sh running-config | section ^router bgp + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp nopeerup-delay post-boot 10 +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# bgp bestpath compare-routerid +# bgp dampening 1 1 1 1 +# bgp advertise-best-external +# neighbor 198.51.100.1 remote-as 100 +# neighbor 198.51.100.1 description merge neighbor +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 route-map test-route out + + +- name: Gather listed global BGP with provided configurations + cisco.ios.ios_bgp_global: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": { +# "as_number": "65000", +# "bgp": { +# "advertise_best_external": true, +# "bestpath": [ +# { +# "compare_routerid": true +# } +# ], +# "dampening": { +# "max_suppress": 1, +# "penalty_half_time": 1, +# "reuse_route_val": 1, +# "suppress_route_val": 1 +# }, +# "graceful_shutdown": { +# "community": "100", +# "local_preference": 100, +# "neighbors": { +# "time": 50 +# } +# }, +# "nopeerup_delay": [ +# { +# "post_boot": 10 +# } +# ] +# }, +# "neighbor": [ +# { +# "address": "198.51.100.1", +# "aigp": { +# "send": { +# "cost_community": { +# "id": 100, +# "poi": { +# "igp_cost": true, +# "transitive": true +# } +# } +# } +# }, +# "description": "merge neighbor", +# "remote_as": 100, +# "route_map": { +# "name": "test-route", +# "out": true +# } +# } +# ] +# } + +# Using Rendered + +- name: Rendered the provided configuration with the exisiting running configuration + cisco.ios.ios_bgp_global: + config: + as_number: 65000 + bgp: + advertise_best_external: true + bestpath: + - compare_routerid: true + nopeerup_delay: + - post_boot: 10 + dampening: + penalty_half_time: 1 + reuse_route_val: 1 + suppress_route_val: 1 + max_suppress: 1 + graceful_shutdown: + neighbors: + time: 50 + community: 100 + local_preference: 100 + neighbor: + - address: 198.51.100.1 + description: merge neighbor + remote_as: 100 + aigp: + send: + cost_community: + id: 100 + poi: + igp_cost: true + transitive: true + route_map: + name: test-route + out: true + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "router bgp 65000", +# "bgp dampening 1 1 1 1", +# "bgp graceful-shutdown all neighbors 50 community 100 local-preference 100", +# "bgp advertise-best-external", +# "bgp nopeerup-delay post-boot 10", +# "bgp bestpath compare-routerid", +# "neighbor 198.51.100.1 remote-as 100", +# "neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive", +# "neighbor 198.51.100.1 description merge neighbor", +# "neighbor 198.51.100.1 route-map test-route out" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# router bgp 65000 +# bgp nopeerup-delay post-boot 10 +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# bgp bestpath compare-routerid +# bgp dampening 1 1 1 1 +# bgp advertise-best-external +# neighbor 198.51.100.1 remote-as 100 +# neighbor 198.51.100.1 description merge neighbor +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 route-map test-route out + +- name: Parse the commands for provided configuration + cisco.ios.ios_bgp_global: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": { +# "as_number": "65000", +# "bgp": { +# "advertise_best_external": true, +# "bestpath": [ +# { +# "compare_routerid": true +# } +# ], +# "dampening": { +# "max_suppress": 1, +# "penalty_half_time": 1, +# "reuse_route_val": 1, +# "suppress_route_val": 1 +# }, +# "graceful_shutdown": { +# "community": "100", +# "local_preference": 100, +# "neighbors": { +# "time": 50 +# } +# }, +# "nopeerup_delay": [ +# { +# "post_boot": 10 +# } +# ] +# }, +# "neighbor": [ +# { +# "address": "198.51.100.1", +# "aigp": { +# "send": { +# "cost_community": { +# "id": 100, +# "poi": { +# "igp_cost": true, +# "transitive": true +# } +# } +# } +# }, +# "description": "merge neighbor", +# "remote_as": 100, +# "route_map": { +# "name": "test-route", +# "out": true +# } +# } +# ] +# } + +""" + +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: ["router bgp 65000", "bgp nopeerup-delay post-boot 10", "bgp advertise-best-external"] +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.bgp_global.bgp_global import ( + Bgp_globalArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.bgp_global.bgp_global import ( + Bgp_global, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + module = AnsibleModule( + argument_spec=Bgp_globalArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Bgp_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_command.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_command.py new file mode 100644 index 00000000..7c502974 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_command.py @@ -0,0 +1,215 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_command +author: Peter Sprygada (@privateip) +short_description: Run commands on remote devices running Cisco IOS +description: +- Sends arbitrary commands to an ios node and returns the results read from the device. + This module includes an argument that will cause the module to wait for a specific + condition before returning or timing out if the condition is not met. +- This module does not support running commands in configuration mode. Please use + M(ios_config) to configure IOS devices. +version_added: 1.0.0 +extends_documentation_fragment: +- cisco.ios.ios +notes: +- Tested against IOS 15.6 +options: + commands: + description: + - List of commands to send to the remote ios device over the configured provider. + The resulting output from the command is returned. If the I(wait_for) argument + is provided, the module is not returned until the condition is satisfied or + the number of retries has expired. 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). Common answers are 'y' or "\\r" (carriage return, must be double + quotes). See examples. + required: true + type: list + elements: raw + wait_for: + description: + - List of conditions to evaluate against the output of the command. The task will + wait for each condition to be true before moving forward. If the conditional + is not true within the configured number of retries, the task fails. See examples. + aliases: + - waitfor + type: list + elements: str + 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 by tried before it is considered + failed. The command is run on the target device every retry and evaluated against + the I(wait_for) conditions. + default: 10 + type: int + interval: + description: + - Configures the interval in seconds to wait between 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 +""" +EXAMPLES = """ +- name: run show version on remote devices + cisco.ios.ios_command: + commands: show version + +- name: run show version and check to see if output contains IOS + cisco.ios.ios_command: + commands: show version + wait_for: result[0] contains IOS + +- name: run multiple commands on remote nodes + cisco.ios.ios_command: + commands: + - show version + - show interfaces + +- name: run multiple commands and evaluate the output + cisco.ios.ios_command: + commands: + - show version + - show interfaces + wait_for: + - result[0] contains IOS + - result[1] contains Loopback0 + +- name: run commands that require answering a prompt + cisco.ios.ios_command: + commands: + - command: clear counters GigabitEthernet0/1 + prompt: Clear "show interface" counters on this interface [confirm] + answer: y + - command: clear counters GigabitEthernet0/2 + prompt: '[confirm]' + answer: '\r' +""" +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 apart from low level errors (such as action plugin) + type: list + sample: [['...', '...'], ['...'], ['...']] +failed_conditions: + description: The list of conditionals that have failed + returned: failed + 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 ( + transform_commands, + to_lines, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + run_commands, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) + + +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(): + """main entry point for module execution + """ + argument_spec = dict( + commands=dict(type="list", elements="raw", required=True), + wait_for=dict(type="list", elements="str", aliases=["waitfor"]), + match=dict(default="all", choices=["all", "any"]), + retries=dict(default=10, type="int"), + interval=dict(default=1, type="int"), + ) + argument_spec.update(ios_argument_spec) + module = AnsibleModule( + argument_spec=argument_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() + 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"] + while retries > 0: + 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) + retries -= 1 + 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/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_config.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_config.py new file mode 100644 index 00000000..260b74b0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_config.py @@ -0,0 +1,593 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_config +author: Peter Sprygada (@privateip) +short_description: Manage Cisco IOS configuration sections +description: +- Cisco IOS configurations use a simple block indent file syntax for segmenting configuration + into sections. This module provides an implementation for working with IOS configuration + sections in a deterministic way. +version_added: 1.0.0 +extends_documentation_fragment: +- cisco.ios.ios +notes: +- Tested against IOS 15.6 +- Abbreviated commands are NOT idempotent, see L + (Network FAQ,../network/user_guide/faq.html#why-do-the-config-modules-always-return-changed-true-with-abbreviated-commands). +- 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 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 + aliases: + - commands + parents: + description: + - The ordered set of parents that uniquely identify the section or hierarchy the + commands should be checked against. If the parents argument is omitted, the + commands are checked against the set of top level or global commands. + type: list + elements: str + src: + description: + - Specifies the source path to the file that contains the configuration or configuration + template to load. The path to the source file can either be the full path on + the Ansible control host or a relative path from the playbook or role root directory. This + argument is mutually exclusive with I(lines), I(parents). 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 the indentation to ensure idempotency and correct diff. + type: str + before: + description: + - The ordered set of commands to push on to the command stack if a change needs + to be made. This allows the playbook designer the opportunity to perform configuration + commands prior to pushing any changes without affecting how the set of commands + are matched against the system. + type: list + elements: str + after: + description: + - The ordered set of commands to append to the end of the command stack if a change + needs to be made. Just like with I(before) this allows the playbook designer + to append a set of commands to be executed after the command set. + type: list + elements: str + match: + description: + - Instructs the module on the way to perform the matching of the set of commands + against the current device config. If match is set to I(line), commands are + matched line by line. If match is set to I(strict), command lines are matched + with respect to position. If match is set to I(exact), command lines must be + an equal match. Finally, if match is set to I(none), the module will not attempt + to compare the source configuration with the running configuration on the remote + device. + choices: + - line + - strict + - exact + - none + type: str + default: line + replace: + description: + - Instructs the module on the way to perform the configuration on the device. + If the replace argument is set to I(line) then the modified lines are pushed + to the device in configuration mode. If the replace argument is set to I(block) + then the entire command block is pushed to the device in configuration mode + if any line is not correct. + default: line + choices: + - line + - block + type: str + multiline_delimiter: + description: + - This argument is used when pushing a multiline configuration element to the + IOS device. It specifies the character to use as the delimiting character. This + only applies to the configuration action. + default: '@' + type: str + backup: + description: + - This argument will cause the module to create a full backup of the current C(running-config) + from the remote device before any changes are made. If the C(backup_options) + value is not given, the backup file is written to the C(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: no + 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. + The configuration lines for this option should be similar to how it will appear if present + in the running-configuration of the device including the indentation to ensure idempotency + and correct diff. + type: str + aliases: + - config + defaults: + description: + - This argument specifies whether or not to collect all defaults when getting + the remote device running config. When enabled, the module will get the current + config by issuing the command C(show running-config all). + type: bool + default: no + save_when: + description: + - When changes are made to the device running-configuration, the changes are not + copied to non-volatile storage by default. Using this argument will change + that before. If the argument is set to I(always), then the running-config will + always be copied to the startup-config and the I(modified) flag will always + be set to True. If the argument is set to I(modified), then the running-config + will only be copied to the startup-config if it has changed since the last save + to startup-config. If the argument is set to I(never), the running-config will + never be copied to the startup-config. If the argument is set to I(changed), + then the running-config will only be copied to the startup-config if the task + has made a change. I(changed) was added in Ansible 2.5. + default: never + choices: + - always + - never + - modified + - changed + type: str + diff_against: + description: + - When using the C(ansible-playbook --diff) command line argument the module can + generate diffs against different sources. + - When this option is configure as I(startup), the module will return the diff + of the running-config against the startup-config. + - When this option is configured as I(intended), the module will return the diff + of the running-config against the configuration provided in the C(intended_config) + argument. + - When this option is configured as I(running), the module will return the before + and after diff of the running-config with respect to any changes made to the + device configuration. + type: str + choices: + - running + - startup + - intended + diff_ignore_lines: + description: + - Use this argument to specify one or more lines that should be ignored during + the diff. This is used for lines in the configuration that are automatically + updated by the system. This argument takes a list of regular expressions or + exact line matches. + type: list + elements: str + intended_config: + description: + - The C(intended_config) provides the master configuration that the node should + conform to and is used to check the final running-config against. This argument + will not modify any settings on the remote device and is strictly used to check + the compliance of the current device's configuration against. When specifying + this argument, the task should also modify the C(diff_against) value and set + it to I(intended). The configuration lines for this value should be similar to how it + will appear if present in the running-configuration of the device including the indentation + to ensure correct diff. + type: str + 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 I(yes), + if C(backup) is set to I(no) 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 <hostname>_config.<current-date>@<current-time> + 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 +""" +EXAMPLES = """ +- name: configure top level configuration + cisco.ios.ios_config: + lines: hostname {{ inventory_hostname }} + +- name: configure interface settings + cisco.ios.ios_config: + lines: + - description test interface + - ip address 172.31.1.1 255.255.255.0 + parents: interface Ethernet1 + +- name: configure ip helpers on multiple interfaces + cisco.ios.ios_config: + lines: + - ip helper-address 172.26.1.10 + - ip helper-address 172.26.3.8 + parents: '{{ item }}' + with_items: + - interface Ethernet1 + - interface Ethernet2 + - interface GigabitEthernet1 + +- name: configure policer in Scavenger class + cisco.ios.ios_config: + lines: + - conform-action transmit + - exceed-action drop + parents: + - policy-map Foo + - class Scavenger + - police cir 64000 + +- name: load new acl into device + cisco.ios.ios_config: + lines: + - 10 permit ip host 192.0.2.1 any log + - 20 permit ip host 192.0.2.2 any log + - 30 permit ip host 192.0.2.3 any log + - 40 permit ip host 192.0.2.4 any log + - 50 permit ip host 192.0.2.5 any log + parents: ip access-list extended test + before: no ip access-list extended test + match: exact + +- name: check the running-config against master config + cisco.ios.ios_config: + diff_against: intended + intended_config: "{{ lookup('file', 'master.cfg') }}" + +- name: check the startup-config against the running-config + cisco.ios.ios_config: + diff_against: startup + diff_ignore_lines: + - ntp clock .* + +- name: save running to startup when modified + cisco.ios.ios_config: + save_when: modified + +- name: for idempotency, use full-form commands + cisco.ios.ios_config: + lines: + # - shut + - shutdown + # parents: int gig1/0/11 + parents: interface GigabitEthernet1/0/11 + +# Set boot image based on comparison to a group_var (version) and the version +# that is returned from the `ios_facts` module +- name: SETTING BOOT IMAGE + cisco.ios.ios_config: + lines: + - no boot system + - boot system flash bootflash:{{new_image}} + host: '{{ inventory_hostname }}' + when: ansible_net_version != version +- name: render a Jinja2 template onto an IOS device + cisco.ios.ios_config: + backup: yes + src: ios_template.j2 + +- name: configurable backup path + cisco.ios.ios_config: + src: ios_template.j2 + backup: yes + backup_options: + filename: backup.cfg + dir_path: /home/user +""" +RETURN = """ +updates: + description: The set of commands that will be pushed to the remote device + returned: always + type: list + sample: ['hostname foo', 'router ospf 1', 'router-id 192.0.2.1'] +commands: + description: The set of commands that will be pushed to the remote device + returned: always + type: list + sample: ['hostname foo', 'router ospf 1', 'router-id 192.0.2.1'] +backup_path: + description: The full path to the backup file + returned: when backup is yes + type: str + sample: /playbooks/ansible/backup/ios_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: ios_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/ios_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 json +from ansible.module_utils._text import to_text +from ansible.module_utils.connection import ConnectionError +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + run_commands, + get_config, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_defaults_flag, + get_connection, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( + NetworkConfig, + dumps, +) + + +def check_args(module, warnings): + if module.params["multiline_delimiter"]: + if len(module.params["multiline_delimiter"]) != 1: + module.fail_json( + msg="multiline_delimiter value can only be a single character" + ) + + +def edit_config_or_macro(connection, commands): + # only catch the macro configuration command, + # not negated 'no' variation. + if commands[0].startswith("macro name"): + connection.edit_macro(candidate=commands) + else: + connection.edit_config(candidate=commands) + + +def get_candidate_config(module): + candidate = "" + if module.params["src"]: + candidate = module.params["src"] + elif module.params["lines"]: + candidate_obj = NetworkConfig(indent=1) + parents = module.params["parents"] or list() + candidate_obj.add(module.params["lines"], parents=parents) + candidate = dumps(candidate_obj, "raw") + return candidate + + +def get_running_config(module, current_config=None, flags=None): + running = module.params["running_config"] + if not running: + if not module.params["defaults"] and current_config: + running = current_config + else: + running = get_config(module, flags=flags) + return running + + +def save_config(module, result): + result["changed"] = True + if not module.check_mode: + run_commands(module, "copy running-config startup-config\r") + else: + module.warn( + "Skipping command `copy running-config startup-config` due to check_mode. Configuration not copied to non-volatile storage" + ) + + +def main(): + """ main entry point for module execution + """ + backup_spec = dict(filename=dict(), dir_path=dict(type="path")) + argument_spec = dict( + src=dict(type="str"), + lines=dict(aliases=["commands"], type="list", elements="str"), + parents=dict(type="list", elements="str"), + before=dict(type="list", elements="str"), + after=dict(type="list", elements="str"), + match=dict( + default="line", choices=["line", "strict", "exact", "none"] + ), + replace=dict(default="line", choices=["line", "block"]), + multiline_delimiter=dict(default="@"), + running_config=dict(aliases=["config"]), + intended_config=dict(), + defaults=dict(type="bool", default=False), + backup=dict(type="bool", default=False), + backup_options=dict(type="dict", options=backup_spec), + save_when=dict( + choices=["always", "never", "modified", "changed"], default="never" + ), + diff_against=dict(choices=["startup", "intended", "running"]), + diff_ignore_lines=dict(type="list", elements="str"), + ) + argument_spec.update(ios_argument_spec) + mutually_exclusive = [("lines", "src"), ("parents", "src")] + required_if = [ + ("match", "strict", ["lines"]), + ("match", "exact", ["lines"]), + ("replace", "block", ["lines"]), + ("diff_against", "intended", ["intended_config"]), + ] + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + required_if=required_if, + supports_check_mode=True, + ) + result = {"changed": False} + warnings = list() + check_args(module, warnings) + result["warnings"] = warnings + diff_ignore_lines = module.params["diff_ignore_lines"] + config = None + contents = None + flags = get_defaults_flag(module) if module.params["defaults"] else [] + connection = get_connection(module) + if ( + module.params["backup"] + or module._diff + and module.params["diff_against"] == "running" + ): + contents = get_config(module, flags=flags) + config = NetworkConfig(indent=1, contents=contents) + if module.params["backup"]: + result["__backup__"] = contents + if any((module.params["lines"], module.params["src"])): + 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" + warnings.append(msg) + match = module.params["match"] + replace = module.params["replace"] + path = module.params["parents"] + candidate = get_candidate_config(module) + running = get_running_config(module, contents, flags=flags) + try: + response = connection.get_diff( + candidate=candidate, + running=running, + diff_match=match, + diff_ignore_lines=diff_ignore_lines, + path=path, + diff_replace=replace, + ) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + config_diff = response["config_diff"] + banner_diff = response["banner_diff"] + if config_diff or banner_diff: + commands = config_diff.split("\n") + if module.params["before"]: + commands[:0] = module.params["before"] + if module.params["after"]: + commands.extend(module.params["after"]) + result["commands"] = commands + result["updates"] = commands + result["banners"] = banner_diff + + # send the configuration commands to the device and merge + # them with the current running config + if not module.check_mode: + if commands: + edit_config_or_macro(connection, commands) + if banner_diff: + connection.edit_banner( + candidate=json.dumps(banner_diff), + multiline_delimiter=module.params[ + "multiline_delimiter" + ], + ) + result["changed"] = True + running_config = module.params["running_config"] + startup_config = None + if module.params["save_when"] == "always": + save_config(module, result) + elif module.params["save_when"] == "modified": + output = run_commands( + module, ["show running-config", "show startup-config"] + ) + running_config = NetworkConfig( + indent=1, contents=output[0], ignore_lines=diff_ignore_lines + ) + startup_config = NetworkConfig( + indent=1, contents=output[1], ignore_lines=diff_ignore_lines + ) + if running_config.sha1 != startup_config.sha1: + save_config(module, result) + elif module.params["save_when"] == "changed" and result["changed"]: + save_config(module, result) + if module._diff: + if not running_config: + output = run_commands(module, "show running-config") + contents = output[0] + else: + contents = running_config + + # recreate the object in order to process diff_ignore_lines + running_config = NetworkConfig( + indent=1, contents=contents, ignore_lines=diff_ignore_lines + ) + if module.params["diff_against"] == "running": + if module.check_mode: + module.warn( + "unable to perform diff against running-config due to check mode" + ) + contents = None + else: + contents = config.config_text + elif module.params["diff_against"] == "startup": + if not startup_config: + output = run_commands(module, "show startup-config") + contents = output[0] + else: + contents = startup_config.config_text + elif module.params["diff_against"] == "intended": + contents = module.params["intended_config"] + if contents is not None: + base_config = NetworkConfig( + indent=1, contents=contents, ignore_lines=diff_ignore_lines + ) + if running_config.sha1 != base_config.sha1: + if module.params["diff_against"] == "intended": + before = running_config + after = base_config + elif module.params["diff_against"] in ("startup", "running"): + before = base_config + after = running_config + result.update( + { + "changed": True, + "diff": {"before": str(before), "after": str(after)}, + } + ) + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_facts.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_facts.py new file mode 100644 index 00000000..e66d9048 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_facts.py @@ -0,0 +1,236 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_facts +author: +- Peter Sprygada (@privateip) +- Sumit Jaiswal (@justjais) +short_description: Collect facts from remote devices running Cisco IOS +description: +- Collects a base set of device facts from a remote device that is running IOS. This + module prepends all of the base network fact keys with C(ansible_net_<fact>). 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 +extends_documentation_fragment: +- cisco.ios.ios +notes: +- Tested against IOS 15.6 +options: + gather_subset: + description: + - When supplied, this argument restricts the facts collected to a given subset. + - Possible values for this argument include C(all), C(min), C(hardware), C(config), + and C(interfaces). + - Specify a list of values to include a larger subset. + - Use a value with an initial C(!) to collect all facts except that subset. + required: false + default: '!config' + 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, + vlans etc. Can specify a list of values to include a larger subset. Values can + also be used with an initial C(M(!)) to specify that a specific subset should + not be collected. Valid subsets are 'all', 'interfaces', 'l2_interfaces', 'vlans', + 'lag_interfaces', 'lacp', 'lacp_interfaces', 'lldp_global', 'lldp_interfaces', + 'l3_interfaces', 'acl_interfaces', 'static_routes', 'acls'. + type: list + elements: str +""" +EXAMPLES = """ +- name: Gather all legacy facts + cisco.ios.ios_facts: + gather_subset: all + +- name: Gather only the config and default facts + cisco.ios.ios_facts: + gather_subset: + - config + +- name: Do not gather hardware facts + cisco.ios.ios_facts: + gather_subset: + - '!hardware' + +- name: Gather legacy and resource facts + cisco.ios.ios_facts: + gather_subset: all + gather_network_resources: all + +- name: Gather only the interfaces resource facts and no legacy facts + cisco.ios.ios_facts: + gather_subset: + - '!all' + - '!min' + gather_network_resources: + - interfaces + +- name: Gather interfaces resource and minimal legacy facts + cisco.ios.ios_facts: + gather_subset: min + gather_network_resources: interfaces + +- name: Gather L2 interfaces resource and minimal legacy facts + cisco.ios.ios_facts: + gather_subset: min + gather_network_resources: l2_interfaces + +- name: Gather L3 interfaces resource and minimal legacy facts + cisco.ios.ios_facts: + gather_subset: min + gather_network_resources: l3_interfaces +""" +RETURN = """ +ansible_net_gather_subset: + description: The list of fact subsets collected from the device + returned: always + type: list + +ansible_net_gather_network_resources: + description: The list of fact for network resource subsets collected from the device + returned: when the resource is configured + type: list + +# default +ansible_net_model: + description: The model name returned from the device + returned: always + type: str +ansible_net_serialnum: + description: The serial number of the remote device + returned: always + type: str +ansible_net_version: + description: The operating system version running on the remote device + returned: always + type: str +ansible_net_iostype: + description: The operating system type (IOS or IOS-XE) running on the remote device + returned: always + type: str +ansible_net_hostname: + description: The configured hostname of the device + returned: always + type: str +ansible_net_image: + description: The image file the device is running + returned: always + type: str +ansible_net_stacked_models: + description: The model names of each device in the stack + returned: when multiple devices are configured in a stack + type: list +ansible_net_stacked_serialnums: + description: The serial numbers of each device in the stack + returned: when multiple devices are configured in a stack + 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 + +# hardware +ansible_net_filesystems: + description: All file system names available on the device + returned: when hardware is configured + type: list +ansible_net_filesystems_info: + description: A hash of all file systems containing info about each file system (e.g. free and total space) + returned: when hardware is configured + type: dict +ansible_net_memfree_mb: + description: The available free memory on the remote device in Mb + returned: when hardware is configured + type: int +ansible_net_memtotal_mb: + description: The total memory on the remote device in Mb + returned: when hardware is configured + type: int + +# config +ansible_net_config: + description: The current active config from the device + returned: when config is configured + type: str + +# interfaces +ansible_net_all_ipv4_addresses: + description: All IPv4 addresses configured on the device + returned: when interfaces is configured + type: list +ansible_net_all_ipv6_addresses: + description: All IPv6 addresses configured on the device + returned: when interfaces is configured + type: list +ansible_net_interfaces: + description: A hash of all interfaces running on the system + returned: when interfaces is configured + type: dict +ansible_net_neighbors: + description: + - The list of CDP and LLDP neighbors from the remote device. If both, + CDP and LLDP neighbor data is present on one port, CDP is preferred. + returned: when interfaces is configured + type: dict +""" +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.facts.facts import ( + FactsArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) + + +def main(): + """ + Main entry point for module execution + + :returns: ansible_facts + """ + argument_spec = FactsArgs.argument_spec + argument_spec.update(ios_argument_spec) + module = AnsibleModule( + argument_spec=argument_spec, supports_check_mode=True + ) + warnings = [] + if module.params["gather_subset"] == "!config": + warnings.append( + "default value for `gather_subset` will be changed to `min` from `!config` v2.11 onwards" + ) + result = Facts(module).get_facts() + ansible_facts, additional_warnings = result + warnings.extend(additional_warnings) + module.exit_json(ansible_facts=ansible_facts, warnings=warnings) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_interface.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_interface.py new file mode 100644 index 00000000..a1e540bc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_interface.py @@ -0,0 +1,597 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_interface +author: Ganesh Nalawade (@ganeshrn) +short_description: (deprecated, removed after 2022-06-01) Manage Interface on Cisco + IOS network devices +description: +- This module provides declarative management of Interfaces on Cisco IOS network devices. +version_added: 1.0.0 +deprecated: + alternative: ios_interfaces + why: Newer and updated modules released with more functionality in Ansible 2.9 + removed_at_date: '2022-06-01' +notes: +- Tested against IOS 15.6 +options: + name: + description: + - Name of the Interface. + type: str + description: + description: + - Description of Interface. + type: str + enabled: + description: + - Interface link status. + default: True + type: bool + speed: + description: + - Interface link speed. + type: str + mtu: + description: + - Maximum size of transmit packet. + type: str + duplex: + description: + - Interface link status + type: str + choices: + - full + - half + - auto + tx_rate: + description: + - Transmit rate in bits per second (bps). + - This is state check parameter only. + - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) + type: str + rx_rate: + description: + - Receiver rate in bits per second (bps). + - This is state check parameter only. + - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) + type: str + neighbors: + description: + - Check the operational state of given interface C(name) for CDP/LLDP neighbor. + - The following suboptions are available. + type: list + elements: dict + suboptions: + host: + description: + - CDP/LLDP neighbor host for given interface C(name). + type: str + port: + description: + - CDP/LLDP neighbor port to which given interface C(name) is connected. + type: str + aggregate: + description: List of Interfaces definitions. + type: list + elements: dict + suboptions: + name: + description: + - Name of the Interface. + required: true + type: str + description: + description: + - Description of Interface. + type: str + enabled: + description: + - Interface link status. + type: bool + speed: + description: + - Interface link speed. + type: str + mtu: + description: + - Maximum size of transmit packet. + type: str + duplex: + description: + - Interface link status + choices: + - full + - half + - auto + type: str + tx_rate: + description: + - Transmit rate in bits per second (bps). + - This is state check parameter only. + - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) + type: str + rx_rate: + description: + - Receiver rate in bits per second (bps). + - This is state check parameter only. + - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) + type: str + neighbors: + description: + - Check the operational state of given interface C(name) for CDP/LLDP neighbor. + - The following suboptions are available. + type: list + elements: dict + suboptions: + host: + description: + - CDP/LLDP neighbor host for given interface C(name). + type: str + port: + description: + - CDP/LLDP neighbor port to which given interface C(name) is connected. + type: str + delay: + description: + - Time in seconds to wait before checking for the operational state on remote + device. This wait is applicable for operational state argument which are I(state) + with values C(up)/C(down), I(tx_rate) and I(rx_rate). + type: int + state: + description: + - State of the Interface configuration, C(up) means present and operationally + up and C(down) means present and operationally C(down) + choices: + - present + - absent + - up + - down + type: str + delay: + description: + - Time in seconds to wait before checking for the operational state on remote + device. This wait is applicable for operational state argument which are I(state) + with values C(up)/C(down), I(tx_rate) and I(rx_rate). + default: 10 + type: int + state: + description: + - State of the Interface configuration, C(up) means present and operationally + up and C(down) means present and operationally C(down) + default: present + choices: + - present + - absent + - up + - down + type: str +extends_documentation_fragment: +- cisco.ios.ios + + +""" +EXAMPLES = """ +- name: configure interface + cisco.ios.ios_interface: + name: GigabitEthernet0/2 + description: test-interface + speed: 100 + duplex: half + mtu: 512 + +- name: remove interface + cisco.ios.ios_interface: + name: Loopback9 + state: absent + +- name: make interface up + cisco.ios.ios_interface: + name: GigabitEthernet0/2 + enabled: true + +- name: make interface down + cisco.ios.ios_interface: + name: GigabitEthernet0/2 + enabled: false + +- name: Check intent arguments + cisco.ios.ios_interface: + name: GigabitEthernet0/2 + state: up + tx_rate: ge(0) + rx_rate: le(0) + +- name: Check neighbors intent arguments + cisco.ios.ios_interface: + name: Gi0/0 + neighbors: + - port: eth0 + host: netdev + +- name: Config + intent + cisco.ios.ios_interface: + name: GigabitEthernet0/2 + enabled: false + state: down + +- name: Add interface using aggregate + cisco.ios.ios_interface: + aggregate: + - {name: GigabitEthernet0/1, mtu: 256, description: test-interface-1} + - {name: GigabitEthernet0/2, mtu: 516, description: test-interface-2} + duplex: full + speed: 100 + state: present + +- name: Delete interface using aggregate + cisco.ios.ios_interface: + aggregate: + - name: Loopback9 + - name: Loopback10 + state: absent +""" +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device. + returned: always, except for the platforms that use Netconf transport to manage the device. + type: list + sample: + - interface GigabitEthernet0/2 + - description test-interface + - duplex half + - mtu 512 +""" +import re +from copy import deepcopy +from time import sleep +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import exec_command +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) +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 ( + conditional, + remove_default_spec, +) + + +def validate_mtu(value, module): + if value and not 64 <= int(value) <= 9600: + module.fail_json(msg="mtu must be between 64 and 9600") + + +def validate_param_values(module, obj, param=None): + if param is None: + param = module.params + for key in obj: + # validate the param value (if validator func exists) + validator = globals().get("validate_%s" % key) + if callable(validator): + validator(param.get(key), module) + + +def parse_shutdown(configobj, name): + cfg = configobj["interface %s" % name] + cfg = "\n".join(cfg.children) + match = re.search("^shutdown", cfg, re.M) + if match: + return True + else: + return False + + +def parse_config_argument(configobj, name, arg=None): + cfg = configobj["interface %s" % name] + cfg = "\n".join(cfg.children) + match = re.search("%s (.+)$" % arg, cfg, re.M) + if match: + return match.group(1) + + +def search_obj_in_list(name, lst): + for o in lst: + if o["name"] == name: + return o + return None + + +def add_command_to_interface(interface, cmd, commands): + if interface not in commands: + commands.append(interface) + commands.append(cmd) + + +def map_config_to_obj(module): + config = get_config(module) + configobj = NetworkConfig(indent=1, contents=config) + match = re.findall("^interface (\\S+)", config, re.M) + if not match: + return list() + instances = list() + for item in set(match): + obj = { + "name": item, + "description": parse_config_argument( + configobj, item, "description" + ), + "speed": parse_config_argument(configobj, item, "speed"), + "duplex": parse_config_argument(configobj, item, "duplex"), + "mtu": parse_config_argument(configobj, item, "mtu"), + "disable": True if parse_shutdown(configobj, item) else False, + "state": "present", + } + instances.append(obj) + return instances + + +def map_params_to_obj(module): + obj = [] + aggregate = module.params.get("aggregate") + if aggregate: + for item in aggregate: + for key in item: + if item.get(key) is None: + item[key] = module.params[key] + validate_param_values(module, item, item) + d = item.copy() + if d["enabled"]: + d["disable"] = False + else: + d["disable"] = True + obj.append(d) + else: + params = { + "name": module.params["name"], + "description": module.params["description"], + "speed": module.params["speed"], + "mtu": module.params["mtu"], + "duplex": module.params["duplex"], + "state": module.params["state"], + "delay": module.params["delay"], + "tx_rate": module.params["tx_rate"], + "rx_rate": module.params["rx_rate"], + "neighbors": module.params["neighbors"], + } + validate_param_values(module, params) + if module.params["enabled"]: + params.update({"disable": False}) + else: + params.update({"disable": True}) + obj.append(params) + return obj + + +def map_obj_to_commands(updates): + commands = list() + want, have = updates + args = "speed", "description", "duplex", "mtu" + for w in want: + name = w["name"] + disable = w["disable"] + state = w["state"] + obj_in_have = search_obj_in_list(name, have) + interface = "interface " + name + if state == "absent" and obj_in_have: + commands.append("no " + interface) + elif state in ("present", "up", "down"): + if obj_in_have: + for item in args: + candidate = w.get(item) + running = obj_in_have.get(item) + if candidate != running: + if candidate: + cmd = item + " " + str(candidate) + add_command_to_interface(interface, cmd, commands) + if disable and not obj_in_have.get("disable", False): + add_command_to_interface(interface, "shutdown", commands) + elif not disable and obj_in_have.get("disable", False): + add_command_to_interface( + interface, "no shutdown", commands + ) + else: + commands.append(interface) + for item in args: + value = w.get(item) + if value: + commands.append(item + " " + str(value)) + if disable: + commands.append("no shutdown") + return commands + + +def check_declarative_intent_params(module, want, result): + failed_conditions = [] + have_neighbors_lldp = None + have_neighbors_cdp = None + for w in want: + want_state = w.get("state") + want_tx_rate = w.get("tx_rate") + want_rx_rate = w.get("rx_rate") + want_neighbors = w.get("neighbors") + if ( + want_state not in ("up", "down") + and not want_tx_rate + and not want_rx_rate + and not want_neighbors + ): + continue + if result["changed"]: + sleep(w["delay"]) + command = "show interfaces %s" % w["name"] + rc, out, err = exec_command(module, command) + if rc != 0: + module.fail_json( + msg=to_text(err, errors="surrogate_then_replace"), + command=command, + rc=rc, + ) + if want_state in ("up", "down"): + match = re.search("%s (\\w+)" % "line protocol is", out, re.M) + have_state = None + if match: + have_state = match.group(1) + if have_state is None or not conditional( + want_state, have_state.strip() + ): + failed_conditions.append("state " + "eq(%s)" % want_state) + if want_tx_rate: + match = re.search("%s (\\d+)" % "output rate", out, re.M) + have_tx_rate = None + if match: + have_tx_rate = match.group(1) + if have_tx_rate is None or not conditional( + want_tx_rate, have_tx_rate.strip(), cast=int + ): + failed_conditions.append("tx_rate " + want_tx_rate) + if want_rx_rate: + match = re.search("%s (\\d+)" % "input rate", out, re.M) + have_rx_rate = None + if match: + have_rx_rate = match.group(1) + if have_rx_rate is None or not conditional( + want_rx_rate, have_rx_rate.strip(), cast=int + ): + failed_conditions.append("rx_rate " + want_rx_rate) + if want_neighbors: + have_host = [] + have_port = [] + + # Process LLDP neighbors + if have_neighbors_lldp is None: + rc, have_neighbors_lldp, err = exec_command( + module, "show lldp neighbors detail" + ) + if rc != 0: + module.fail_json( + msg=to_text(err, errors="surrogate_then_replace"), + command=command, + rc=rc, + ) + if have_neighbors_lldp: + lines = have_neighbors_lldp.strip().split("Local Intf: ") + for line in lines: + field = line.split("\n") + if field[0].strip() == w["name"]: + for item in field: + if item.startswith("System Name:"): + have_host.append(item.split(":")[1].strip()) + if item.startswith("Port Description:"): + have_port.append(item.split(":")[1].strip()) + # Process CDP neighbors + if have_neighbors_cdp is None: + rc, have_neighbors_cdp, err = exec_command( + module, "show cdp neighbors detail" + ) + if rc != 0: + module.fail_json( + msg=to_text(err, errors="surrogate_then_replace"), + command=command, + rc=rc, + ) + if have_neighbors_cdp: + neighbors_cdp = re.findall( + """Device ID: (.*?) +.*?Interface: (.*?), Port ID .outgoing port.: (.*?) +""", + have_neighbors_cdp, + re.S, + ) + for host, localif, remoteif in neighbors_cdp: + if localif == w["name"]: + have_host.append(host) + have_port.append(remoteif) + for item in want_neighbors: + host = item.get("host") + port = item.get("port") + if host and host not in have_host: + failed_conditions.append("host " + host) + if port and port not in have_port: + failed_conditions.append("port " + port) + return failed_conditions + + +def main(): + """ main entry point for module execution + """ + neighbors_spec = dict(host=dict(), port=dict()) + element_spec = dict( + name=dict(), + description=dict(), + speed=dict(), + mtu=dict(), + duplex=dict(choices=["full", "half", "auto"]), + enabled=dict(default=True, type="bool"), + tx_rate=dict(), + rx_rate=dict(), + neighbors=dict(type="list", elements="dict", options=neighbors_spec), + delay=dict(default=10, type="int"), + state=dict( + default="present", choices=["present", "absent", "up", "down"] + ), + ) + aggregate_spec = deepcopy(element_spec) + aggregate_spec["name"] = dict(required=True) + + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + argument_spec = dict( + aggregate=dict(type="list", elements="dict", options=aggregate_spec) + ) + argument_spec.update(element_spec) + argument_spec.update(ios_argument_spec) + required_one_of = [["name", "aggregate"]] + mutually_exclusive = [["name", "aggregate"]] + module = AnsibleModule( + argument_spec=argument_spec, + required_one_of=required_one_of, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + warnings = list() + result = {"changed": False} + if warnings: + result["warnings"] = warnings + want = map_params_to_obj(module) + have = map_config_to_obj(module) + commands = map_obj_to_commands((want, have)) + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + failed_conditions = check_declarative_intent_params(module, want, result) + if failed_conditions: + msg = "One or more conditional statements have not been satisfied" + module.fail_json(msg=msg, failed_conditions=failed_conditions) + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_interfaces.py new file mode 100644 index 00000000..aeb1561d --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_interfaces.py @@ -0,0 +1,568 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +""" +The module file for ios_interfaces +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_interfaces +short_description: Interfaces resource module +description: This module manages the interface attributes of Cisco IOS network devices. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL +options: + config: + description: A dictionary of interface options + type: list + elements: dict + suboptions: + name: + description: + - Full name of interface, e.g. GigabitEthernet0/2, loopback999. + type: str + required: true + description: + description: + - Interface description. + type: str + enabled: + 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 + default: true + speed: + description: + - Interface link speed. Applicable for Ethernet interfaces only. + type: str + mtu: + description: + - MTU for a specific interface. Applicable for Ethernet interfaces only. + - Refer to vendor documentation for valid values. + type: int + duplex: + description: + - Interface link status. Applicable for Ethernet interfaces only, either in + half duplex, full duplex or in automatic state which negotiates the duplex + automatically. + type: str + choices: + - full + - half + - auto + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by + executing the command B(show running-config | section ^interface). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# no ip address +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description This is test +# no ip address +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# no ip address +# duplex auto +# speed auto + +- name: Merge provided configuration with device configuration + cisco.ios.ios_interfaces: + config: + - name: GigabitEthernet0/2 + description: Configured and Merged by Ansible Network + enabled: true + - name: GigabitEthernet0/3 + description: Configured and Merged by Ansible Network + mtu: 2800 + enabled: false + speed: 100 + duplex: full + state: merged + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# no ip address +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description Configured and Merged by Ansible Network +# no ip address +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# description Configured and Merged by Ansible Network +# mtu 2800 +# no ip address +# shutdown +# duplex full +# speed 100 + +# Using replaced + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# no ip address +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description Configured by Ansible Network +# no ip address +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# mtu 2000 +# no ip address +# shutdown +# duplex full +# speed 100 + +- name: Replaces device configuration of listed interfaces with provided configuration + cisco.ios.ios_interfaces: + config: + - name: GigabitEthernet0/3 + description: Configured and Replaced by Ansible Network + enabled: false + duplex: auto + mtu: 2500 + speed: 1000 + state: replaced + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# no ip address +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description Configured by Ansible Network +# no ip address +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# description Configured and Replaced by Ansible Network +# mtu 2500 +# no ip address +# shutdown +# duplex full +# speed 1000 + +# Using overridden + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface# +# interface GigabitEthernet0/1 +# description Configured by Ansible +# no ip address +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description This is test +# no ip address +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# description Configured by Ansible +# mtu 2800 +# no ip address +# shutdown +# duplex full +# speed 100 + +- name: Override device configuration of all interfaces with provided configuration + cisco.ios.ios_interfaces: + config: + - name: GigabitEthernet0/2 + description: Configured and Overridden by Ansible Network + speed: 1000 + - name: GigabitEthernet0/3 + description: Configured and Overridden by Ansible Network + enabled: false + duplex: full + mtu: 2000 + state: overridden + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# no ip address +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description Configured and Overridden by Ansible Network +# no ip address +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# description Configured and Overridden by Ansible Network +# mtu 2000 +# no ip address +# shutdown +# duplex full +# speed 100 + +# Using Deleted + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# no ip address +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description Configured by Ansible Network +# no ip address +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# description Configured by Ansible Network +# mtu 2500 +# no ip address +# shutdown +# duplex full +# speed 1000 + +- name: "Delete module attributes of given interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_interfaces: + config: + - name: GigabitEthernet0/2 + state: deleted + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# no ip address +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# no ip address +# duplex auto +# speed auto +# interface GigabitEthernet0/3 +# description Configured by Ansible Network +# mtu 2500 +# no ip address +# shutdown +# duplex full +# speed 1000 + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured resource module attributes from each configured interface)" + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# no ip address +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description Configured by Ansible Network +# no ip address +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# description Configured by Ansible Network +# mtu 2500 +# no ip address +# shutdown +# duplex full +# speed 1000 + +- name: "Delete module attributes of all interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_interfaces: + state: deleted + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# no ip address +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# no ip address +# duplex auto +# speed auto +# interface GigabitEthernet0/3 +# no ip address +# duplex auto +# speed auto + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh running-config | section ^interface +# interface GigabitEthernet0/1 +# description this is interface1 +# mtu 65 +# duplex auto +# speed 10 +# interface GigabitEthernet0/2 +# description this is interface2 +# mtu 110 +# shutdown +# duplex auto +# speed 100 + +- name: Gather listed interfaces with provided configurations + cisco.ios.ios_interfaces: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "description": "this is interface1", +# "duplex": "auto", +# "enabled": true, +# "mtu": 65, +# "name": "GigabitEthernet0/1", +# "speed": "10" +# }, +# { +# "description": "this is interface2", +# "duplex": "auto", +# "enabled": false, +# "mtu": 110, +# "name": "GigabitEthernet0/2", +# "speed": "100" +# } +# ] + +# After state: +# ------------ +# +# vios#sh running-config | section ^interface +# interface GigabitEthernet0/1 +# description this is interface1 +# mtu 65 +# duplex auto +# speed 10 +# interface GigabitEthernet0/2 +# description this is interface2 +# mtu 110 +# shutdown +# duplex auto +# speed 100 + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_interfaces: + config: + - name: GigabitEthernet0/1 + description: Configured by Ansible-Network + mtu: 110 + enabled: true + duplex: half + - name: GigabitEthernet0/2 + description: Configured by Ansible-Network + mtu: 2800 + enabled: false + speed: 100 + duplex: full + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "interface GigabitEthernet0/1", +# "description Configured by Ansible-Network", +# "mtu 110", +# "duplex half", +# "no shutdown", +# "interface GigabitEthernet0/2", +# "description Configured by Ansible-Network", +# "mtu 2800", +# "speed 100", +# "duplex full", +# "shutdown" + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# interface GigabitEthernet0/1 +# description interfaces 0/1 +# mtu 110 +# duplex half +# no shutdown +# interface GigabitEthernet0/2 +# description interfaces 0/2 +# mtu 2800 +# speed 100 +# duplex full +# shutdown + +- name: Parse the commands for provided configuration + cisco.ios.ios_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "description": "interfaces 0/1", +# "duplex": "half", +# "enabled": true, +# "mtu": 110, +# "name": "GigabitEthernet0/1" +# }, +# { +# "description": "interfaces 0/2", +# "duplex": "full", +# "enabled": true, +# "mtu": 2800, +# "name": "GigabitEthernet0/2", +# "speed": "100" +# } +# ] + +""" +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: ['interface GigabitEthernet 0/1', 'description This is test', 'speed 100'] +""" +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.interfaces.interfaces import ( + InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.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", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=InterfacesArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_l2_interface.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_l2_interface.py new file mode 100644 index 00000000..dfe8ca63 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_l2_interface.py @@ -0,0 +1,581 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_l2_interface +extends_documentation_fragment: +- cisco.ios.ios +short_description: (deprecated, removed after 2022-06-01) Manage Layer-2 interface + on Cisco IOS devices. +description: +- This module provides declarative management of Layer-2 interfaces on Cisco IOS devices. +version_added: 1.0.0 +deprecated: + alternative: ios_l2_interfaces + why: Newer and updated modules released with more functionality in Ansible 2.9 + removed_at_date: '2022-06-01' +author: +- Nathaniel Case (@Qalthos) +options: + name: + description: + - Full name of the interface excluding any logical unit number, i.e. GigabitEthernet0/1. + aliases: + - interface + type: str + mode: + description: + - Mode in which interface needs to be configured. + choices: + - access + - trunk + type: str + access_vlan: + description: + - Configure given VLAN in access port. If C(mode=access), used as the access VLAN + ID. + type: str + trunk_vlans: + description: + - List of VLANs to be configured in trunk port. If C(mode=trunk), used as the + VLAN range to ADD or REMOVE from the trunk. + type: str + native_vlan: + description: + - Native VLAN to be configured in trunk port. If C(mode=trunk), used as the trunk + native VLAN ID. + type: str + trunk_allowed_vlans: + description: + - List of allowed VLANs in a given trunk port. If C(mode=trunk), these are the + only VLANs that will be configured on the trunk, i.e. "2-10,15". + type: str + aggregate: + description: + - List of Layer-2 interface definitions. + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface excluding any logical unit number, i.e. GigabitEthernet0/1. + aliases: + - interface + type: str + mode: + description: + - Mode in which interface needs to be configured. + choices: + - access + - trunk + type: str + access_vlan: + description: + - Configure given VLAN in access port. If C(mode=access), used as the access VLAN + ID. + type: str + trunk_vlans: + description: + - List of VLANs to be configured in trunk port. If C(mode=trunk), used as the + VLAN range to ADD or REMOVE from the trunk. + type: str + native_vlan: + description: + - Native VLAN to be configured in trunk port. If C(mode=trunk), used as the trunk + native VLAN ID. + type: str + trunk_allowed_vlans: + description: + - List of allowed VLANs in a given trunk port. If C(mode=trunk), these are the + only VLANs that will be configured on the trunk, i.e. "2-10,15". + type: str + state: + description: + - Manage the state of the Layer-2 Interface configuration. + choices: + - present + - absent + - unconfigured + type: str + state: + description: + - Manage the state of the Layer-2 Interface configuration. + default: present + choices: + - present + - absent + - unconfigured + type: str + +""" +EXAMPLES = """ +- name: Ensure GigabitEthernet0/5 is in its default l2 interface state + ios.ios_l2_interface: + name: GigabitEthernet0/5 + state: unconfigured +- name: Ensure GigabitEthernet0/5 is configured for access vlan 20 + ios.ios_l2_interface: + name: GigabitEthernet0/5 + mode: access + access_vlan: 20 +- name: Ensure GigabitEthernet0/5 only has vlans 5-10 as trunk vlans + ios.ios_l2_interface: + name: GigabitEthernet0/5 + mode: trunk + native_vlan: 10 + trunk_allowed_vlans: 5-10 +- name: Ensure GigabitEthernet0/5 is a trunk port and ensure 2-50 are being tagged + (doesn't mean others aren't also being tagged) + ios.ios_l2_interface: + name: GigabitEthernet0/5 + mode: trunk + native_vlan: 10 + trunk_vlans: 2-50 +- name: Ensure these VLANs are not being tagged on the trunk + ios.ios_l2_interface: + name: GigabitEthernet0/5 + mode: trunk + trunk_vlans: 51-4094 + state: absent +""" +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always, except for the platforms that use Netconf transport to manage the device. + type: list + sample: + - interface GigabitEthernet0/5 + - switchport access vlan 20 +""" +import re +from copy import deepcopy +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + load_config, + run_commands, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) + + +def get_interface_type(interface): + intf_type = "unknown" + if interface.upper()[:2] in ( + "ET", + "GI", + "FA", + "TE", + "FO", + "HU", + "TWE", + "TW", + ): + intf_type = "ethernet" + elif interface.upper().startswith("VL"): + intf_type = "svi" + elif interface.upper().startswith("LO"): + intf_type = "loopback" + elif interface.upper()[:2] in ("MG", "MA"): + intf_type = "management" + elif interface.upper().startswith("PO"): + intf_type = "portchannel" + elif interface.upper().startswith("NV"): + intf_type = "nve" + return intf_type + + +def is_switchport(name, module): + intf_type = get_interface_type(name) + if intf_type in ("ethernet", "portchannel"): + config = run_commands( + module, ["show interface {0} switchport".format(name)] + )[0] + match = re.search("Switchport: Enabled", config) + return bool(match) + return False + + +def interface_is_portchannel(name, module): + if get_interface_type(name) == "ethernet": + config = run_commands(module, ["show run interface {0}".format(name)])[ + 0 + ] + if any(c in config for c in ["channel group", "channel-group"]): + return True + return False + + +def get_switchport(name, module): + config = run_commands( + module, ["show interface {0} switchport".format(name)] + )[0] + mode = re.search("Administrative Mode: (?:.* )?(\\w+)$", config, re.M) + access = re.search("Access Mode VLAN: (\\d+)", config) + native = re.search("Trunking Native Mode VLAN: (\\d+)", config) + trunk = re.search("Trunking VLANs Enabled: (.+)$", config, re.M) + if mode: + mode = mode.group(1) + if access: + access = access.group(1) + if native: + native = native.group(1) + if trunk: + trunk = trunk.group(1) + if trunk == "ALL": + trunk = "1-4094" + switchport_config = { + "interface": name, + "mode": mode, + "access_vlan": access, + "native_vlan": native, + "trunk_vlans": trunk, + } + return switchport_config + + +def remove_switchport_config_commands(name, existing, proposed, module): + mode = proposed.get("mode") + commands = [] + command = None + if mode == "access": + av_check = existing.get("access_vlan") == proposed.get("access_vlan") + if av_check: + command = "no switchport access vlan {0}".format( + existing.get("access_vlan") + ) + commands.append(command) + elif mode == "trunk": + # Supported Remove Scenarios for trunk_vlans_list + # 1) Existing: 1,2,3 Proposed: 1,2,3 - Remove all + # 2) Existing: 1,2,3 Proposed: 1,2 - Remove 1,2 Leave 3 + # 3) Existing: 1,2,3 Proposed: 2,3 - Remove 2,3 Leave 1 + # 4) Existing: 1,2,3 Proposed: 4,5,6 - None removed. + # 5) Existing: None Proposed: 1,2,3 - None removed. + existing_vlans = existing.get("trunk_vlans_list") + proposed_vlans = proposed.get("trunk_vlans_list") + vlans_to_remove = set(proposed_vlans).intersection(existing_vlans) + if vlans_to_remove: + proposed_allowed_vlans = proposed.get("trunk_allowed_vlans") + remove_trunk_allowed_vlans = proposed.get( + "trunk_vlans", proposed_allowed_vlans + ) + command = "switchport trunk allowed vlan remove {0}".format( + remove_trunk_allowed_vlans + ) + commands.append(command) + native_check = existing.get("native_vlan") == proposed.get( + "native_vlan" + ) + if native_check and proposed.get("native_vlan"): + command = "no switchport trunk native vlan {0}".format( + existing.get("native_vlan") + ) + commands.append(command) + if commands: + commands.insert(0, "interface " + name) + return commands + + +def get_switchport_config_commands(name, existing, proposed, module): + """Gets commands required to config a given switchport interface + """ + proposed_mode = proposed.get("mode") + existing_mode = existing.get("mode") + commands = [] + command = None + if proposed_mode != existing_mode: + if proposed_mode == "trunk": + command = "switchport mode trunk" + elif proposed_mode == "access": + command = "switchport mode access" + if command: + commands.append(command) + if proposed_mode == "access": + av_check = str(existing.get("access_vlan")) == str( + proposed.get("access_vlan") + ) + if not av_check: + command = "switchport access vlan {0}".format( + proposed.get("access_vlan") + ) + commands.append(command) + elif proposed_mode == "trunk": + tv_check = existing.get("trunk_vlans_list") == proposed.get( + "trunk_vlans_list" + ) + if not tv_check: + if proposed.get("allowed"): + command = "switchport trunk allowed vlan {0}".format( + proposed.get("trunk_allowed_vlans") + ) + commands.append(command) + else: + existing_vlans = existing.get("trunk_vlans_list") + proposed_vlans = proposed.get("trunk_vlans_list") + vlans_to_add = set(proposed_vlans).difference(existing_vlans) + if vlans_to_add: + command = "switchport trunk allowed vlan add {0}".format( + proposed.get("trunk_vlans") + ) + commands.append(command) + native_check = str(existing.get("native_vlan")) == str( + proposed.get("native_vlan") + ) + if not native_check and proposed.get("native_vlan"): + command = "switchport trunk native vlan {0}".format( + proposed.get("native_vlan") + ) + commands.append(command) + if commands: + commands.insert(0, "interface " + name) + return commands + + +def is_switchport_default(existing): + """Determines if switchport has a default config based on mode + Args: + existing (dict): existing switchport configuration from Ansible mod + Returns: + boolean: True if switchport has OOB Layer 2 config, i.e. + vlan 1 and trunk all and mode is access + """ + c1 = str(existing["access_vlan"]) == "1" + c2 = str(existing["native_vlan"]) == "1" + c3 = existing["trunk_vlans"] == "1-4094" + c4 = existing["mode"] == "access" + default = c1 and c2 and c3 and c4 + return default + + +def default_switchport_config(name): + commands = [] + commands.append("interface " + name) + commands.append("switchport mode access") + commands.append("switch access vlan 1") + commands.append("switchport trunk native vlan 1") + commands.append("switchport trunk allowed vlan all") + return commands + + +def vlan_range_to_list(vlans): + result = [] + if vlans: + for part in vlans.split(","): + if part.lower() == "none": + break + if part: + if "-" in part: + start, stop = (int(i) for i in part.split("-")) + result.extend(range(start, stop + 1)) + else: + result.append(int(part)) + return sorted(result) + + +def get_list_of_vlans(module): + config = run_commands(module, ["show vlan"])[0] + vlans = set() + lines = config.strip().splitlines() + for line in lines: + line_parts = line.split() + if line_parts: + try: + int(line_parts[0]) + except ValueError: + continue + vlans.add(line_parts[0]) + return list(vlans) + + +def flatten_list(commands): + flat_list = [] + for command in commands: + if isinstance(command, list): + flat_list.extend(command) + else: + flat_list.append(command) + return flat_list + + +def map_params_to_obj(module): + obj = [] + aggregate = module.params.get("aggregate") + if aggregate: + for item in aggregate: + for key in item: + if item.get(key) is None: + item[key] = module.params[key] + obj.append(item.copy()) + else: + obj.append( + { + "name": module.params["name"], + "mode": module.params["mode"], + "access_vlan": module.params["access_vlan"], + "native_vlan": module.params["native_vlan"], + "trunk_vlans": module.params["trunk_vlans"], + "trunk_allowed_vlans": module.params["trunk_allowed_vlans"], + "state": module.params["state"], + } + ) + return obj + + +def main(): + """ main entry point for module execution + """ + element_spec = dict( + name=dict(type="str", aliases=["interface"]), + mode=dict(choices=["access", "trunk"]), + access_vlan=dict(type="str"), + native_vlan=dict(type="str"), + trunk_vlans=dict(type="str"), + trunk_allowed_vlans=dict(type="str"), + state=dict( + choices=["absent", "present", "unconfigured"], default="present" + ), + ) + aggregate_spec = deepcopy(element_spec) + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + argument_spec = dict( + aggregate=dict(type="list", elements="dict", options=aggregate_spec) + ) + argument_spec.update(element_spec) + argument_spec.update(ios_argument_spec) + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=[ + ["access_vlan", "trunk_vlans"], + ["access_vlan", "native_vlan"], + ["access_vlan", "trunk_allowed_vlans"], + ], + supports_check_mode=True, + ) + warnings = list() + commands = [] + result = {"changed": False, "warnings": warnings} + want = map_params_to_obj(module) + for w in want: + name = w["name"] + mode = w["mode"] + access_vlan = w["access_vlan"] + state = w["state"] + trunk_vlans = w["trunk_vlans"] + native_vlan = w["native_vlan"] + trunk_allowed_vlans = w["trunk_allowed_vlans"] + args = dict( + name=name, + mode=mode, + access_vlan=access_vlan, + native_vlan=native_vlan, + trunk_vlans=trunk_vlans, + trunk_allowed_vlans=trunk_allowed_vlans, + ) + proposed = dict((k, v) for k, v in args.items() if v is not None) + name = name.lower() + if mode == "access" and state == "present" and not access_vlan: + module.fail_json( + msg="access_vlan param is required when mode=access && state=present" + ) + if mode == "trunk" and access_vlan: + module.fail_json( + msg="access_vlan param not supported when using mode=trunk" + ) + if not is_switchport(name, module): + module.fail_json( + msg="""Ensure interface is configured to be a L2 +port first before using this module. You can use +the ios_interface module for this.""" + ) + if interface_is_portchannel(name, module): + module.fail_json( + msg="""Cannot change L2 config on physical +port because it is in a portchannel. +You should update the portchannel config.""" + ) + # existing will never be null for Eth intfs as there is always a default + existing = get_switchport(name, module) + # Safeguard check + # If there isn't an existing, something is wrong per previous comment + if not existing: + module.fail_json( + msg="Make sure you are using the FULL interface name" + ) + if trunk_vlans or trunk_allowed_vlans: + if trunk_vlans: + trunk_vlans_list = vlan_range_to_list(trunk_vlans) + elif trunk_allowed_vlans: + trunk_vlans_list = vlan_range_to_list(trunk_allowed_vlans) + proposed["allowed"] = True + existing_trunks_list = vlan_range_to_list(existing["trunk_vlans"]) + existing["trunk_vlans_list"] = existing_trunks_list + proposed["trunk_vlans_list"] = trunk_vlans_list + current_vlans = get_list_of_vlans(module) + if state == "present": + if access_vlan and access_vlan not in current_vlans: + module.fail_json( + msg="""You are trying to configure a VLAN on an interface that +does not exist on the switch yet!""", + vlan=access_vlan, + ) + elif native_vlan and native_vlan not in current_vlans: + module.fail_json( + msg="""You are trying to configure a VLAN on an interface that +does not exist on the switch yet!""", + vlan=native_vlan, + ) + else: + command = get_switchport_config_commands( + name, existing, proposed, module + ) + commands.append(command) + elif state == "unconfigured": + is_default = is_switchport_default(existing) + if not is_default: + command = default_switchport_config(name) + commands.append(command) + elif state == "absent": + command = remove_switchport_config_commands( + name, existing, proposed, module + ) + commands.append(command) + if trunk_vlans or trunk_allowed_vlans: + existing.pop("trunk_vlans_list") + proposed.pop("trunk_vlans_list") + cmds = flatten_list(commands) + if cmds: + if module.check_mode: + module.exit_json(changed=True, commands=cmds) + else: + result["changed"] = True + load_config(module, cmds) + if "configure" in cmds: + cmds.pop(0) + result["commands"] = cmds + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_l2_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_l2_interfaces.py new file mode 100644 index 00000000..08b208d4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_l2_interfaces.py @@ -0,0 +1,560 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +""" +The module file for ios_l2_interfaces +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: ios_l2_interfaces +short_description: L2 interfaces resource module +description: This module provides declarative management of Layer-2 interface on Cisco + IOS devices. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL. +options: + config: + description: A dictionary of Layer-2 interface options + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface excluding any logical unit number, i.e. GigabitEthernet0/1. + type: str + required: true + access: + description: + - Switchport mode access command to configure the interface as a layer 2 access. + type: dict + suboptions: + vlan: + description: + - Configure given VLAN in access port. It's used as the access VLAN ID. + type: int + voice: + description: + - Switchport mode voice command to configure the interface with a voice vlan. + type: dict + suboptions: + vlan: + description: + - Configure given voice VLAN on access port. It's used as the voice VLAN + ID. + type: int + trunk: + description: + - Switchport mode trunk command to configure the interface as a Layer 2 trunk. + Note The encapsulation is always set to dot1q. + type: dict + suboptions: + allowed_vlans: + description: + - List of allowed VLANs in a given trunk port. These are the only VLANs + that will be configured on the trunk. + type: list + elements: str + native_vlan: + description: + - Native VLAN to be configured in trunk port. It's used as the trunk native + VLAN ID. + type: int + encapsulation: + description: + - Trunking encapsulation when interface is in trunking mode. + choices: + - dot1q + - isl + - negotiate + type: str + pruning_vlans: + description: + - Pruning VLAN to be configured in trunk port. It's used as the trunk + pruning VLAN ID. + type: list + elements: str + mode: + description: + - Mode in which interface needs to be configured. + - An interface whose trunk encapsulation is "Auto" can not be configured to + "trunk" mode. + type: str + choices: + - access + - trunk + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by + executing the command B(show running-config | section ^interface). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# media-type rj45 +# negotiation auto + +- name: Merge provided configuration with device configuration + cisco.ios.ios_l2_interfaces: + config: + - name: GigabitEthernet0/1 + mode: access + access: + vlan: 10 + voice: + vlan: 40 + - name: GigabitEthernet0/2 + mode: trunk + trunk: + allowed_vlans: 10-20,40 + native_vlan: 20 + pruning_vlans: 10,20 + encapsulation: dot1q + state: merged + +# After state: +# ------------ +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 10 +# switchport voice vlan 40 +# switchport mode access +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport trunk allowed vlan 10-20,40 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 20 +# switchport trunk pruning vlan 10,20 +# switchport mode trunk +# media-type rj45 +# negotiation auto + +# Using replaced + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# media-type rj45 +# negotiation auto + +- name: Replaces device configuration of listed l2 interfaces with provided configuration + cisco.ios.ios_l2_interfaces: + config: + - name: GigabitEthernet0/2 + trunk: + allowed_vlans: 20-25,40 + native_vlan: 20 + pruning_vlans: 10 + encapsulation: isl + state: replaced + +# After state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport trunk allowed vlan 20-25,40 +# switchport trunk encapsulation isl +# switchport trunk native vlan 20 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + +# Using overridden + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 20 +# media-type rj45 +# negotiation auto + +- name: Override device configuration of all l2 interfaces with provided configuration + cisco.ios.ios_l2_interfaces: + config: + - name: GigabitEthernet0/2 + access: + vlan: 20 + voice: + vlan: 40 + state: overridden + +# After state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport voice vlan 40 +# media-type rj45 +# negotiation auto + +# Using Deleted + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk allowed vlan 20-40,60,80 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 10 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + +- name: Delete IOS L2 interfaces as in given arguments + cisco.ios.ios_l2_interfaces: + config: + - name: GigabitEthernet0/1 + state: deleted + +# After state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk allowed vlan 20-40,60,80 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 10 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured resource module attributes from each configured interface)" + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk allowed vlan 20-40,60,80 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 10 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + +- name: Delete IOS L2 interfaces as in given arguments + cisco.ios.ios_l2_interfaces: + state: deleted + +# After state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# media-type rj45 +# negotiation auto + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh running-config | section ^interface +# interface GigabitEthernet0/1 +# switchport access vlan 10 +# interface GigabitEthernet0/2 +# switchport trunk allowed vlan 10-20,40 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 10 +# switchport trunk pruning vlan 10,20 +# switchport mode trunk + +- name: Gather listed l2 interfaces with provided configurations + cisco.ios.ios_l2_interfaces: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "name": "GigabitEthernet0/0" +# }, +# { +# "access": { +# "vlan": 10 +# }, +# "name": "GigabitEthernet0/1" +# }, +# { +# "mode": "trunk", +# "name": "GigabitEthernet0/2", +# "trunk": { +# "allowed_vlans": [ +# "10-20", +# "40" +# ], +# "encapsulation": "dot1q", +# "native_vlan": 10, +# "pruning_vlans": [ +# "10", +# "20" +# ] +# } +# } +# ] + +# After state: +# ------------ +# +# vios#sh running-config | section ^interface +# interface GigabitEthernet0/1 +# switchport access vlan 10 +# interface GigabitEthernet0/2 +# switchport trunk allowed vlan 10-20,40 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 10 +# switchport trunk pruning vlan 10,20 +# switchport mode trunk + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_l2_interfaces: + config: + - name: GigabitEthernet0/1 + access: + vlan: 30 + - name: GigabitEthernet0/2 + trunk: + allowed_vlans: 10-20,40 + native_vlan: 20 + pruning_vlans: 10,20 + encapsulation: dot1q + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "interface GigabitEthernet0/1", +# "switchport access vlan 30", +# "interface GigabitEthernet0/2", +# "switchport trunk encapsulation dot1q", +# "switchport trunk native vlan 20", +# "switchport trunk allowed vlan 10-20,40", +# "switchport trunk pruning vlan 10,20" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# interface GigabitEthernet0/1 +# switchport mode access +# switchport access vlan 30 +# interface GigabitEthernet0/2 +# switchport trunk allowed vlan 15-20,40 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 20 +# switchport trunk pruning vlan 10,20 + +- name: Parse the commands for provided configuration + cisco.ios.ios_l2_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "access": { +# "vlan": 30 +# }, +# "mode": "access", +# "name": "GigabitEthernet0/1" +# }, +# { +# "name": "GigabitEthernet0/2", +# "trunk": { +# "allowed_vlans": [ +# "15-20", +# "40" +# ], +# "encapsulation": "dot1q", +# "native_vlan": 20, +# "pruning_vlans": [ +# "10", +# "20" +# ] +# } +# } +# ] + +""" +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: ['interface GigabitEthernet0/1', 'switchport access vlan 20'] +""" +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.l2_interfaces.l2_interfaces import ( + L2_InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.l2_interfaces.l2_interfaces import ( + L2_Interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=L2_InterfacesArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = L2_Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_l3_interface.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_l3_interface.py new file mode 100644 index 00000000..2a2b10a5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_l3_interface.py @@ -0,0 +1,382 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_l3_interface +author: Ganesh Nalawade (@ganeshrn) +short_description: (deprecated, removed after 2022-06-01) Manage Layer-3 interfaces + on Cisco IOS network devices. +description: +- This module provides declarative management of Layer-3 interfaces on IOS network + devices. +version_added: 1.0.0 +deprecated: + alternative: ios_l3_interfaces + why: Newer and updated modules released with more functionality in Ansible 2.9 + removed_at_date: '2022-06-01' +notes: +- Tested against IOS 15.2 +options: + name: + description: + - Name of the Layer-3 interface to be configured eg. GigabitEthernet0/2 + type: str + ipv4: + description: + - IPv4 address to be set for the Layer-3 interface mentioned in I(name) option. + The address format is <ipv4 address>/<mask>, the mask is number in range 0-32 + eg. 192.168.0.1/24 + type: str + ipv6: + description: + - IPv6 address to be set for the Layer-3 interface mentioned in I(name) option. + The address format is <ipv6 address>/<mask>, the mask is number in range 0-128 + eg. fd5d:12c9:2201:1::1/64 + type: str + aggregate: + description: + - List of Layer-3 interfaces definitions. Each of the entry in aggregate list + should define name of interface C(name) and a optional C(ipv4) or C(ipv6) address. + type: list + elements: dict + suboptions: + name: + description: + - Name of the Layer-3 interface to be configured eg. GigabitEthernet0/2 + type: str + required: true + ipv4: + description: + - IPv4 address to be set for the Layer-3 interface mentioned in I(name) option. + The address format is <ipv4 address>/<mask>, the mask is number in range 0-32 + eg. 192.168.0.1/24 + type: str + ipv6: + description: + - IPv6 address to be set for the Layer-3 interface mentioned in I(name) option. + The address format is <ipv6 address>/<mask>, the mask is number in range 0-128 + eg. fd5d:12c9:2201:1::1/64 + type: str + state: + description: + - State of the Layer-3 interface configuration. It indicates if the configuration + should be present or absent on remote device. + type: str + choices: + - present + - absent + state: + description: + - State of the Layer-3 interface configuration. It indicates if the configuration + should be present or absent on remote device. + default: present + choices: + - present + - absent + type: str +extends_documentation_fragment: +- cisco.ios.ios + + +""" +EXAMPLES = """ +- name: Remove GigabitEthernet0/3 IPv4 and IPv6 address + cisco.ios.ios_l3_interface: + name: GigabitEthernet0/3 + state: absent +- name: Set GigabitEthernet0/3 IPv4 address + cisco.ios.ios_l3_interface: + name: GigabitEthernet0/3 + ipv4: 192.168.0.1/24 +- name: Set GigabitEthernet0/3 IPv6 address + cisco.ios.ios_l3_interface: + name: GigabitEthernet0/3 + ipv6: fd5d:12c9:2201:1::1/64 +- name: Set GigabitEthernet0/3 in dhcp + cisco.ios.ios_l3_interface: + name: GigabitEthernet0/3 + ipv4: dhcp + ipv6: dhcp +- name: Set interface Vlan1 (SVI) IPv4 address + cisco.ios.ios_l3_interface: + name: Vlan1 + ipv4: 192.168.0.5/24 +- name: Set IP addresses on aggregate + cisco.ios.ios_l3_interface: + aggregate: + - name: GigabitEthernet0/3 + ipv4: 192.168.2.10/24 + - name: GigabitEthernet0/3 + ipv4: 192.168.3.10/24 + ipv6: fd5d:12c9:2201:1::1/64 +- name: Remove IP addresses on aggregate + cisco.ios.ios_l3_interface: + aggregate: + - name: GigabitEthernet0/3 + ipv4: 192.168.2.10/24 + - name: GigabitEthernet0/3 + ipv4: 192.168.3.10/24 + ipv6: fd5d:12c9:2201:1::1/64 + state: absent +""" +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always, except for the platforms that use Netconf transport to manage the device. + type: list + sample: + - interface GigabitEthernet0/2 + - ip address 192.168.0.1 255.255.255.0 + - ipv6 address fd5d:12c9:2201:1::1/64 +""" +import re +from copy import deepcopy +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) +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 ( + remove_default_spec, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + is_netmask, + is_masklen, + to_netmask, + to_masklen, +) + + +def validate_ipv4(value, module): + if value: + address = value.split("/") + if len(address) != 2: + module.fail_json( + msg="address format is <ipv4 address>/<mask>, got invalid format %s" + % value + ) + if not is_masklen(address[1]): + module.fail_json( + msg="invalid value for mask: %s, mask should be in range 0-32" + % address[1] + ) + + +def validate_ipv6(value, module): + if value: + address = value.split("/") + if len(address) != 2: + module.fail_json( + msg="address format is <ipv6 address>/<mask>, got invalid format %s" + % value + ) + elif not 0 <= int(address[1]) <= 128: + module.fail_json( + msg="invalid value for mask: %s, mask should be in range 0-128" + % address[1] + ) + + +def validate_param_values(module, obj, param=None): + if param is None: + param = module.params + for key in obj: + # validate the param value (if validator func exists) + validator = globals().get("validate_%s" % key) + if callable(validator): + validator(param.get(key), module) + + +def parse_config_argument(configobj, name, arg=None): + cfg = configobj["interface %s" % name] + cfg = "\n".join(cfg.children) + values = [] + matches = re.finditer("%s (.+)$" % arg, cfg, re.M) + for match in matches: + match_str = match.group(1).strip() + if arg == "ipv6 address": + values.append(match_str) + else: + values = match_str + break + return values or None + + +def search_obj_in_list(name, lst): + for o in lst: + if o["name"] == name: + return o + return None + + +def map_obj_to_commands(updates, module): + commands = list() + want, have = updates + for w in want: + name = w["name"] + ipv4 = w["ipv4"] + ipv6 = w["ipv6"] + state = w["state"] + interface = "interface " + name + commands.append(interface) + obj_in_have = search_obj_in_list(name, have) + if state == "absent" and obj_in_have: + if obj_in_have["ipv4"]: + if ipv4: + address = ipv4.split("/") + if len(address) == 2: + ipv4 = "{0} {1}".format( + address[0], to_netmask(address[1]) + ) + commands.append("no ip address {0}".format(ipv4)) + else: + commands.append("no ip address") + if obj_in_have["ipv6"]: + if ipv6: + commands.append("no ipv6 address {0}".format(ipv6)) + else: + commands.append("no ipv6 address") + if "dhcp" in obj_in_have["ipv6"]: + commands.append("no ipv6 address dhcp") + elif state == "present": + if ipv4: + if ( + obj_in_have is None + or obj_in_have.get("ipv4") is None + or ipv4 != obj_in_have["ipv4"] + ): + address = ipv4.split("/") + if len(address) == 2: + ipv4 = "{0} {1}".format( + address[0], to_netmask(address[1]) + ) + commands.append("ip address {0}".format(ipv4)) + if ipv6: + if ( + obj_in_have is None + or obj_in_have.get("ipv6") is None + or ipv6.lower() + not in [addr.lower() for addr in obj_in_have["ipv6"]] + ): + commands.append("ipv6 address {0}".format(ipv6)) + if commands[-1] == interface: + commands.pop(-1) + return commands + + +def map_config_to_obj(module): + config = get_config(module) + configobj = NetworkConfig(indent=1, contents=config) + match = re.findall("^interface (\\S+)", config, re.M) + if not match: + return list() + instances = list() + for item in set(match): + ipv4 = parse_config_argument(configobj, item, "ip address") + if ipv4: + address = ipv4.strip().split(" ") + if len(address) == 2 and is_netmask(address[1]): + ipv4 = "{0}/{1}".format( + address[0], to_text(to_masklen(address[1])) + ) + obj = { + "name": item, + "ipv4": ipv4, + "ipv6": parse_config_argument(configobj, item, "ipv6 address"), + "state": "present", + } + instances.append(obj) + return instances + + +def map_params_to_obj(module): + obj = [] + aggregate = module.params.get("aggregate") + if aggregate: + for item in aggregate: + for key in item: + if item.get(key) is None: + item[key] = module.params[key] + validate_param_values(module, item, item) + obj.append(item.copy()) + else: + obj.append( + { + "name": module.params["name"], + "ipv4": module.params["ipv4"], + "ipv6": module.params["ipv6"], + "state": module.params["state"], + } + ) + validate_param_values(module, obj) + return obj + + +def main(): + """ main entry point for module execution + """ + element_spec = dict( + name=dict(), + ipv4=dict(), + ipv6=dict(), + state=dict(default="present", choices=["present", "absent"]), + ) + aggregate_spec = deepcopy(element_spec) + aggregate_spec["name"] = dict(required=True) + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + argument_spec = dict( + aggregate=dict(type="list", elements="dict", options=aggregate_spec) + ) + argument_spec.update(element_spec) + argument_spec.update(ios_argument_spec) + required_one_of = [["name", "aggregate"]] + mutually_exclusive = [["name", "aggregate"]] + module = AnsibleModule( + argument_spec=argument_spec, + required_one_of=required_one_of, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + warnings = list() + result = {"changed": False} + want = map_params_to_obj(module) + have = map_config_to_obj(module) + commands = map_obj_to_commands((want, have), module) + result["commands"] = commands + if commands: + if not module.check_mode: + resp = load_config(module, commands) + warnings.extend(out for out in resp if out) + result["changed"] = True + if warnings: + result["warnings"] = warnings + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_l3_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_l3_interfaces.py new file mode 100644 index 00000000..0ea1bb3a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_l3_interfaces.py @@ -0,0 +1,605 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +""" +The module file for ios_l3_interfaces +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: ios_l3_interfaces +short_description: L3 interfaces resource module +description: +- This module provides declarative management of Layer-3 interface on Cisco IOS devices. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL. +options: + config: + description: A dictionary of Layer-3 interface options + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface excluding any logical unit number, i.e. GigabitEthernet0/1. + type: str + required: true + ipv4: + description: + - IPv4 address to be set for the Layer-3 interface mentioned in I(name) option. + The address format is <ipv4 address>/<mask>, the mask is number in range + 0-32 eg. 192.168.0.1/24. + type: list + elements: dict + suboptions: + address: + description: + - Configures the IPv4 address for Interface. + type: str + secondary: + description: + - Configures the IP address as a secondary address. + type: bool + dhcp_client: + description: + - Configures and specifies client-id to use over DHCP ip. Note, This option + shall work only when dhcp is configured as IP. + - GigabitEthernet interface number + type: int + dhcp_hostname: + description: + - Configures and specifies value for hostname option over DHCP ip. Note, + This option shall work only when dhcp is configured as IP. + type: str + ipv6: + description: + - IPv6 address to be set for the Layer-3 interface mentioned in I(name) option. + - The address format is <ipv6 address>/<mask>, the mask is number in range + 0-128 eg. fd5d:12c9:2201:1::1/64 + type: list + elements: dict + suboptions: + address: + description: + - Configures the IPv6 address for Interface. + 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 IOS device + by executing the command B(show running-config | section ^interface). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + 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 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 running-config | section ^interface) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + +""" + +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# ip address 10.1.1.1 255.255.255.0 +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description This is test +# no ip address +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# description Configured by Ansible Network +# no ip address +# interface GigabitEthernet0/3.100 +# encapsulation dot1Q 20 + +- name: Merge provided configuration with device configuration + cisco.ios.ios_l3_interfaces: + config: + - name: GigabitEthernet0/1 + ipv4: + - address: 192.168.0.1/24 + secondary: true + - name: GigabitEthernet0/2 + ipv4: + - address: 192.168.0.2/24 + - name: GigabitEthernet0/3 + ipv6: + - address: fd5d:12c9:2201:1::1/64 + - name: GigabitEthernet0/3.100 + ipv4: + - address: 192.168.0.3/24 + state: merged + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# ip address 10.1.1.1 255.255.255.0 +# ip address 192.168.0.1 255.255.255.0 secondary +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description This is test +# ip address 192.168.0.2 255.255.255.0 +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# description Configured by Ansible Network +# ipv6 address FD5D:12C9:2201:1::1/64 +# interface GigabitEthernet0/3.100 +# encapsulation dot1Q 20 +# ip address 192.168.0.3 255.255.255.0 + +# Using replaced +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# ip address 10.1.1.1 255.255.255.0 +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description This is test +# no ip address +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# description Configured by Ansible Network +# ip address 192.168.2.0 255.255.255.0 +# interface GigabitEthernet0/3.100 +# encapsulation dot1Q 20 +# ip address 192.168.0.2 255.255.255.0 + +- name: Replaces device configuration of listed interfaces with provided configuration + cisco.ios.ios_l3_interfaces: + config: + - name: GigabitEthernet0/2 + ipv4: + - address: 192.168.2.0/24 + - name: GigabitEthernet0/3 + ipv4: + - address: dhcp + dhcp_client: 2 + dhcp_hostname: test.com + - name: GigabitEthernet0/3.100 + ipv4: + - address: 192.168.0.3/24 + secondary: true + state: replaced + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# ip address 10.1.1.1 255.255.255.0 +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description This is test +# ip address 192.168.2.1 255.255.255.0 +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# description Configured by Ansible Network +# ip address dhcp client-id GigabitEthernet0/2 hostname test.com +# interface GigabitEthernet0/3.100 +# encapsulation dot1Q 20 +# ip address 192.168.0.2 255.255.255.0 +# ip address 192.168.0.3 255.255.255.0 secondary + +# Using overridden +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# ip address 10.1.1.1 255.255.255.0 +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description This is test +# ip address 192.168.2.1 255.255.255.0 +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# description Configured by Ansible Network +# ipv6 address FD5D:12C9:2201:1::1/64 +# interface GigabitEthernet0/3.100 +# encapsulation dot1Q 20 +# ip address 192.168.0.2 255.255.255.0 + +- name: Override device configuration of all interfaces with provided configuration + cisco.ios.ios_l3_interfaces: + config: + - name: GigabitEthernet0/2 + ipv4: + - address: 192.168.0.1/24 + - name: GigabitEthernet0/3.100 + ipv6: + - address: autoconfig + state: overridden + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# no ip address +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description This is test +# ip address 192.168.0.1 255.255.255.0 +# duplex auto +# speed 1000 +# interface GigabitEthernet0/3 +# description Configured by Ansible Network +# interface GigabitEthernet0/3.100 +# encapsulation dot1Q 20 +# ipv6 address autoconfig + +# Using Deleted +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# ip address 192.0.2.10 255.255.255.0 +# shutdown +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description Configured by Ansible Network +# ip address 192.168.1.0 255.255.255.0 +# interface GigabitEthernet0/3 +# description Configured by Ansible Network +# ip address 192.168.0.1 255.255.255.0 +# shutdown +# duplex full +# speed 10 +# ipv6 address FD5D:12C9:2201:1::1/64 +# interface GigabitEthernet0/3.100 +# encapsulation dot1Q 20 +# ip address 192.168.0.2 255.255.255.0 + +- name: "Delete attributes of given interfaces (NOTE: This won't delete the interface sitself)" + cisco.ios.ios_l3_interfaces: + config: + - name: GigabitEthernet0/2 + - name: GigabitEthernet0/3.100 + state: deleted + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# no ip address +# shutdown +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description Configured by Ansible Network +# no ip address +# interface GigabitEthernet0/3 +# description Configured by Ansible Network +# ip address 192.168.0.1 255.255.255.0 +# shutdown +# duplex full +# speed 10 +# ipv6 address FD5D:12C9:2201:1::1/64 +# interface GigabitEthernet0/3.100 +# encapsulation dot1Q 20 + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured L3 resource module attributes from each configured interface)" + +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# ip address 192.0.2.10 255.255.255.0 +# shutdown +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description Configured by Ansible Network +# ip address 192.168.1.0 255.255.255.0 +# interface GigabitEthernet0/3 +# description Configured by Ansible Network +# ip address 192.168.0.1 255.255.255.0 +# shutdown +# duplex full +# speed 10 +# ipv6 address FD5D:12C9:2201:1::1/64 +# interface GigabitEthernet0/3.100 +# encapsulation dot1Q 20 +# ip address 192.168.0.2 255.255.255.0 + +- name: "Delete L3 attributes of ALL interfaces together (NOTE: This won't delete the interface itself)" + cisco.ios.ios_l3_interfaces: + state: deleted + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet0/1 +# no ip address +# shutdown +# duplex auto +# speed auto +# interface GigabitEthernet0/2 +# description Configured by Ansible Network +# no ip address +# interface GigabitEthernet0/3 +# description Configured by Ansible Network +# shutdown +# duplex full +# speed 10 +# interface GigabitEthernet0/3.100 +# encapsulation dot1Q 20 + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh running-config | section ^interface +# interface GigabitEthernet0/1 +# ip address 203.0.113.27 255.255.255.0 +# interface GigabitEthernet0/2 +# ip address 192.0.2.1 255.255.255.0 secondary +# ip address 192.0.2.2 255.255.255.0 +# ipv6 address 2001:DB8:0:3::/64 + +- name: Gather listed l3 interfaces with provided configurations + cisco.ios.ios_l3_interfaces: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "ipv4": [ +# { +# "address": "203.0.113.27 255.255.255.0" +# } +# ], +# "name": "GigabitEthernet0/1" +# }, +# { +# "ipv4": [ +# { +# "address": "192.0.2.1 255.255.255.0", +# "secondary": true +# }, +# { +# "address": "192.0.2.2 255.255.255.0" +# } +# ], +# "ipv6": [ +# { +# "address": "2001:db8:0:3::/64" +# } +# ], +# "name": "GigabitEthernet0/2" +# } +# ] + +# After state: +# ------------ +# +# vios#sh running-config | section ^interface +# interface GigabitEthernet0/1 +# ip address 203.0.113.27 255.255.255.0 +# interface GigabitEthernet0/2 +# ip address 192.0.2.1 255.255.255.0 secondary +# ip address 192.0.2.2 255.255.255.0 +# ipv6 address 2001:DB8:0:3::/64 + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_l3_interfaces: + config: + - name: GigabitEthernet0/1 + ipv4: + - address: dhcp + dhcp_client: 0 + dhcp_hostname: test.com + - name: GigabitEthernet0/2 + ipv4: + - address: 198.51.100.1/24 + secondary: true + - address: 198.51.100.2/24 + ipv6: + - address: 2001:db8:0:3::/64 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "interface GigabitEthernet0/1", +# "ip address dhcp client-id GigabitEthernet 0/0 hostname test.com", +# "interface GigabitEthernet0/2", +# "ip address 198.51.100.1 255.255.255.0 secondary", +# "ip address 198.51.100.2 255.255.255.0", +# "ipv6 address 2001:db8:0:3::/64" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# interface GigabitEthernet0/1 +# ip address dhcp client-id +# GigabitEthernet 0/0 hostname test.com +# interface GigabitEthernet0/2 +# ip address 198.51.100.1 255.255.255.0 +# secondary ip address 198.51.100.2 255.255.255.0 +# ipv6 address 2001:db8:0:3::/64 + +- name: Parse the commands for provided configuration + cisco.ios.ios_l3_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "ipv4": [ +# { +# "address": "dhcp", +# "dhcp_client": 0, +# "dhcp_hostname": "test.com" +# } +# ], +# "name": "GigabitEthernet0/1" +# }, +# { +# "ipv4": [ +# { +# "address": "198.51.100.1 255.255.255.0", +# "secondary": true +# }, +# { +# "address": "198.51.100.2 255.255.255.0" +# } +# ], +# "ipv6": [ +# { +# "address": "2001:db8:0:3::/64" +# } +# ], +# "name": "GigabitEthernet0/2" +# } +# ] + +""" + +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: ['interface GigabitEthernet0/1', 'ip address 192.168.0.2 255.255.255.0'] +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.l3_interfaces.l3_interfaces import ( + L3_InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.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", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=L3_InterfacesArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = L3_Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lacp.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lacp.py new file mode 100644 index 00000000..71884ab0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lacp.py @@ -0,0 +1,273 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +""" +The module file for ios_lacp +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: ios_lacp +short_description: LACP resource module +description: This module provides declarative management of Global LACP on Cisco IOS + network devices. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL. +options: + config: + description: The provided configurations. + type: dict + suboptions: + system: + description: This option sets the default system parameters for LACP. + type: dict + suboptions: + priority: + description: + - LACP priority for the system. + - Refer to vendor documentation for valid values. + type: int + required: true + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by + executing the command B(show lacp sys-id). + - 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 + - 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - deleted + - rendered + - parsed + - gathered + default: merged +""" + +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# vios#show lacp sys-id +# 32768, 5e00.0000.8000 + +- name: Merge provided configuration with device configuration + cisco.ios.ios_lacp: + config: + system: + priority: 123 + state: merged + +# After state: +# ------------ +# +# vios#show lacp sys-id +# 123, 5e00.0000.8000 + +# Using replaced +# +# Before state: +# ------------- +# +# vios#show lacp sys-id +# 500, 5e00.0000.8000 + +- name: Replaces Global LACP configuration + cisco.ios.ios_lacp: + config: + system: + priority: 123 + state: replaced + +# After state: +# ------------ +# +# vios#show lacp sys-id +# 123, 5e00.0000.8000 + +# Using Deleted +# +# Before state: +# ------------- +# +# vios#show lacp sys-id +# 500, 5e00.0000.8000 + +- name: Delete Global LACP attribute + cisco.ios.ios_lacp: + state: deleted + +# After state: +# ------------- +# +# vios#show lacp sys-id +# 32768, 5e00.0000.8000 + +# Using Gathered + +# Before state: +# ------------- +# +# vios#show lacp sys-id +# 123, 5e00.0000.8000 + +- name: Gather listed LACP with provided configurations + cisco.ios.ios_lacp: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": { +# "system": { +# "priority": 500 +# } +# } + +# After state: +# ------------ +# +# vios#show lacp sys-id +# 123, 5e00.0000.8000 + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_lacp: + config: + system: + priority: 123 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "lacp system-priority 10" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# lacp system-priority 123 + +- name: Parse the commands for provided configuration + cisco.ios.ios_lacp: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": { +# "system": { +# "priority": 123 +# } +# } + +""" + +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: ['lacp system-priority 10'] +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lacp.lacp import ( + LacpArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.lacp.lacp import ( + Lacp, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=LacpArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Lacp(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lacp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lacp_interfaces.py new file mode 100644 index 00000000..7bf4092a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lacp_interfaces.py @@ -0,0 +1,508 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +""" +The module file for ios_lacp_interfaces +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: ios_lacp_interfaces +short_description: LACP interfaces resource module +description: This module provides declarative management of LACP on Cisco IOS network + devices lacp_interfaces. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL. +options: + config: + description: A dictionary of LACP lacp_interfaces option + type: list + elements: dict + suboptions: + name: + description: + - Name of the Interface for configuring LACP. + type: str + required: true + port_priority: + description: + - LACP priority on this interface. + - Refer to vendor documentation for valid port values. + type: int + fast_switchover: + description: + - LACP fast switchover supported on this port channel. + type: bool + max_bundle: + description: + - LACP maximum number of ports to bundle in this port channel. + - Refer to vendor documentation for valid port 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 IOS device by + executing the command B(show running-config | section ^interface). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + description: + - The state the configuration should be left in + - 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + default: merged +""" + +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# interface GigabitEthernet0/3 +# shutdown + +- name: Merge provided configuration with device configuration + cisco.ios.ios_lacp_interfaces: + config: + - name: GigabitEthernet0/1 + port_priority: 10 + - name: GigabitEthernet0/2 + port_priority: 20 + - name: GigabitEthernet0/3 + port_priority: 30 + - name: Port-channel10 + fast_switchover: true + max_bundle: 5 + state: merged + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# lacp fast-switchover +# lacp max-bundle 5 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 30 + +# Using overridden +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# lacp fast-switchover +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 30 + +- name: Override device configuration of all lacp_interfaces with provided configuration + cisco.ios.ios_lacp_interfaces: + config: + - name: GigabitEthernet0/1 + port_priority: 20 + - name: Port-channel10 + max_bundle: 2 + state: overridden + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# lacp max-bundle 2 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/2 +# shutdown +# interface GigabitEthernet0/3 +# shutdown + +# Using replaced +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# lacp max-bundle 5 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 30 + +- name: Replaces device configuration of listed lacp_interfaces with provided configuration + cisco.ios.ios_lacp_interfaces: + config: + - name: GigabitEthernet0/3 + port_priority: 40 + - name: Port-channel10 + fast_switchover: true + max_bundle: 2 + state: replaced + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# lacp fast-switchover +# lacp max-bundle 2 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 40 + +# Using Deleted +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# flowcontrol receive on +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 30 + +- name: "Delete LACP attributes of given interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_lacp_interfaces: + config: + - name: GigabitEthernet0/1 + state: deleted + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 30 + +# Using Deleted without any config passed +# "(NOTE: This will delete all of configured LLDP module attributes)" +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# lacp fast-switchover +# interface Port-channel20 +# lacp max-bundle 2 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 30 + +- name: "Delete LACP attributes for all configured interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_lacp_interfaces: + state: deleted + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# interface GigabitEthernet0/3 +# shutdown + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh running-config | section ^interface +# interface Port-channel10 +# lacp fast-switchover +# lacp max-bundle 2 +# interface Port-channel40 +# lacp max-bundle 5 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# lacp port-priority 30 +# interface GigabitEthernet0/2 +# lacp port-priority 20 + +- name: Gather listed LACP interfaces with provided configurations + cisco.ios.ios_lacp_interfaces: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "fast_switchover": true, +# "max_bundle": 2, +# "name": "Port-channel10" +# }, +# { +# "max_bundle": 5, +# "name": "Port-channel40" +# }, +# { +# "name": "GigabitEthernet0/0" +# }, +# { +# "name": "GigabitEthernet0/1", +# "port_priority": 30 +# }, +# { +# "name": "GigabitEthernet0/2", +# "port_priority": 20 +# } +# ] + +# After state: +# ------------ +# +# vios#sh running-config | section ^interface +# interface Port-channel10 +# lacp fast-switchover +# lacp max-bundle 2 +# interface Port-channel40 +# lacp max-bundle 5 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# lacp port-priority 30 +# interface GigabitEthernet0/2 +# lacp port-priority 20 + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_lacp_interfaces: + config: + - name: GigabitEthernet0/1 + port_priority: 10 + - name: GigabitEthernet0/2 + port_priority: 20 + - name: Port-channel10 + fast_switchover: true + max_bundle: 2 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "interface GigabitEthernet0/1", +# "lacp port-priority 10", +# "interface GigabitEthernet0/2", +# "lacp port-priority 20", +# "interface Port-channel10", +# "lacp max-bundle 2", +# "lacp fast-switchover" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# interface GigabitEthernet0/1 +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# lacp port-priority 20 +# interface Port-channel10 +# lacp max-bundle 2 fast-switchover + +- name: Parse the commands for provided configuration + cisco.ios.ios_lacp_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "name": "GigabitEthernet0/1", +# "port_priority": 10 +# }, +# { +# "name": "GigabitEthernet0/2", +# "port_priority": 20 +# }, +# { +# "fast_switchover": true, +# "max_bundle": 2, +# "name": "Port-channel10" +# } +# ] + +""" +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: ['interface GigabitEthernet 0/1', 'lacp port-priority 30'] +""" +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lacp_interfaces.lacp_interfaces import ( + Lacp_InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.lacp_interfaces.lacp_interfaces import ( + Lacp_Interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=Lacp_InterfacesArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Lacp_Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lag_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lag_interfaces.py new file mode 100644 index 00000000..679925ea --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lag_interfaces.py @@ -0,0 +1,534 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +""" +The module file for ios_l3_interfaces +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_lag_interfaces +short_description: LAG interfaces resource module +description: This module manages properties of Link Aggregation Group on Cisco IOS + devices. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL. +options: + config: + description: A list of link aggregation group configurations. + type: list + elements: dict + suboptions: + name: + description: + - ID of Ethernet Channel of interfaces. + - Refer to vendor documentation for valid port values. + type: str + required: true + members: + description: + - Interface options for the link aggregation group. + type: list + elements: dict + suboptions: + member: + description: + - Interface member of the link aggregation group. + type: str + mode: + description: + - Etherchannel Mode of the interface for link aggregation. + - On mode has to be quoted as 'on' or else pyyaml will convert + to True before it gets to Ansible. + type: str + required: true + choices: + - auto + - 'on' + - desirable + - active + - passive + link: + description: + - Assign a link identifier used for load-balancing. + - Refer to vendor documentation for valid values. + - NOTE, parameter only supported on Cisco IOS XE platform. + 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 IOS device + by executing the command B(show running-config | section ^interface). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + description: + - The state the configuration should be left in + - 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - parsed + - gathered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# interface GigabitEthernet0/3 +# shutdown +# interface GigabitEthernet0/4 +# shutdown + +- name: Merge provided configuration with device configuration + cisco.ios.ios_lag_interfaces: + config: + - name: 10 + members: + - member: GigabitEthernet0/1 + mode: auto + - member: GigabitEthernet0/2 + mode: auto + - name: 20 + members: + - member: GigabitEthernet0/3 + mode: on + - name: 30 + members: + - member: GigabitEthernet0/4 + mode: active + state: merged + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode on +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +# Using overridden +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode on +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +- name: Override device configuration of all interfaces with provided configuration + cisco.ios.ios_lag_interfaces: + config: + - name: 20 + members: + - member: GigabitEthernet0/2 + mode: auto + - member: GigabitEthernet0/3 + mode: auto + state: overridden + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# channel-group 20 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode auto +# interface GigabitEthernet0/4 +# shutdown + +# Using replaced +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode on +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +- name: Replaces device configuration of listed interfaces with provided configuration + cisco.ios.ios_lag_interfaces: + config: + - name: 40 + members: + - member: GigabitEthernet0/3 + mode: auto + state: replaced + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface Port-channel40 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 40 mode on +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +# Using Deleted +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode on +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +- name: "Delete LAG attributes of given interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_lag_interfaces: + config: + - name: 10 + - name: 20 + state: deleted + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# interface GigabitEthernet0/3 +# shutdown +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured LLDP module attributes)" + +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode on +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +- name: "Delete all configured LAG attributes for interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_lag_interfaces: + state: deleted + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# interface GigabitEthernet0/3 +# shutdown +# interface GigabitEthernet0/4 +# shutdown + +# Using Gathered + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel11 +# interface Port-channel22 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 11 mode active +# interface GigabitEthernet0/2 +# shutdown +# channel-group 22 mode active + +- name: Gather listed LAG interfaces with provided configurations + cisco.ios.ios_lag_interfaces: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "members": [ +# { +# "member": "GigabitEthernet0/1", +# "mode": "active" +# } +# ], +# "name": "Port-channel11" +# }, +# { +# "members": [ +# { +# "member": "GigabitEthernet0/2", +# "mode": "active" +# } +# ], +# "name": "Port-channel22" +# } +# ] + +# After state: +# ------------ +# +# vios#sh running-config | section ^interface +# interface Port-channel11 +# interface Port-channel22 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 11 mode active +# interface GigabitEthernet0/2 +# shutdown +# channel-group 22 mode active + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_lag_interfaces: + config: + - name: Port-channel11 + members: + - member: GigabitEthernet0/1 + mode: active + - name: Port-channel22 + members: + - member: GigabitEthernet0/2 + mode: passive + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "interface GigabitEthernet0/1", +# "channel-group 11 mode active", +# "interface GigabitEthernet0/2", +# "channel-group 22 mode passive", +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# interface GigabitEthernet0/1 +# channel-group 11 mode active +# interface GigabitEthernet0/2 +# channel-group 22 mode passive + +- name: Parse the commands for provided configuration + cisco.ios.ios_lag_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "members": [ +# { +# "member": "GigabitEthernet0/1", +# "mode": "active" +# } +# ], +# "name": "Port-channel11" +# }, +# { +# "members": [ +# { +# "member": "GigabitEthernet0/2", +# "mode": "passive" +# } +# ], +# "name": "Port-channel22" +# } +# ] + +""" +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: ['interface GigabitEthernet0/1', 'channel-group 1 mode active'] +""" +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lag_interfaces.lag_interfaces import ( + Lag_interfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.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", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=Lag_interfacesArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Lag_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_linkagg.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_linkagg.py new file mode 100644 index 00000000..ebf934d8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_linkagg.py @@ -0,0 +1,363 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = r""" +module: ios_linkagg +author: Trishna Guha (@trishnaguha) +short_description: Manage link aggregation groups on Cisco IOS network devices +description: +- This module provides declarative management of link aggregation groups on Cisco + IOS network devices. +version_added: 1.0.0 +notes: +- Tested against IOS 15.2 +options: + group: + description: + - Channel-group number for the port-channel Link aggregation group. Range 1-255. + type: int + mode: + description: + - Mode of the link aggregation group. + - On mode has to be quoted as 'on' or else pyyaml will convert + to True before it gets to Ansible. + choices: + - active + - 'on' + - passive + - auto + - desirable + type: str + members: + description: + - List of members of the link aggregation group. + type: list + elements: str + aggregate: + description: List of link aggregation definitions. + type: list + elements: dict + suboptions: + group: + description: + - Channel-group number for the port-channel Link aggregation group. Range 1-255. + type: str + required: true + mode: + description: + - Mode of the link aggregation group. + - On mode has to be quoted as 'on' or else pyyaml will convert + to True before it gets to Ansible. + choices: + - active + - 'on' + - passive + - auto + - desirable + type: str + members: + description: + - List of members of the link aggregation group. + type: list + elements: str + state: + description: + - State of the link aggregation group. + choices: + - present + - absent + type: str + state: + description: + - State of the link aggregation group. + default: present + choices: + - present + - absent + type: str + purge: + description: + - Purge links not defined in the I(aggregate) parameter. + default: false + type: bool +extends_documentation_fragment: +- cisco.ios.ios +""" +EXAMPLES = """ +- name: create link aggregation group + cisco.ios.ios_linkagg: + group: 10 + state: present + +- name: delete link aggregation group + cisco.ios.ios_linkagg: + group: 10 + state: absent + +- name: set link aggregation group to members + cisco.ios.ios_linkagg: + group: 200 + mode: active + members: + - GigabitEthernet0/0 + - GigabitEthernet0/1 + +- name: remove link aggregation group from GigabitEthernet0/0 + cisco.ios.ios_linkagg: + group: 200 + mode: active + members: + - GigabitEthernet0/1 + +- name: Create aggregate of linkagg definitions + cisco.ios.ios_linkagg: + aggregate: + - {group: 3, mode: on, members: [GigabitEthernet0/1]} + - {group: 100, mode: passive, members: [GigabitEthernet0/2]} +""" +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always, except for the platforms that use Netconf transport to manage the device. + type: list + sample: + - interface port-channel 30 + - interface GigabitEthernet0/3 + - channel-group 30 mode on + - no interface port-channel 30 +""" +import re +from copy import deepcopy +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( + CustomNetworkConfig, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) + + +def search_obj_in_list(group, lst): + for o in lst: + if o["group"] == group: + return o + + +def map_obj_to_commands(updates, module): + commands = list() + want, have = updates + purge = module.params["purge"] + for w in want: + group = w["group"] + mode = w["mode"] + members = w.get("members") or [] + state = w["state"] + del w["state"] + obj_in_have = search_obj_in_list(group, have) + if state == "absent": + if obj_in_have: + commands.append("no interface port-channel {0}".format(group)) + elif state == "present": + cmd = ["interface port-channel {0}".format(group), "end"] + if not obj_in_have: + if not group: + module.fail_json(msg="group is a required option") + commands.extend(cmd) + if members: + for m in members: + commands.append("interface {0}".format(m)) + commands.append( + "channel-group {0} mode {1}".format(group, mode) + ) + elif members: + if "members" not in obj_in_have.keys(): + for m in members: + commands.extend(cmd) + commands.append("interface {0}".format(m)) + commands.append( + "channel-group {0} mode {1}".format(group, mode) + ) + elif set(members) != set(obj_in_have["members"]): + missing_members = list( + set(members) - set(obj_in_have["members"]) + ) + for m in missing_members: + commands.extend(cmd) + commands.append("interface {0}".format(m)) + commands.append( + "channel-group {0} mode {1}".format(group, mode) + ) + superfluous_members = list( + set(obj_in_have["members"]) - set(members) + ) + for m in superfluous_members: + commands.extend(cmd) + commands.append("interface {0}".format(m)) + commands.append( + "no channel-group {0} mode {1}".format(group, mode) + ) + if purge: + for h in have: + obj_in_want = search_obj_in_list(h["group"], want) + if not obj_in_want: + commands.append( + "no interface port-channel {0}".format(h["group"]) + ) + return commands + + +def map_params_to_obj(module): + obj = [] + aggregate = module.params.get("aggregate") + if aggregate: + for item in aggregate: + for key in item: + if item.get(key) is None: + item[key] = module.params[key] + d = item.copy() + d["group"] = str(d["group"]) + obj.append(d) + else: + obj.append( + { + "group": str(module.params["group"]), + "mode": module.params["mode"], + "members": module.params["members"], + "state": module.params["state"], + } + ) + return obj + + +def parse_mode(module, config, group, member): + mode = None + netcfg = CustomNetworkConfig(indent=1, contents=config) + parents = ["interface {0}".format(member)] + body = netcfg.get_section(parents) + match_int = re.findall("interface {0}\\n".format(member), body, re.M) + if match_int: + match = re.search( + "channel-group {0} mode (\\S+)".format(group), body, re.M + ) + if match: + mode = match.group(1) + return mode + + +def parse_members(module, config, group): + members = [] + for line in config.strip().split("!"): + l = line.strip() + if l.startswith("interface"): + match_group = re.findall( + "channel-group {0} mode".format(group), l, re.M + ) + if match_group: + match = re.search("interface (\\S+)", l, re.M) + if match: + members.append(match.group(1)) + return members + + +def get_channel(module, config, group): + match = re.findall("^interface (\\S+)", config, re.M) + if not match: + return {} + channel = {} + for item in set(match): + member = item + channel["mode"] = parse_mode(module, config, group, member) + channel["members"] = parse_members(module, config, group) + return channel + + +def map_config_to_obj(module): + objs = list() + config = get_config(module) + for line in config.split("\n"): + l = line.strip() + match = re.search("interface Port-channel(\\S+)", l, re.M) + if match: + obj = {} + group = match.group(1) + obj["group"] = group + obj.update(get_channel(module, config, group)) + objs.append(obj) + return objs + + +def main(): + """ main entry point for module execution + """ + element_spec = dict( + group=dict(type="int"), + mode=dict(choices=["active", "on", "passive", "auto", "desirable"]), + members=dict(type="list", elements="str"), + state=dict(default="present", choices=["present", "absent"]), + ) + aggregate_spec = deepcopy(element_spec) + aggregate_spec["group"] = dict(required=True) + required_one_of = [["group", "aggregate"]] + required_together = [["members", "mode"]] + mutually_exclusive = [["group", "aggregate"]] + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + argument_spec = dict( + aggregate=dict( + type="list", + elements="dict", + options=aggregate_spec, + required_together=required_together, + ), + purge=dict(default=False, type="bool"), + ) + argument_spec.update(element_spec) + argument_spec.update(ios_argument_spec) + module = AnsibleModule( + argument_spec=argument_spec, + required_one_of=required_one_of, + required_together=required_together, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + warnings = list() + result = {"changed": False} + if warnings: + result["warnings"] = warnings + want = map_params_to_obj(module) + have = map_config_to_obj(module) + commands = map_obj_to_commands((want, have), module) + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lldp.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lldp.py new file mode 100644 index 00000000..7b6d48e6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lldp.py @@ -0,0 +1,113 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_lldp +author: Ganesh Nalawade (@ganeshrn) +short_description: Manage LLDP configuration on Cisco IOS network devices. +description: +- This module provides declarative management of LLDP service on Cisco IOS network + devices. +version_added: 1.0.0 +notes: +- Tested against IOS 15.2 +options: + state: + description: + - State of the LLDP configuration. If value is I(present) lldp will be enabled + else if it is I(absent) it will be disabled. + default: present + choices: + - present + - absent + - enabled + - disabled + type: str +extends_documentation_fragment: +- cisco.ios.ios +""" +EXAMPLES = """ +- name: Enable LLDP service + cisco.ios.ios_lldp: + state: present + +- name: Disable LLDP service + cisco.ios.ios_lldp: + state: absent +""" +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always, except for the platforms that use Netconf transport to manage the device. + type: list + sample: + - lldp run +""" +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + load_config, + run_commands, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) + + +def has_lldp(module): + output = run_commands(module, ["show lldp"]) + is_lldp_enable = False + if len(output) > 0 and "LLDP is not enabled" not in output[0]: + is_lldp_enable = True + return is_lldp_enable + + +def main(): + """ main entry point for module execution + """ + argument_spec = dict( + state=dict( + default="present", + choices=["present", "absent", "enabled", "disabled"], + ) + ) + argument_spec.update(ios_argument_spec) + module = AnsibleModule( + argument_spec=argument_spec, supports_check_mode=True + ) + warnings = list() + result = {"changed": False} + if warnings: + result["warnings"] = warnings + HAS_LLDP = has_lldp(module) + commands = [] + if module.params["state"] == "absent" and HAS_LLDP: + commands.append("no lldp run") + elif module.params["state"] == "present" and not HAS_LLDP: + commands.append("lldp run") + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lldp_global.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lldp_global.py new file mode 100644 index 00000000..77f65a05 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lldp_global.py @@ -0,0 +1,356 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +""" +The module file for ios_lldp_global +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_lldp_global +short_description: LLDP resource module +description: This module configures and manages the Link Layer Discovery Protocol(LLDP) + attributes on IOS platforms. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL. +options: + config: + description: A dictionary of LLDP options + type: dict + suboptions: + holdtime: + description: + - LLDP holdtime (in sec) to be sent in packets. + - Refer to vendor documentation for valid values. + type: int + reinit: + description: + - Specify the delay (in secs) for LLDP to initialize. + - Refer to vendor documentation for valid values. + - NOTE, if LLDP reinit is configured with a starting value, idempotency won't + be maintained as the Cisco device doesn't record the starting reinit configured + value. As such, Ansible cannot verify if the respective starting reinit + value is already configured or not from the device side. If you try to apply + starting reinit value in every play run, Ansible will show changed as True. + For any other reinit value, idempotency will be maintained since any other + reinit value is recorded in the Cisco device. + type: int + enabled: + description: + - Enable LLDP + type: bool + timer: + description: + - Specify the rate at which LLDP packets are sent (in sec). + - Refer to vendor documentation for valid values. + type: int + tlv_select: + description: + - Selection of LLDP TLVs i.e. type-length-value to send + - NOTE, if tlv-select is configured idempotency won't be maintained as Cisco + device doesn't record configured tlv-select options. As such, Ansible cannot + verify if the respective tlv-select options is already configured or not + from the device side. If you try to apply tlv-select option in every play + run, Ansible will show changed as True. + type: dict + suboptions: + four_wire_power_management: + description: + - Cisco 4-wire Power via MDI TLV + type: bool + mac_phy_cfg: + description: + - IEEE 802.3 MAC/Phy Configuration/status TLV + type: bool + management_address: + description: + - Management Address TLV + type: bool + port_description: + description: + - Port Description TLV + type: bool + port_vlan: + description: + - Port VLAN ID TLV + type: bool + power_management: + description: + - IEEE 802.3 DTE Power via MDI TLV + type: bool + system_capabilities: + description: + - System Capabilities TLV + type: bool + system_description: + description: + - System Description TLV + type: bool + system_name: + description: + - System Name TLV + type: bool + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device + by executing the command B(show running-config | section ^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 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - deleted + - rendered + - gathered + - parsed + default: merged + + +""" +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# vios#sh running-config | section ^lldp +# vios1# + + +- name: Merge provided configuration with device configuration + cisco.ios.ios_lldp_global: + config: + holdtime: 10 + enabled: true + reinit: 3 + timer: 10 + state: merged + +# After state: +# ------------ +# vios#sh running-config | section ^lldp +# lldp timer 10 +# lldp holdtime 10 +# lldp reinit 3 +# lldp run + + +# Using replaced + +# Before state: +# ------------- +# vios#sh running-config | section ^lldp +# lldp timer 10 +# lldp holdtime 10 +# lldp reinit 3 +# lldp run + + +- name: Replaces LLDP device configuration with provided configuration + cisco.ios.ios_lldp_global: + config: + holdtime: 20 + reinit: 5 + state: replaced + +# After state: +# ------------- +# vios#sh running-config | section ^lldp +# lldp holdtime 20 +# lldp reinit 5 + + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured LLDP module attributes)" + +# Before state: +# ------------- +# vios#sh running-config | section ^lldp +# lldp timer 10 +# lldp holdtime 10 +# lldp reinit 3 +# lldp run + + +- name: Delete LLDP attributes + cisco.ios.ios_lldp_global: + state: deleted + +# After state: +# ------------- +# vios#sh running-config | section ^lldp +# vios1# + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh running-config | section ^lldp +# lldp timer 10 +# lldp holdtime 10 +# lldp reinit 3 +# lldp run + +- name: Gather listed interfaces with provided configurations + cisco.ios.ios_lldp_global: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": { +# "enabled": true, +# "holdtime": 10, +# "reinit": 3, +# "timer": 10 +# } + +# After state: +# ------------ +# +# vios#sh running-config | section ^lldp +# lldp timer 10 +# lldp holdtime 10 +# lldp reinit 3 +# lldp run + +# Using Rendered +- name: Render the commands for provided configuration + cisco.ios.ios_lldp_global: + config: + holdtime: 10 + enabled: true + reinit: 3 + timer: 10 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "lldp holdtime 10", +# "lldp run", +# "lldp timer 10", +# "lldp reinit 3" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# lldp timer 10 +# lldp holdtime 10 +# lldp reinit 3 +# lldp run + +- name: Parse the commands for provided configuration + cisco.ios.ios_lldp_global: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": { +# "enabled": true, +# "holdtime": 10, +# "reinit": 3, +# "timer": 10 +# } + +""" + +RETURN = """ +before: + description: The configuration as structured data prior to module invocation. + returned: always + type: dict + 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: 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: ['lldp holdtime 10', 'lldp run', 'lldp timer 10'] +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lldp_global.lldp_global import ( + Lldp_globalArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.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", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=Lldp_globalArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Lldp_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lldp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lldp_interfaces.py new file mode 100644 index 00000000..32629d09 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_lldp_interfaces.py @@ -0,0 +1,666 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +""" +The module file for ios_lldp_interfaces +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: ios_lldp_interfaces +short_description: LLDP interfaces resource module +description: This module manages link layer discovery protocol (LLDP) attributes of + interfaces on Cisco IOS devices. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL. +options: + config: + description: A dictionary of LLDP options + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface excluding any logical unit number, i.e. GigabitEthernet0/1. + type: str + required: true + receive: + description: + - Enable LLDP reception on interface. + type: bool + transmit: + description: + - Enable LLDP transmission on interface. + type: bool + med_tlv_select: + description: + - Selection of LLDP MED TLVs to send + - NOTE, if med-tlv-select is configured idempotency won't be maintained as + Cisco device doesn't record configured med-tlv-select options. As such, + Ansible cannot verify if the respective med-tlv-select options is already + configured or not from the device side. If you try to apply med-tlv-select + option in every play run, Ansible will show changed as True. + type: dict + suboptions: + inventory_management: + description: + - LLDP MED Inventory Management TLV + type: bool + tlv_select: + description: + - Selection of LLDP type-length-value i.e. TLVs to send + - NOTE, if tlv-select is configured idempotency won't be maintained as Cisco + device doesn't record configured tlv-select options. As such, Ansible cannot + verify if the respective tlv-select options is already configured or not + from the device side. If you try to apply tlv-select option in every play + run, Ansible will show changed as True. + type: dict + suboptions: + power_management: + description: + - IEEE 802.3 DTE Power via MDI TLV + type: bool + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by + executing the command B(sh lldp interface). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + description: + - The state the configuration should be left in + - 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + default: merged +""" + +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# + +- name: Merge provided configuration with device configuration + cisco.ios.ios_lldp_interfaces: + config: + - name: GigabitEthernet0/1 + receive: true + transmit: true + - name: GigabitEthernet0/2 + receive: true + - name: GigabitEthernet0/3 + transmit: true + state: merged + +# After state: +# ------------ +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: enabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# + +# Using overridden +# +# Before state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +- name: Override device configuration of all lldp_interfaces with provided configuration + cisco.ios.ios_lldp_interfaces: + config: + - name: GigabitEthernet0/2 + receive: true + transmit: true + state: overridden + +# After state: +# ------------ +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +# Using replaced +# +# Before state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# + +- name: Replaces device configuration of listed lldp_interfaces with provided configuration + cisco.ios.ios_lldp_interfaces: + config: + - name: GigabitEthernet0/2 + receive: true + transmit: true + - name: GigabitEthernet0/3 + receive: true + state: replaced + +# After state: +# ------------ +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: disabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# + +# Using Deleted +# +# Before state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +- name: "Delete LLDP attributes of given interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_lldp_interfaces: + config: + - name: GigabitEthernet0/1 + state: deleted + +# After state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# + +# Using Deleted without any config passed +# "(NOTE: This will delete all of configured LLDP module attributes)" +# +# Before state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +- name: "Delete LLDP attributes for all configured interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_lldp_interfaces: + state: deleted + +# After state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +- name: Gather listed LLDP interfaces with provided configurations + cisco.ios.ios_lldp_interfaces: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "name": "GigabitEthernet0/0", +# "receive": true, +# "transmit": true +# }, +# { +# "name": "GigabitEthernet0/1", +# "receive": true, +# "transmit": true +# }, +# { +# "name": "GigabitEthernet0/2", +# "receive": true, +# "transmit": true +# } +# ] + +# After state: +# ------------ +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +# GigabitEthernet0/2: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_lldp_interfaces: + config: + - name: GigabitEthernet0/0 + receive: true + transmit: true + - name: GigabitEthernet0/1 + receive: true + transmit: true + - name: GigabitEthernet0/2 + receive: true + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "interface GigabitEthernet0/0", +# "lldp receive", +# "lldp transmit", +# "interface GigabitEthernet0/1", +# "lldp receive", +# "lldp transmit", +# "interface GigabitEthernet0/2", +# "lldp receive" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# GigabitEthernet0/0: +# Tx: enabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: enabled +# Tx state: IDLE +# Rx state: INIT + +- name: Parse the commands for provided configuration + cisco.ios.ios_lldp_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "name": "GigabitEthernet0/0", +# "receive": false, +# "transmit": true +# }, +# { +# "name": "GigabitEthernet0/1", +# "receive": true, +# "transmit": true +# }, +# { +# "name": "GigabitEthernet0/2", +# "receive": true, +# "transmit": false +# } +# ] + +""" +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: ['interface GigabitEthernet 0/1', 'lldp transmit', 'lldp receive'] +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lldp_interfaces.lldp_interfaces import ( + Lldp_InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.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", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=Lldp_InterfacesArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Lldp_Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_logging.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_logging.py new file mode 100644 index 00000000..0d1046a1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_logging.py @@ -0,0 +1,511 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_logging +author: Trishna Guha (@trishnaguha) +short_description: Manage logging on network devices +description: +- This module provides declarative management of logging on Cisco Ios devices. +version_added: 1.0.0 +notes: +- Tested against IOS 15.6 +options: + dest: + description: + - Destination of the logs. + - On dest has to be quoted as 'on' or else pyyaml will convert + to True before it gets to Ansible. + choices: + - 'on' + - host + - console + - monitor + - buffered + - trap + type: str + name: + description: + - The hostname or IP address of the destination. + - Required when I(dest=host). + type: str + size: + description: + - Size of buffer. The acceptable value is in range from 4096 to 4294967295 bytes. + type: int + facility: + description: + - Set logging facility. + type: str + level: + description: + - Set logging severity levels. + default: debugging + choices: + - emergencies + - alerts + - critical + - errors + - warnings + - notifications + - informational + - debugging + type: str + aggregate: + description: List of logging definitions. + type: list + elements: dict + suboptions: + dest: + description: + - Destination of the logs. + - On dest has to be quoted as 'on' or else pyyaml will convert + to True before it gets to Ansible. + choices: + - 'on' + - host + - console + - monitor + - buffered + - trap + type: str + name: + description: + - The hostname or IP address of the destination. + - Required when I(dest=host). + type: str + size: + description: + - Size of buffer. The acceptable value is in range from 4096 to 4294967295 bytes. + type: int + facility: + description: + - Set logging facility. + type: str + level: + description: + - Set logging severity levels. + type: str + choices: + - emergencies + - alerts + - critical + - errors + - warnings + - notifications + - informational + - debugging + state: + description: + - State of the logging configuration. + choices: + - present + - absent + type: str + state: + description: + - State of the logging configuration. + default: present + choices: + - present + - absent + type: str +extends_documentation_fragment: +- cisco.ios.ios +""" +EXAMPLES = """ +- name: configure host logging + cisco.ios.ios_logging: + dest: host + name: 172.16.0.1 + state: present + +- name: remove host logging configuration + cisco.ios.ios_logging: + dest: host + name: 172.16.0.1 + state: absent + +- name: configure console logging level and facility + cisco.ios.ios_logging: + dest: console + facility: local7 + level: debugging + state: present + +- name: enable logging to all + cisco.ios.ios_logging: + dest: on + +- name: configure buffer size + cisco.ios.ios_logging: + dest: buffered + size: 5000 + +- name: Configure logging using aggregate + cisco.ios.ios_logging: + aggregate: + - {dest: console, level: notifications} + - {dest: buffered, size: 9000} + +- name: remove logging using aggregate + cisco.ios.ios_logging: + aggregate: + - {dest: console, level: notifications} + - {dest: buffered, size: 9000} + state: absent +""" +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - logging facility local7 + - logging host 172.16.0.1 +""" +import re +from copy import deepcopy +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, + validate_ip_address, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_capabilities, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) + + +def validate_size(value, module): + if value: + if not int(4096) <= int(value) <= int(4294967295): + module.fail_json(msg="size must be between 4096 and 4294967295") + else: + return value + + +def map_obj_to_commands(updates, module, os_version): + dest_group = "console", "monitor", "buffered", "on", "trap" + commands = list() + want, have = updates + for w in want: + dest = w["dest"] + name = w["name"] + size = w["size"] + facility = w["facility"] + level = w["level"] + state = w["state"] + del w["state"] + if facility: + w["dest"] = "facility" + if state == "absent" and w in have: + if dest: + if dest == "host": + if "12." in os_version: + commands.append("no logging {0}".format(name)) + else: + commands.append("no logging host {0}".format(name)) + elif dest in dest_group: + commands.append("no logging {0}".format(dest)) + else: + module.fail_json( + msg="dest must be among console, monitor, buffered, host, on, trap" + ) + if facility: + commands.append("no logging facility {0}".format(facility)) + if state == "present" and w not in have: + if facility: + present = False + for entry in have: + if ( + entry["dest"] == "facility" + and entry["facility"] == facility + ): + present = True + if not present: + commands.append("logging facility {0}".format(facility)) + if dest == "host": + if "12." in os_version: + commands.append("logging {0}".format(name)) + else: + commands.append("logging host {0}".format(name)) + elif dest == "on": + commands.append("logging on") + elif dest == "buffered" and size: + present = False + for entry in have: + if ( + entry["dest"] == "buffered" + and entry["size"] == size + and entry["level"] == level + ): + present = True + if not present: + if level and level != "debugging": + commands.append( + "logging buffered {0} {1}".format(size, level) + ) + else: + commands.append("logging buffered {0}".format(size)) + elif dest: + dest_cmd = "logging {0}".format(dest) + if level: + dest_cmd += " {0}".format(level) + commands.append(dest_cmd) + return commands + + +def parse_facility(line, dest): + facility = None + if dest == "facility": + match = re.search("logging facility (\\S+)", line, re.M) + if match: + facility = match.group(1) + return facility + + +def parse_size(line, dest): + size = None + if dest == "buffered": + match = re.search( + "logging buffered(?: (\\d+))?(?: [a-z]+)?", line, re.M + ) + if match: + if match.group(1) is not None: + size = match.group(1) + else: + size = "4096" + return size + + +def parse_name(line, dest): + if dest == "host": + match = re.search("logging host (\\S+)", line, re.M) + if match: + name = match.group(1) + else: + name = None + return name + + +def parse_level(line, dest): + level_group = ( + "emergencies", + "alerts", + "critical", + "errors", + "warnings", + "notifications", + "informational", + "debugging", + ) + if dest == "host": + level = "debugging" + else: + if dest == "buffered": + match = re.search( + "logging buffered(?: \\d+)?(?: ([a-z]+))?", line, re.M + ) + else: + match = re.search("logging {0} (\\S+)".format(dest), line, re.M) + if match and match.group(1) in level_group: + level = match.group(1) + else: + level = "debugging" + return level + + +def map_config_to_obj(module): + obj = [] + dest_group = ( + "console", + "host", + "monitor", + "buffered", + "on", + "facility", + "trap", + ) + data = get_config(module, flags=["| include logging"]) + for line in data.split("\n"): + match = re.search("^logging (\\S+)", line, re.M) + if match: + if match.group(1) in dest_group: + dest = match.group(1) + obj.append( + { + "dest": dest, + "name": parse_name(line, dest), + "size": parse_size(line, dest), + "facility": parse_facility(line, dest), + "level": parse_level(line, dest), + } + ) + elif validate_ip_address(match.group(1)): + dest = "host" + obj.append( + { + "dest": dest, + "name": match.group(1), + "size": parse_size(line, dest), + "facility": parse_facility(line, dest), + "level": parse_level(line, dest), + } + ) + else: + ip_match = re.search( + "\\d+\\.\\d+\\.\\d+\\.\\d+", match.group(1), re.M + ) + if ip_match: + dest = "host" + obj.append( + { + "dest": dest, + "name": match.group(1), + "size": parse_size(line, dest), + "facility": parse_facility(line, dest), + "level": parse_level(line, dest), + } + ) + return obj + + +def map_params_to_obj(module, required_if=None): + obj = [] + aggregate = module.params.get("aggregate") + if aggregate: + for item in aggregate: + for key in item: + if item.get(key) is None: + item[key] = module.params[key] + module._check_required_if(required_if, item) + d = item.copy() + if d["dest"] != "host": + d["name"] = None + if d["dest"] == "buffered": + if "size" in d: + d["size"] = str(validate_size(d["size"], module)) + elif "size" not in d: + d["size"] = str(4096) + else: + pass + if d["dest"] != "buffered": + d["size"] = None + obj.append(d) + else: + if module.params["dest"] != "host": + module.params["name"] = None + if module.params["dest"] == "buffered": + if not module.params["size"]: + module.params["size"] = str(4096) + else: + module.params["size"] = None + if module.params["size"] is None: + obj.append( + { + "dest": module.params["dest"], + "name": module.params["name"], + "size": module.params["size"], + "facility": module.params["facility"], + "level": module.params["level"], + "state": module.params["state"], + } + ) + else: + obj.append( + { + "dest": module.params["dest"], + "name": module.params["name"], + "size": str(validate_size(module.params["size"], module)), + "facility": module.params["facility"], + "level": module.params["level"], + "state": module.params["state"], + } + ) + return obj + + +def main(): + """ main entry point for module execution + """ + element_spec = dict( + dest=dict( + type="str", + choices=["on", "host", "console", "monitor", "buffered", "trap"], + ), + name=dict(type="str"), + size=dict(type="int"), + facility=dict(type="str"), + level=dict( + type="str", + default="debugging", + choices=[ + "emergencies", + "alerts", + "critical", + "errors", + "warnings", + "notifications", + "informational", + "debugging", + ], + ), + state=dict(default="present", choices=["present", "absent"]), + ) + aggregate_spec = deepcopy(element_spec) + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + argument_spec = dict( + aggregate=dict(type="list", elements="dict", options=aggregate_spec) + ) + argument_spec.update(element_spec) + argument_spec.update(ios_argument_spec) + required_if = [("dest", "host", ["name"])] + module = AnsibleModule( + argument_spec=argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + device_info = get_capabilities(module) + os_version = device_info["device_info"]["network_os_version"] + warnings = list() + result = {"changed": False} + if warnings: + result["warnings"] = warnings + want = map_params_to_obj(module, required_if=required_if) + have = map_config_to_obj(module) + commands = map_obj_to_commands((want, have), module, os_version) + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ntp.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ntp.py new file mode 100644 index 00000000..8f8e0da4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ntp.py @@ -0,0 +1,326 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_ntp +extends_documentation_fragment: +- cisco.ios.ios +short_description: Manages core NTP configuration. +description: +- Manages core NTP configuration. +version_added: 1.0.0 +author: +- Federico Olivieri (@Federico87) +options: + server: + description: + - Network address of NTP server. + type: str + source_int: + description: + - Source interface for NTP packets. + type: str + acl: + description: + - ACL for peer/server access restricition. + type: str + logging: + description: + - Enable NTP logs. Data type boolean. + type: bool + default: false + auth: + description: + - Enable NTP authentication. Data type boolean. + type: bool + default: false + auth_key: + description: + - md5 NTP authentication key of tye 7. + type: str + key_id: + description: + - auth_key id. Data type string + type: str + state: + description: + - Manage the state of the resource. + default: present + choices: + - present + - absent + type: str +""" +EXAMPLES = """ +# Set new NTP server and source interface +- cisco.ios.ios_ntp: + server: 10.0.255.10 + source_int: Loopback0 + logging: false + state: present + +# Remove NTP ACL and logging +- cisco.ios.ios_ntp: + acl: NTP_ACL + logging: true + state: absent + +# Set NTP authentication +- cisco.ios.ios_ntp: + key_id: 10 + auth_key: 15435A030726242723273C21181319000A + auth: true + state: present + +# Set new NTP configuration +- cisco.ios.ios_ntp: + server: 10.0.255.10 + source_int: Loopback0 + acl: NTP_ACL + logging: true + key_id: 10 + auth_key: 15435A030726242723273C21181319000A + auth: true + state: present +""" +RETURN = """ +commands: + description: command sent to the device + returned: always + type: list + sample: ["no ntp server 10.0.255.10", "no ntp source Loopback0"] +""" +import re +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) + + +def parse_server(line, dest): + if dest == "server": + match = re.search( + "(ntp server )(\\d+\\.\\d+\\.\\d+\\.\\d+)", line, re.M + ) + if match: + server = match.group(2) + return server + + +def parse_source_int(line, dest): + if dest == "source": + match = re.search("(ntp source )(\\S+)", line, re.M) + if match: + source = match.group(2) + return source + + +def parse_acl(line, dest): + if dest == "access-group": + match = re.search( + "ntp access-group (?:peer|serve)(?:\\s+)(\\S+)", line, re.M + ) + if match: + acl = match.group(1) + return acl + + +def parse_logging(line, dest): + if dest == "logging": + logging = dest + return logging + + +def parse_auth_key(line, dest): + if dest == "authentication-key": + match = re.search( + "(ntp authentication-key \\d+ md5 )(\\w+)", line, re.M + ) + if match: + auth_key = match.group(2) + return auth_key + + +def parse_key_id(line, dest): + if dest == "trusted-key": + match = re.search("(ntp trusted-key )(\\d+)", line, re.M) + if match: + auth_key = match.group(2) + return auth_key + + +def parse_auth(dest): + if dest == "authenticate": + return dest + + +def map_config_to_obj(module): + obj_dict = {} + obj = [] + server_list = [] + config = get_config(module, flags=["| include ntp"]) + for line in config.splitlines(): + match = re.search("ntp (\\S+)", line, re.M) + if match: + dest = match.group(1) + server = parse_server(line, dest) + source_int = parse_source_int(line, dest) + acl = parse_acl(line, dest) + logging = parse_logging(line, dest) + auth = parse_auth(dest) + auth_key = parse_auth_key(line, dest) + key_id = parse_key_id(line, dest) + if server: + server_list.append(server) + if source_int: + obj_dict["source_int"] = source_int + if acl: + obj_dict["acl"] = acl + if logging: + obj_dict["logging"] = True + if auth: + obj_dict["auth"] = True + if auth_key: + obj_dict["auth_key"] = auth_key + if key_id: + obj_dict["key_id"] = key_id + obj_dict["server"] = server_list + obj.append(obj_dict) + return obj + + +def map_params_to_obj(module): + obj = [] + obj.append( + { + "state": module.params["state"], + "server": module.params["server"], + "source_int": module.params["source_int"], + "logging": module.params["logging"], + "acl": module.params["acl"], + "auth": module.params["auth"], + "auth_key": module.params["auth_key"], + "key_id": module.params["key_id"], + } + ) + return obj + + +def map_obj_to_commands(want, have, module): + commands = list() + server_have = have[0].get("server", None) + source_int_have = have[0].get("source_int", None) + acl_have = have[0].get("acl", None) + logging_have = have[0].get("logging", None) + auth_have = have[0].get("auth", None) + auth_key_have = have[0].get("auth_key", None) + key_id_have = have[0].get("key_id", None) + for w in want: + server = w["server"] + source_int = w["source_int"] + acl = w["acl"] + logging = w["logging"] + state = w["state"] + auth = w["auth"] + auth_key = w["auth_key"] + key_id = w["key_id"] + if state == "absent": + if server_have and server in server_have: + commands.append("no ntp server {0}".format(server)) + if source_int and source_int_have: + commands.append("no ntp source {0}".format(source_int)) + if acl and acl_have: + commands.append("no ntp access-group peer {0}".format(acl)) + if logging is True and logging_have: + commands.append("no ntp logging") + if auth is True and auth_have: + commands.append("no ntp authenticate") + if key_id and key_id_have: + commands.append("no ntp trusted-key {0}".format(key_id)) + if auth_key and auth_key_have: + if key_id and key_id_have: + commands.append( + "no ntp authentication-key {0} md5 {1} 7".format( + key_id, auth_key + ) + ) + elif state == "present": + if server is not None and server not in server_have: + commands.append("ntp server {0}".format(server)) + if source_int is not None and source_int != source_int_have: + commands.append("ntp source {0}".format(source_int)) + if acl is not None and acl != acl_have: + commands.append("ntp access-group peer {0}".format(acl)) + if ( + logging is not None + and logging != logging_have + and logging is not False + ): + commands.append("ntp logging") + if auth is not None and auth != auth_have and auth is not False: + commands.append("ntp authenticate") + if key_id is not None and key_id != key_id_have: + commands.append("ntp trusted-key {0}".format(key_id)) + if auth_key is not None and auth_key != auth_key_have: + if key_id is not None: + commands.append( + "ntp authentication-key {0} md5 {1} 7".format( + key_id, auth_key + ) + ) + return commands + + +def main(): + argument_spec = dict( + server=dict(), + source_int=dict(), + acl=dict(), + logging=dict(type="bool", default=False), + auth=dict(type="bool", default=False), + auth_key=dict(), + key_id=dict(), + state=dict(choices=["absent", "present"], default="present"), + ) + argument_spec.update(ios_argument_spec) + module = AnsibleModule( + argument_spec=argument_spec, supports_check_mode=True + ) + result = {"changed": False} + warnings = list() + if warnings: + result["warnings"] = warnings + want = map_params_to_obj(module) + have = map_config_to_obj(module) + commands = map_obj_to_commands(want, have, module) + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ospf_interfaces.py new file mode 100644 index 00000000..b5bdc775 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ospf_interfaces.py @@ -0,0 +1,1101 @@ +#!/usr/bin/python +# +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for ios_ospf_interfaces +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: ios_ospf_interfaces +short_description: OSPF_Interfaces resource module +description: This module configures and manages the Open Shortest Path First (OSPF) + version 2 on IOS platforms. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL. +options: + config: + description: A dictionary of OSPF interfaces options. + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface excluding any logical unit number, + i.e. GigabitEthernet0/1. + type: str + required: true + address_family: + description: + - OSPF interfaces settings on the interfaces in address-family + context. + type: list + elements: dict + suboptions: + afi: + description: + - Address Family Identifier (AFI) for OSPF interfaces settings + on the interfaces. + type: str + choices: + - ipv4 + - ipv6 + required: true + process: + description: OSPF interfaces process config + type: dict + suboptions: + id: + description: + - Address Family Identifier (AFI) for OSPF interfaces settings + on the interfaces. Please refer vendor documentation of Valid + values. + type: int + area_id: + description: + - OSPF interfaces area ID as a decimal value. Please + refer vendor documentation of Valid values. + - OSPF interfaces area ID in IP address format(e.g. + A.B.C.D) + type: str + secondaries: + description: + - Include or exclude secondary IP addresses. + - Valid only with IPv4 config + type: bool + instance_id: + description: + - Set the OSPF instance based on ID + - Valid only with IPv6 OSPF config + type: int + adjacency: + description: Adjacency staggering + type: bool + authentication: + description: Enable authentication + type: dict + suboptions: + key_chain: + description: Use a key-chain for cryptographic + authentication keys + type: str + message_digest: + description: Use message-digest authentication + type: bool + 'null': + description: Use no authentication + type: bool + bfd: + description: + - BFD configuration commands + - Enable/Disable BFD on this interface + type: bool + cost: + description: Interface cost + type: dict + suboptions: + interface_cost: + description: Interface cost or Route cost of this interface + type: int + dynamic_cost: + description: + - Specify dynamic cost options + - Valid only with IPv6 OSPF config + type: dict + suboptions: + default: + description: Specify default link metric value + type: int + hysteresis: + description: Specify hysteresis value for LSA dampening + type: dict + suboptions: + percent: + description: Specify hysteresis percent changed. + Please refer vendor documentation of Valid values. + type: int + threshold: + description: Specify hysteresis threshold value. + Please refer vendor documentation of Valid values. + type: int + weight: + description: Specify weight to be placed on individual + metrics + type: dict + suboptions: + l2_factor: + description: + - Specify weight to be given to L2-factor metric + - Percentage weight of L2-factor metric. Please refer + vendor documentation of Valid values. + type: int + latency: + description: + - Specify weight to be given to latency metric. + - Percentage weight of latency metric. Please refer + vendor documentation of Valid values. + type: int + oc: + description: + - Specify weight to be given to cdr/mdr for oc + - Give 100 percent weightage for current data rate(0 + for maxdatarate) + type: bool + resources: + description: + - Specify weight to be given to resources metric + - Percentage weight of resources metric. Please refer + vendor documentation of Valid values. + type: int + throughput: + description: + - Specify weight to be given to throughput metric + - Percentage weight of throughput metric. Please refer + vendor documentation of Valid values. + type: int + database_filter: + description: Filter OSPF LSA during synchronization and flooding + type: bool + dead_interval: + description: Interval after which a neighbor is declared dead + type: dict + suboptions: + time: + description: time in seconds + type: int + minimal: + description: + - Set to 1 second and set multiplier for Hellos + - Number of Hellos sent within 1 second. Please refer + vendor documentation of Valid values. + - Valid only with IP OSPF config + type: int + demand_circuit: + description: OSPF Demand Circuit, enable or disable + the demand circuit' + type: dict + suboptions: + enable: + description: Enable Demand Circuit + type: bool + ignore: + description: Ignore demand circuit auto-negotiation requests + type: bool + disable: + description: + - Disable demand circuit on this interface + - Valid only with IPv6 OSPF config + type: bool + flood_reduction: + description: OSPF Flood Reduction + type: bool + hello_interval: + description: + - Time between HELLO packets + - Please refer vendor documentation of Valid values. + type: int + lls: + description: + - Link-local Signaling (LLS) support + - Valid only with IP OSPF config + type: bool + manet: + description: + - Mobile Adhoc Networking options + - MANET Peering options + - Valid only with IPv6 OSPF config + type: dict + suboptions: + cost: + description: Redundant path cost improvement required to peer + type: dict + suboptions: + percent: + description: Relative incremental path cost. + Please refer vendor documentation of Valid values. + type: int + threshold: + description: Absolute incremental path cost. + Please refer vendor documentation of Valid values. + type: int + link_metrics: + description: Redundant path cost improvement required to peer + type: dict + suboptions: + set: + description: Enable link-metrics + type: bool + cost_threshold: + description: Minimum link cost threshold. + Please refer vendor documentation of Valid values. + type: int + mtu_ignore: + description: Ignores the MTU in DBD packets + type: bool + multi_area: + description: + - Set the OSPF multi-area ID + - Valid only with IP OSPF config + type: dict + suboptions: + id: + description: + - OSPF multi-area ID as a decimal value. Please refer vendor + documentation of Valid values. + - OSPF multi-area ID in IP address format(e.g. A.B.C.D) + type: int + cost: + description: Interface cost + type: int + neighbor: + description: + - OSPF neighbor link-local IPv6 address (X:X:X:X::X) + - Valid only with IPv6 OSPF config + type: dict + suboptions: + address: + description: Neighbor link-local IPv6 address + type: str + cost: + description: OSPF cost for point-to-multipoint neighbor + type: int + database_filter: + description: Filter OSPF LSA during synchronization and flooding for point-to-multipoint neighbor + type: bool + poll_interval: + description: OSPF dead-router polling interval + type: int + priority: + description: OSPF priority of non-broadcast neighbor + type: int + network: + description: Network type + type: dict + suboptions: + broadcast: + description: Specify OSPF broadcast multi-access network + type: bool + manet: + description: + - Specify MANET OSPF interface type + - Valid only with IPv6 OSPF config + type: bool + non_broadcast: + description: Specify OSPF NBMA network + type: bool + point_to_multipoint: + description: Specify OSPF point-to-multipoint network + type: bool + point_to_point: + description: Specify OSPF point-to-point network + type: bool + prefix_suppression: + description: Enable/Disable OSPF prefix suppression + type: bool + priority: + description: Router priority. Please refer vendor documentation + of Valid values. + type: int + resync_timeout: + description: Interval after which adjacency is reset if oob-resync + is not started. Please refer vendor documentation of Valid values. + type: int + retransmit_interval: + description: Time between retransmitting lost link state + advertisements. Please refer vendor documentation of Valid values. + type: int + shutdown: + description: Set OSPF protocol's state to disable under + current interface + type: bool + transmit_delay: + description: Link state transmit delay. + Please refer vendor documentation of Valid values. + type: int + ttl_security: + description: + - TTL security check + - Valid only with IPV4 OSPF config + type: dict + suboptions: + set: + description: Enable TTL Security on all interfaces + type: bool + hops: + description: + - Maximum number of IP hops allowed + - Please refer vendor documentation of 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 IOS + device by executing the command B(sh running-config | section + ^interface). + - The state I(parsed) reads the configuration from C(running_config) + option and transforms it into Ansible structured data as per the + resource module's argspec and the value is then returned in the + I(parsed) key within the result. + type: str + state: + description: + - The state the configuration should be left in + - 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 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 running-config | include ip route|ipv6 + route) executed on device. For state I(parsed) active connection to + remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged + +""" +EXAMPLES = """ + +# Using deleted + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ipv6 ospf 55 area 105 +# ipv6 ospf priority 20 +# ipv6 ospf transmit-delay 30 +# ipv6 ospf adjacency stagger disable +# interface GigabitEthernet0/2 +# ip ospf priority 40 +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf 10 area 20 +# ip ospf cost 30 + +- name: Delete provided OSPF Interface config + cisco.ios.ios_ospf_interfaces: + config: + - name: GigabitEthernet0/1 + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "interface GigabitEthernet0/1", +# "no ipv6 ospf 55 area 105", +# "no ipv6 ospf adjacency stagger disable", +# "no ipv6 ospf priority 20", +# "no ipv6 ospf transmit-delay 30" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# interface GigabitEthernet0/2 +# ip ospf priority 40 +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf 10 area 20 +# ip ospf cost 30 + +# Using deleted without any config passed (NOTE: This will delete all OSPF Interfaces configuration from device) + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ipv6 ospf 55 area 105 +# ipv6 ospf priority 20 +# ipv6 ospf transmit-delay 30 +# ipv6 ospf adjacency stagger disable +# interface GigabitEthernet0/2 +# ip ospf priority 40 +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf 10 area 20 +# ip ospf cost 30 + +- name: Delete all OSPF config from interfaces + cisco.ios.ios_ospf_interfaces: + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "interface GigabitEthernet0/2", +# "no ip ospf 10 area 20", +# "no ip ospf adjacency stagger disable", +# "no ip ospf cost 30", +# "no ip ospf priority 40", +# "no ip ospf ttl-security hops 50", +# "interface GigabitEthernet0/1", +# "no ipv6 ospf 55 area 105", +# "no ipv6 ospf adjacency stagger disable", +# "no ipv6 ospf priority 20", +# "no ipv6 ospf transmit-delay 30" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# interface GigabitEthernet0/2 + +# Using merged + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# router-ios# + +- name: Merge provided OSPF Interfaces configuration + cisco.ios.ios_ospf_interfaces: + config: + - name: GigabitEthernet0/1 + address_family: + - afi: ipv4 + process: + id: 10 + area_id: 30 + adjacency: true + bfd: true + cost: + interface_cost: 5 + dead_interval: + time: 5 + demand_circuit: + ignore: true + network: + broadcast: true + priority: 25 + resync_timeout: 10 + shutdown: true + ttl_security: + hops: 50 + - afi: ipv6 + process: + id: 35 + area_id: 45 + adjacency: true + database_filter: true + manet: + link_metrics: + cost_threshold: 10 + priority: 55 + transmit_delay: 45 + state: merged + +# Commands Fired: +# --------------- +# +# "commands": [ +# "interface GigabitEthernet0/1", +# "ip ospf 10 area 30", +# "ip ospf adjacency stagger disable", +# "ip ospf bfd", +# "ip ospf cost 5", +# "ip ospf dead-interval 5", +# "ip ospf demand-circuit ignore", +# "ip ospf network broadcast", +# "ip ospf priority 25", +# "ip ospf resync-timeout 10", +# "ip ospf shutdown", +# "ip ospf ttl-security hops 50", +# "ipv6 ospf 35 area 45", +# "ipv6 ospf adjacency stagger disable", +# "ipv6 ospf database-filter all out", +# "ipv6 ospf manet peering link-metrics 10", +# "ipv6 ospf priority 55", +# "ipv6 ospf transmit-delay 45" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/2 + +# Using overridden + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/2 + +- name: Override provided OSPF Interfaces configuration + cisco.ios.ios_ospf_interfaces: + config: + - name: GigabitEthernet0/1 + address_family: + - afi: ipv6 + process: + id: 55 + area_id: 105 + adjacency: true + priority: 20 + transmit_delay: 30 + - name: GigabitEthernet0/2 + address_family: + - afi: ipv4 + process: + id: 10 + area_id: 20 + adjacency: true + cost: + interface_cost: 30 + priority: 40 + ttl_security: + hops: 50 + state: overridden + +# Commands Fired: +# --------------- +# +# "commands": [ +# "interface GigabitEthernet0/2", +# "ip ospf 10 area 20", +# "ip ospf adjacency stagger disable", +# "ip ospf cost 30", +# "ip ospf priority 40", +# "ip ospf ttl-security hops 50", +# "interface GigabitEthernet0/1", +# "ipv6 ospf 55 area 105", +# "no ipv6 ospf database-filter all out", +# "no ipv6 ospf manet peering link-metrics 10", +# "ipv6 ospf priority 20", +# "ipv6 ospf transmit-delay 30", +# "no ip ospf 10 area 30", +# "no ip ospf adjacency stagger disable", +# "no ip ospf bfd", +# "no ip ospf cost 5", +# "no ip ospf dead-interval 5", +# "no ip ospf demand-circuit ignore", +# "no ip ospf network broadcast", +# "no ip ospf priority 25", +# "no ip ospf resync-timeout 10", +# "no ip ospf shutdown", +# "no ip ospf ttl-security hops 50" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ipv6 ospf 55 area 105 +# ipv6 ospf priority 20 +# ipv6 ospf transmit-delay 30 +# ipv6 ospf adjacency stagger disable +# interface GigabitEthernet0/2 +# ip ospf priority 40 +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf 10 area 20 +# ip ospf cost 30 + +# Using replaced + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/2 + +- name: Replaced provided OSPF Interfaces configuration + cisco.ios.ios_ospf_interfaces: + config: + - name: GigabitEthernet0/2 + address_family: + - afi: ipv6 + process: + id: 55 + area_id: 105 + adjacency: true + priority: 20 + transmit_delay: 30 + state: replaced + +# Commands Fired: +# --------------- +# "commands": [ +# "interface GigabitEthernet0/2", +# "ipv6 ospf 55 area 105", +# "ipv6 ospf adjacency stagger disable", +# "ipv6 ospf priority 20", +# "ipv6 ospf transmit-delay 30" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/2 +# ipv6 ospf 55 area 105 +# ipv6 ospf priority 20 +# ipv6 ospf transmit-delay 30 +# ipv6 ospf adjacency stagger disable + +# Using Gathered + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/2 + +- name: Gather OSPF Interfaces provided configurations + cisco.ios.ios_ospf_interfaces: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "name": "GigabitEthernet0/2" +# }, +# { +# "address_family": [ +# { +# "adjacency": true, +# "afi": "ipv4", +# "bfd": true, +# "cost": { +# "interface_cost": 5 +# }, +# "dead_interval": { +# "time": 5 +# }, +# "demand_circuit": { +# "ignore": true +# }, +# "network": { +# "broadcast": true +# }, +# "priority": 25, +# "process": { +# "area_id": "30", +# "id": 10 +# }, +# "resync_timeout": 10, +# "shutdown": true, +# "ttl_security": { +# "hops": 50 +# } +# }, +# { +# "adjacency": true, +# "afi": "ipv6", +# "database_filter": true, +# "manet": { +# "link_metrics": { +# "cost_threshold": 10 +# } +# }, +# "priority": 55, +# "process": { +# "area_id": "45", +# "id": 35 +# }, +# "transmit_delay": 45 +# } +# ], +# "name": "GigabitEthernet0/1" +# }, +# { +# "name": "GigabitEthernet0/0" +# } +# ] + +# After state: +# ------------ +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/2 + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_ospf_interfaces: + config: + - name: GigabitEthernet0/1 + address_family: + - afi: ipv4 + process: + id: 10 + area_id: 30 + adjacency: true + bfd: true + cost: + interface_cost: 5 + dead_interval: + time: 5 + demand_circuit: + ignore: true + network: + broadcast: true + priority: 25 + resync_timeout: 10 + shutdown: true + ttl_security: + hops: 50 + - afi: ipv6 + process: + id: 35 + area_id: 45 + adjacency: true + database_filter: true + manet: + link_metrics: + cost_threshold: 10 + priority: 55 + transmit_delay: 45 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "interface GigabitEthernet0/1", +# "ip ospf 10 area 30", +# "ip ospf adjacency stagger disable", +# "ip ospf bfd", +# "ip ospf cost 5", +# "ip ospf dead-interval 5", +# "ip ospf demand-circuit ignore", +# "ip ospf network broadcast", +# "ip ospf priority 25", +# "ip ospf resync-timeout 10", +# "ip ospf shutdown", +# "ip ospf ttl-security hops 50", +# "ipv6 ospf 35 area 45", +# "ipv6 ospf adjacency stagger disable", +# "ipv6 ospf database-filter all out", +# "ipv6 ospf manet peering link-metrics 10", +# "ipv6 ospf priority 55", +# "ipv6 ospf transmit-delay 45" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# interface GigabitEthernet0/2 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/0 + +- name: Parse the provided configuration with the exisiting running configuration + cisco.ios.ios_ospf_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# }, +# { +# "name": "GigabitEthernet0/2" +# }, +# { +# "address_family": [ +# { +# "adjacency": true, +# "afi": "ipv4", +# "bfd": true, +# "cost": { +# "interface_cost": 5 +# }, +# "dead_interval": { +# "time": 5 +# }, +# "demand_circuit": { +# "ignore": true +# }, +# "network": { +# "broadcast": true +# }, +# "priority": 25, +# "process": { +# "area_id": "30", +# "id": 10 +# }, +# "resync_timeout": 10, +# "shutdown": true, +# "ttl_security": { +# "hops": 50 +# } +# }, +# { +# "adjacency": true, +# "afi": "ipv6", +# "database_filter": true, +# "manet": { +# "link_metrics": { +# "cost_threshold": 10 +# } +# }, +# "priority": 55, +# "process": { +# "area_id": "45", +# "id": 35 +# }, +# "transmit_delay": 45 +# } +# ], +# "name": "GigabitEthernet0/1" +# }, +# { +# "name": "GigabitEthernet0/0" +# } +# ] + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['interface GigabitEthernet0/1', 'ip ospf 10 area 30', 'ip ospf cost 5', 'ip ospf priority 25'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ospf_interfaces.ospf_interfaces import ( + Ospf_InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.ospf_interfaces.ospf_interfaces import ( + Ospf_Interfaces, +) + + +def main(): + """ + Main entry point for module execution + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=Ospf_InterfacesArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + + result = Ospf_Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ospfv2.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ospfv2.py new file mode 100644 index 00000000..6a435b43 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ospfv2.py @@ -0,0 +1,1691 @@ +#!/usr/bin/python +# +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for ios_ospfv2 +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: ios_ospfv2 +short_description: OSPFv2 resource module +description: This module configures and manages the Open Shortest Path First (OSPF) + version 2 on IOS platforms. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL. +options: + config: + description: A dictionary of OSPF options. + type: dict + suboptions: + processes: + description: + - List of OSPF instance configurations. + type: list + elements: dict + suboptions: + process_id: + description: Process ID + required: true + type: int + vrf: + description: Specify parameters for a VPN Routing/Forwarding instance + type: str + address_family: + description: Router Address Family configuration mode + type: dict + suboptions: + default: + description: Set a command to its defaults + type: bool + snmp_context: + description: + - Modify snmp parameters + - Configure SNMP context name + type: str + topology: + description: Associate the routing protocol to a topology instance + type: dict + suboptions: + name: + description: Routing topology instance name + type: str + base: + description: Entering router topology sub mode + type: bool + tid: + description: + - Configuring the routing protocol topology tid + - Note, please refer vendor documentation for valid values + type: bool + adjacency: + description: To configure control adjacency formation + type: dict + suboptions: + min_adjacency: + description: + - Initial number of adjacencies allowed to be forming in an area + - Please refer vendor documentation for valid values + type: int + max_adjacency: + description: + - Maximum number of adjacencies allowed to be forming + - Please refer vendor documentation for valid values + type: int + none: + description: No initial + type: bool + areas: + description: OSPF area parameters + type: list + elements: dict + suboptions: + area_id: + description: + - OSPF area ID as a decimal value. Please refer vendor documentation + of Valid values. + - OSPF area ID in IP address format(e.g. A.B.C.D) + type: str + authentication: + description: Area authentication + type: dict + suboptions: + enable: + description: Enable area authentication + type: bool + message_digest: + description: Use IPsec authentication + type: bool + capability: + description: + - Enable area specific capability + - Enable exclusion of links from base topology + type: bool + default_cost: + description: + - Set the summary default-cost of a NSSA/stub area + - Stub's advertised external route metric + - Note, please refer vendor documentation for respective valid values + type: int + filter_list: + description: Filter networks between OSPF areas + type: list + elements: dict + suboptions: + name: + description: Name of an IP prefix-list + type: str + direction: + description: The direction to apply on the filter networks sent to and from this area. + type: str + choices: ['in', 'out'] + required: true + nssa: + description: Specify a NSSA area + type: dict + suboptions: + set: + description: Enable a NSSA area + type: bool + default_information_originate: + description: Originate Type 7 default into NSSA area + type: dict + suboptions: + metric: + description: OSPF default metric + type: int + metric_type: + description: + - OSPF metric type for default routes + - OSPF Link State type + type: int + choices: [1, 2] + nssa_only: + description: Limit default advertisement to this NSSA area + type: bool + no_ext_capability: + description: Do not send domain specific capabilities into NSSA + type: bool + no_redistribution: + description: No redistribution into this NSSA area + type: bool + no_summary: + description: Do not send summary LSA into NSSA + type: bool + translate: + description: + - Translate LSA + - Always translate LSAs on this ABR + - Suppress forwarding address in translated LSAs + type: str + choices: ['always', 'suppress-fa'] + ranges: + description: Summarize routes matching address/mask (border routers only) + type: list + elements: dict + suboptions: + address: + description: IP address to match + type: str + netmask: + description: IP mask for address + type: str + advertise: + description: + - Advertise this range (default) + - Since, advertise when enabled is not shown in running-config + idempotency won't be maintained for the play in the second or + next run of the play. + type: bool + cost: + description: User specified metric for this range + type: int + not_advertise: + description: DoNotAdvertise this range + type: bool + sham_link: + description: Define a sham link and its parameters + type: dict + suboptions: + source: + description: IP addr associated with sham-link source (A.B.C.D) + type: str + destination: + description: IP addr associated with sham-link destination (A.B.C.D) + type: str + cost: + description: + - Associate a cost with the sham-link + - Cost of the sham-link + - Note, please refer vendor documentation for respective valid values + type: int + ttl_security: + description: + - TTL security check + - Maximum number of IP hops allowed + type: int + stub: + description: + - Specify a stub area + - Backbone can not be configured as stub area + type: dict + suboptions: + set: + description: Enable a stub area + type: bool + no_ext_capability: + description: Do not send domain specific capabilities into stub area + type: bool + no_summary: + description: Do not send summary LSA into stub area + type: bool + auto_cost: + description: Calculate OSPF interface cost according to bandwidth + type: dict + suboptions: + set: + description: Enable OSPF auto-cost + type: bool + reference_bandwidth: + description: + - Use reference bandwidth method to assign OSPF cost + - Note, refer vendor documentation for respective valid values + type: int + bfd: + description: + - BFD configuration commands + - Enable BFD on all interfaces + type: bool + capability: + description: Enable specific OSPF feature + type: dict + suboptions: + lls: + description: Link-local Signaling (LLS) support + type: bool + opaque: + description: Opaque LSA + type: bool + transit: + description: Transit Area + type: bool + vrf_lite: + description: Do not perform PE specific checks + type: bool + compatible: + description: OSPF router compatibility list + type: dict + suboptions: + rfc1583: + description: compatible with RFC 1583 + type: bool + rfc1587: + description: compatible with RFC 1587 + type: bool + rfc5243: + description: supports DBD exchange optimization + type: bool + default_information: + description: Control distribution of default information + type: dict + suboptions: + originate: + description: Distribute a default route + type: bool + always: + description: Always advertise default route + type: bool + metric: + description: + - OSPF default metric + - Note, refer vendor documentation for respective valid values + type: int + metric_type: + description: + - OSPF metric type for default routes + - Note, please refer vendor documentation for respective valid range + type: int + route_map: + description: Route-map reference name + type: str + default_metric: + description: Set metric of redistributed routes + type: int + discard_route: + description: Enable or disable discard-route installation + type: dict + suboptions: + set: + description: Enable discard-route installation + type: bool + external: + description: + - Discard route for redistributed summarised routes + - Administrative distance for redistributed summarised routes + - Note, please refer vendor documentation for respective valid range + type: int + internal: + description: + - Discard route for summarised internal routes + - Administrative distance for summarised internal routes + - Note, please refer vendor documentation for respective valid range + type: int + distance: + description: Define an administrative distance + type: dict + suboptions: + admin_distance: + description: OSPF Administrative distance + type: dict + suboptions: + distance: + description: Administrative distance + type: int + address: + description: IP Source address + type: str + wildcard_bits: + description: Wildcard bits + type: str + acl: + description: Access-list name/number + type: str + ospf: + description: OSPF distance + type: dict + suboptions: + external: + description: External type 5 and type 7 routes + type: int + inter_area: + description: Inter-area routes + type: int + intra_area: + description: Intra-area routes + type: int + distribute_list: + description: Filter networks in routing updates + type: dict + suboptions: + acls: + description: IP access list + type: list + elements: dict + suboptions: + name: + description: IP access list name/number + type: str + required: true + direction: + description: Filter incoming and outgoing routing updates. + type: str + required: true + choices: ['in', 'out'] + interface: + description: + - Interface configuration (GigabitEthernet A/B) + - Valid with incoming traffic + type: str + protocol: + description: + - Protocol config (bgp 1). + - Valid with outgoing traffic + type: str + prefix: + description: Filter prefixes in routing updates + type: dict + suboptions: + name: + description: Name of an IP prefix-list + type: str + required: true + gateway_name: + description: Gateway name for filtering incoming updates based on gateway + type: str + direction: + description: Filter incoming and outgoing routing updates. + type: str + required: true + choices: ['in', 'out'] + interface: + description: + - Interface configuration (GigabitEthernet A/B) + - Valid with incoming traffic + type: str + protocol: + description: + - Protocol config (bgp 1). + - Valid with outgoing traffic + type: str + route_map: + description: Filter prefixes in routing updates + type: dict + suboptions: + name: + description: Route-map name + type: str + required: true + domain_id: + description: OSPF domain-id + type: dict + suboptions: + ip_address: + description: IP address + type: dict + suboptions: + address: + description: OSPF domain ID in IP address format + type: str + secondary: + description: Secondary Domain-ID + type: bool + 'null': + description: Null Domain-ID + type: bool + domain_tag: + description: + - OSPF domain-tag which is OSPF domain tag - 32-bit value + - Note, please refer vendor documentation for respective valid range + type: int + event_log: + description: Event Logging + type: dict + suboptions: + enable: + description: Enable event Logging + type: bool + one_shot: + description: Disable Logging When Log Buffer Becomes Full + type: bool + pause: + description: Pause Event Logging + type: bool + size: + description: + - Maximum Number of Events Stored in the Event Log + - Note, refer vendor documentation for respective valid values + type: int + help: + description: Description of the interactive help system + type: bool + ignore: + description: + - Do not complain about specific event + - Do not complain upon receiving LSA of the specified type, MOSPF Type 6 LSA + type: bool + interface_id: + description: + - Source of the interface ID + - SNMP MIB ifIndex + type: bool + ispf: + description: Enable incremental SPF computation + type: bool + limit: + description: Limit a specific OSPF feature and LS update, DBD, and LS request retransmissions + type: dict + suboptions: + dc: + description: Demand circuit retransmissions + type: dict + suboptions: + number: + description: The maximum number of retransmissions + type: int + disable: + description: Disble the feature + type: bool + non_dc: + description: Non-demand-circuit retransmissions + type: dict + suboptions: + number: + description: The maximum number of retransmissions + type: int + disable: + description: Disble the feature + type: bool + local_rib_criteria: + description: Enable or disable usage of local RIB as route criteria + type: dict + suboptions: + enable: + description: Enable usage of local RIB as route criteria + type: bool + forwarding_address: + description: Local RIB used to validate external/NSSA forwarding addresses + type: bool + inter_area_summary: + description: Local RIB used as criteria for inter-area summaries + type: bool + nssa_translation: + description: Local RIB used as criteria for NSSA translation + type: bool + log_adjacency_changes: + description: Log changes in adjacency state + type: dict + suboptions: + set: + description: Log changes in adjacency state + type: bool + detail: + description: Log all state changes + type: bool + max_lsa: + description: Maximum number of non self-generated LSAs to accept + type: dict + suboptions: + number: + description: + - Maximum number of non self-generated LSAs to accept + - Note, refer vendor documentation for respective valid values + type: int + threshold_value: + description: + - Threshold value (%) at which to generate a warning msg + - Note, refer vendor documentation for respective valid values + type: int + ignore_count: + description: + - Maximum number of times adjacencies can be suppressed + - Note, refer vendor documentation for respective valid values + type: int + ignore_time: + description: + - Number of minutes during which all adjacencies are suppressed + - Note, refer vendor documentation for respective valid values + type: int + reset_time: + description: + - Number of minutes after which ignore-count is reset to zero + - Note, refer vendor documentation for respective valid values + type: int + warning_only: + description: Only give a warning message when limit is exceeded + type: bool + max_metric: + description: Set maximum metric + type: dict + suboptions: + router_lsa: + description: Maximum metric in self-originated router-LSAs + type: bool + required: true + external_lsa: + description: + - Override external-lsa metric with max-metric value + - Overriding metric in external-LSAs + - Note, refer vendor documentation for respective valid values + type: int + include_stub: + description: Set maximum metric for stub links in router-LSAs + type: bool + on_startup: + description: Set maximum metric temporarily after reboot + type: dict + suboptions: + time: + description: + - Time, in seconds, router-LSAs are originated with max-metric + - Note, please refer vendor documentation for respective valid range + type: int + wait_for_bgp: + description: Let BGP decide when to originate router-LSA with normal metric + type: bool + summary_lsa: + description: + - Override summary-lsa metric with max-metric value + - Note, please refer vendor documentation for respective valid range + type: int + maximum_paths: + description: + - Forward packets over multiple paths + - Number of paths + type: int + mpls: + description: Configure MPLS routing protocol parameters + type: dict + suboptions: + ldp: + description: routing protocol commands for MPLS LDP + type: dict + suboptions: + autoconfig: + description: routing protocol commands for MPLS LDP + type: dict + suboptions: + set: + description: Configure LDP automatic configuration and set the config + type: bool + area: + description: Configure an OSPF area to run MPLS LDP + type: str + sync: + description: Configure LDP-IGP Synchronization + type: bool + traffic_eng: + description: Let BGP decide when to originate router-LSA with normal metric + type: dict + suboptions: + area: + description: + - Configure an ospf area to run MPLS Traffic Engineering + - OSPF area ID as a decimal value or in IP address format + type: str + autoroute_exclude: + description: + - MPLS TE autoroute exclude + - Filter prefixes based on name of an IP prefix-list + type: str + interface: + description: MPLS TE interface configuration for this OSPF process + type: dict + suboptions: + interface_type: + description: TE Interface configuration (GigabitEthernet A/B) + type: str + area: + description: + - Advertise MPLS TE information for this interface into area + - OSPF area ID as a decimal value + type: int + mesh_group: + description: Traffic Engineering Mesh-Group advertisement + type: dict + suboptions: + id: + description: Mesh Group Id + type: int + interface: + description: Interface configuration (GigabitEthernet A/B) + type: str + area: + description: configure flooding scope as area + type: str + multicast_intact: + description: MPLS TE and PIM interaction + type: bool + router_id_interface: + description: Router Interface configuration (GigabitEthernet A/B) + type: str + neighbor: + description: Specify a neighbor router + type: dict + suboptions: + address: + description: Neighbor address (A.B.C.D) + type: str + cost: + description: + - OSPF cost for point-to-multipoint neighbor metric + - Note, please refer vendor documentation for respective valid range + type: int + database_filter: + description: + - Filter OSPF LSA during synchronization and flooding for point-to-multipoint neighbor + - Filter all outgoing LSA + type: bool + poll_interval: + description: OSPF dead-router polling interval of non-broadcast neighbor in Seconds + type: int + priority: + description: OSPF priority of non-broadcast neighbor priority + type: int + network: + description: Enable routing on an IP network + type: list + elements: dict + suboptions: + address: + description: Network number + type: str + wildcard_bits: + description: OSPF wild card bits + type: str + area: + description: Set the OSPF area ID + type: str + nsf: + description: Non-stop forwarding + type: dict + suboptions: + cisco: + description: Cisco Non-stop forwarding + type: dict + suboptions: + helper: + description: helper support + type: bool + disable: + description: disable helper support + type: bool + ietf: + description: IETF graceful restart + type: dict + suboptions: + helper: + description: helper support + type: bool + disable: + description: disable helper support + type: bool + strict_lsa_checking: + description: enable helper strict LSA checking + type: bool + passive_interface: + description: + - Suppress routing updates on an interface (GigabitEthernet A/B) + - Interface name with respective interface number + type: str + prefix_suppression: + description: Enable prefix suppression + type: bool + priority: + description: + - OSPF topology priority + - Note, refer vendor documentation for respective valid values + type: int + queue_depth: + description: Hello/Router process queue depth + type: dict + suboptions: + hello: + description: OSPF Hello process queue depth + type: dict + suboptions: + max_packets: + description: maximum number of packets in the queue + type: int + unlimited: + description: Unlimited queue depth + type: bool + update: + description: OSPF Router process queue depth + type: dict + suboptions: + max_packets: + description: maximum number of packets in the queue + type: int + unlimited: + description: Unlimited queue depth + type: bool + router_id: + description: + - Router-id address for this OSPF process + - OSPF router-id in IP address format (A.B.C.D) + type: str + shutdown: + description: Shutdown the router process + type: bool + summary_address: + description: Configure IP address summaries + type: dict + suboptions: + address: + description: IP summary address + type: str + mask: + description: IP Summary mask + type: str + not_advertise: + description: Do not advertise or translate + type: bool + nssa_only: + description: Limit summary to NSSA areas + type: bool + tag: + description: Set tag + type: int + timers: + description: Adjust routing timers + type: dict + suboptions: + lsa: + description: + - OSPF LSA timers, arrival timer + - The minimum interval in milliseconds between accepting the same LSA + - Note, refer vendor documentation for respective valid values + type: int + pacing: + description: OSPF pacing timers + type: dict + suboptions: + flood: + description: + - OSPF flood pacing timer + - The minimum interval in msec to pace limit flooding on interface + - Note, refer vendor documentation for respective valid values + type: int + lsa_group: + description: + - OSPF LSA group pacing timer + - Interval in sec between group of LSA being refreshed or maxaged + - Note, refer vendor documentation for respective valid values + type: int + retransmission: + description: + - OSPF retransmission pacing timer + - The minimum interval in msec between neighbor retransmissions + - Note, refer vendor documentation for respective valid values + type: int + throttle: + description: OSPF throttle timers + type: dict + suboptions: + lsa: + description: OSPF LSA throttle timers + type: dict + suboptions: + first_delay: + description: + - Delay to generate first occurrence of LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + min_delay: + description: + - Minimum delay between originating the same LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + max_delay: + description: + - Maximum delay between originating the same LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + spf: + description: OSPF SPF throttle timers + - Delay between receiving a change to SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: dict + suboptions: + receive_delay: + description: + - Delay between receiving a change to SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + between_delay: + description: + - Delay between first and second SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + max_delay: + description: + - Maximum wait time in milliseconds for SPF calculations + - Note, refer vendor documentation for respective valid values + type: int + traffic_share: + description: + - How to compute traffic share over alternate paths + - All traffic shared among min metric paths + - Use different interfaces for equal-cost paths + type: bool + ttl_security: + description: TTL security check + type: dict + suboptions: + set: + description: Enable TTL Security on all interfaces + type: bool + hops: + description: + - Maximum number of IP hops allowed + - Note, refer vendor documentation for respective 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 IOS + device by executing the command B(sh running-config | section ^router 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 + - 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged + +""" +EXAMPLES = """ + +# Using deleted + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +- name: Delete provided OSPF V2 processes + cisco.ios.ios_ospfv2: + config: + processes: + - process_id: 1 + - process_id: 200 + vrf: blue + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no router ospf 1" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in + +# Using deleted without any config passed (NOTE: This will delete all OSPFV2 configuration from device) + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +- name: Delete all OSPF processes + cisco.ios.ios_ospfv2: + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no router ospf 200 vrf blue", +# "no router ospf 1" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^router ospf +# router-ios# + +# Using merged + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router-ios# + +- name: Merge provided OSPF V2 configuration + cisco.ios.ios_ospfv2: + config: + processes: + - process_id: 1 + max_metric: + router_lsa: true + on_startup: + time: 110 + areas: + - area_id: '5' + capability: true + authentication: + enable: true + - area_id: '10' + authentication: + message_digest: true + nssa: + default_information_originate: + metric: 10 + translate: suppress-fa + default_cost: 10 + filter_list: + - name: test_prefix_in + direction: in + - name: test_prefix_out + direction: out + network: + address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + area: 5 + default_information: + originate: true + - process_id: 200 + vrf: blue + domain_id: + ip_address: + address: 192.0.3.1 + max_metric: + router_lsa: true + on_startup: + time: 100 + auto_cost: + reference_bandwidth: 4 + areas: + - area_id: '10' + capability: true + distribute_list: + acls: + - name: 10 + direction: out + - name: 123 + direction: in + state: merged + +# Commands Fired: +# --------------- +# +# "commands": [ +# "router ospf 200 vrf blue", +# "auto-cost reference-bandwidth 4", +# "distribute-list 10 out", +# "distribute-list 123 in", +# "domain-id 192.0.3.1", +# "max-metric router-lsa on-startup 100", +# "area 10 capability default-exclusion", +# "router ospf 1", +# "default-information originate", +# "max-metric router-lsa on-startup 110", +# "network 198.51.100.0 0.0.0.255 area 5", +# "area 10 authentication message-digest", +# "area 10 default-cost 10", +# "area 10 nssa translate type7 suppress-fa", +# "area 10 nssa default-information-originate metric 10", +# "area 10 filter-list prefix test_prefix_out out", +# "area 10 filter-list prefix test_prefix_in in", +# "area 5 authentication", +# "area 5 capability default-exclusion" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +# Using overridden + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +- name: Override provided OSPF V2 configuration + cisco.ios.ios_ospfv2: + config: + processes: + - process_id: 200 + vrf: blue + domain_id: + ip_address: + address: 192.0.4.1 + max_metric: + router_lsa: true + on_startup: + time: 200 + maximum_paths: 15 + ttl_security: + hops: 7 + areas: + - area_id: '10' + default_cost: 10 + authentication: + message_digest: true + - process_id: 100 + vrf: ospf_vrf + domain_id: + ip_address: + address: 192.0.5.1 + auto_cost: + reference_bandwidth: 5 + areas: + - area_id: '5' + authentication: + message_digest: true + nssa: + default_information_originate: + metric: 10 + translate: suppress-fa + state: overridden + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no router ospf 1", +# "router ospf 100 vrf ospf_vrf", +# "auto-cost reference-bandwidth 5", +# "domain-id 192.0.5.1", +# "area 5 authentication message-digest", +# "area 5 nssa translate type7 suppress-fa", +# "area 5 nssa default-information-originate metric 10", +# "router ospf 200 vrf blue", +# "no auto-cost reference-bandwidth 4", +# "no distribute-list 10 out", +# "no distribute-list 123 in", +# "domain-id 192.0.4.1", +# "max-metric router-lsa on-startup 200", +# "maximum-paths 15", +# "ttl-security all-interfaces hops 7", +# "area 10 authentication message-digest", +# "no area 10 capability default-exclusion", +# "area 10 default-cost 10" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.4.1 +# max-metric router-lsa on-startup 200 +# ttl-security all-interfaces hops 7 +# area 10 authentication message-digest +# area 10 default-cost 10 +# maximum-paths 15 +# router ospf 100 vrf ospf_vrf +# domain-id 192.0.5.1 +# auto-cost reference-bandwidth 5 +# area 5 authentication message-digest +# area 5 nssa default-information-originate metric 10 +# area 5 nssa translate type7 suppress-fa + +# Using replaced + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +- name: Replaced provided OSPF V2 configuration + cisco.ios.ios_ospfv2: + config: + processes: + - process_id: 200 + vrf: blue + domain_id: + ip_address: + address: 192.0.4.1 + max_metric: + router_lsa: true + on_startup: + time: 200 + maximum_paths: 15 + ttl_security: + hops: 7 + areas: + - area_id: '10' + default_cost: 10 + authentication: + message_digest: true + - process_id: 100 + vrf: ospf_vrf + domain_id: + ip_address: + address: 192.0.5.1 + auto_cost: + reference_bandwidth: 5 + areas: + - area_id: '5' + authentication: + message_digest: true + nssa: + default_information_originate: + metric: 10 + translate: suppress-fa + state: replaced + +# Commands Fired: +# --------------- +# "commands": [ +# "router ospf 100 vrf ospf_vrf", +# "auto-cost reference-bandwidth 5", +# "domain-id 192.0.5.1", +# "area 5 authentication message-digest", +# "area 5 nssa translate type7 suppress-fa", +# "area 5 nssa default-information-originate metric 10", +# "router ospf 200 vrf blue", +# "no auto-cost reference-bandwidth 4", +# "no distribute-list 10 out", +# "no distribute-list 123 in", +# "domain-id 192.0.4.1", +# "max-metric router-lsa on-startup 200", +# "maximum-paths 15", +# "ttl-security all-interfaces hops 7", +# "area 10 authentication message-digest", +# "no area 10 capability default-exclusion", +# "area 10 default-cost 10" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.4.1 +# max-metric router-lsa on-startup 200 +# ttl-security all-interfaces hops 7 +# area 10 authentication message-digest +# area 10 default-cost 10 +# maximum-paths 15 +# router ospf 100 vrf ospf_vrf +# domain-id 192.0.5.1 +# auto-cost reference-bandwidth 5 +# area 5 authentication message-digest +# area 5 nssa default-information-originate metric 10 +# area 5 nssa translate type7 suppress-fa +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 5 capability default-exclusion +# area 5 authentication +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_in in +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +# Using Gathered + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +- name: Gather OSPFV2 provided configurations + cisco.ios.ios_ospfv2: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "5", +# "authentication": { +# "enable": true +# }, +# "capability": true +# }, +# { +# "area_id": "10", +# "authentication": { +# "message_digest": true +# }, +# "default_cost": 10, +# "filter_list": [ +# { +# "direction": "in", +# "name": "test_prefix_in" +# }, +# { +# "direction": "out", +# "name": "test_prefix_out" +# } +# ], +# "nssa": { +# "default_information_originate": { +# "metric": 10 +# }, +# "translate": "suppress-fa" +# } +# } +# ], +# "default_information": { +# "originate": true +# }, +# "max_metric": { +# "on_startup": { +# "time": 110 +# }, +# "router_lsa": true +# }, +# "network": { +# "address": "198.51.100.0", +# "area": "5", +# "wildcard_bits": "0.0.0.255" +# }, +# "process_id": 1 +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "capability": true +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 4 +# }, +# "distribute_list": { +# "acls": [ +# { +# "direction": "out", +# "name": "10" +# }, +# { +# "direction": "in", +# "name": "123" +# } +# ] +# }, +# "domain_id": { +# "ip_address": { +# "address": "192.0.3.1" +# } +# }, +# "max_metric": { +# "on_startup": { +# "time": 100 +# }, +# "router_lsa": true +# }, +# "process_id": 200, +# "vrf": "blue" +# } +# ] +# } + +# After state: +# ------------ +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_ospfv2: + config: + processes: + - process_id: 1 + max_metric: + router_lsa: true + on_startup: + time: 110 + areas: + - area_id: '5' + capability: true + authentication: + enable: true + - area_id: '10' + authentication: + message_digest: true + nssa: + default_information_originate: + metric: 10 + translate: suppress-fa + default_cost: 10 + filter_list: + - name: test_prefix_in + direction: in + - name: test_prefix_out + direction: out + network: + address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + area: 5 + default_information: + originate: true + - process_id: 200 + vrf: blue + domain_id: + ip_address: + address: 192.0.3.1 + max_metric: + router_lsa: true + on_startup: + time: 100 + auto_cost: + reference_bandwidth: 4 + areas: + - area_id: '10' + capability: true + distribute_list: + acls: + - name: 10 + direction: out + - name: 123 + direction: in + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "router ospf 200 vrf blue", +# "auto-cost reference-bandwidth 4", +# "distribute-list 10 out", +# "distribute-list 123 in", +# "domain-id 192.0.3.1", +# "max-metric router-lsa on-startup 100", +# "area 10 capability default-exclusion", +# "router ospf 1", +# "default-information originate", +# "max-metric router-lsa on-startup 110", +# "network 198.51.100.0 0.0.0.255 area 5", +# "area 10 authentication message-digest", +# "area 10 default-cost 10", +# "area 10 nssa translate type7 suppress-fa", +# "area 10 nssa default-information-originate metric 10", +# "area 10 filter-list prefix test_prefix_out out", +# "area 10 filter-list prefix test_prefix_in in", +# "area 5 authentication", +# "area 5 capability default-exclusion" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# router ospf 100 +# auto-cost reference-bandwidth 5 +# domain-id 192.0.5.1 +# area 5 authentication message-digest +# area 5 nssa translate type7 suppress-fa +# area 5 nssa default-information-originate metric 10 + +- name: Parse the provided configuration with the exisiting running configuration + cisco.ios.ios_ospfv2: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "5", +# "authentication": { +# "message_digest": true +# }, +# "nssa": { +# "default_information_originate": { +# "metric": 10 +# }, +# "translate": "suppress-fa" +# } +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 5 +# }, +# "domain_id": { +# "ip_address": { +# "address": "192.0.5.1" +# } +# }, +# "process_id": 100 +# } +# ] +# } + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['router ospf 200 vrf blue', 'auto-cost reference-bandwidth 5', 'domain-id 192.0.4.1'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ospfv2.ospfv2 import ( + Ospfv2Args, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.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", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=Ospfv2Args.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + + result = Ospfv2(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ospfv3.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ospfv3.py new file mode 100644 index 00000000..f566a2e2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ospfv3.py @@ -0,0 +1,1974 @@ +#!/usr/bin/python +# +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for ios_ospfv3 +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: ios_ospfv3 +short_description: OSPFv3 resource module +description: This module configures and manages the Open Shortest Path First (OSPF) + version 3 on IOS platforms. +version_added: 1.1.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL. +options: + config: + description: A list of configurations for ospfv3. + type: dict + suboptions: + processes: + description: List of OSPF instance configurations. + type: list + elements: dict + suboptions: + process_id: + description: Process ID + required: true + type: int + address_family: + description: Enter Address Family command mode + type: list + elements: dict + suboptions: + afi: + description: Enter Address Family command mode + type: str + choices: + - ipv4 + - ipv6 + unicast: + description: Address Family modifier + type: bool + vrf: + description: Specify parameters for a VPN Routing/Forwarding instance + type: str + adjacency: + description: Control adjacency formation + type: dict + suboptions: + min_adjacency: + description: + - Initial number of adjacencies allowed to be forming in an area + - Please refer vendor documentation for valid values + type: int + none: + description: No initial + type: bool + max_adjacency: + description: + - Maximum number of adjacencies allowed to be forming + - Please refer vendor documentation for valid values + type: int + disable: + description: Disable adjacency staggering + type: bool + areas: + description: OSPF area parameters + type: list + elements: dict + suboptions: + area_id: + description: + - OSPF area ID as a decimal value. Please refer vendor documentation + of Valid values. + - OSPF area ID in IP address format(e.g. A.B.C.D) + type: str + authentication: + description: Authentication parameters + type: dict + suboptions: + key_chain: + description: Use a key-chain for cryptographic authentication keys + type: str + 'null': + description: Use no authentication + type: bool + default_cost: + description: + - Set the summary default-cost of a NSSA/stub area + - Stub's advertised external route metric + - Note, please refer vendor documentation for respective valid values + type: int + filter_list: + description: Filter networks between OSPFv3 areas + type: list + elements: dict + suboptions: + name: + description: Name of an IP prefix-list + type: str + direction: + description: The direction to apply on the filter networks sent to and from this area. + type: str + choices: ['in', 'out'] + required: True + normal: + description: Specify a normal area type + type: bool + nssa: + description: Specify a NSSA area + type: dict + suboptions: + set: + description: Enable a NSSA area + type: bool + default_information_originate: + description: Originate Type 7 default into NSSA area + type: dict + suboptions: + metric: + description: OSPF default metric + type: int + metric_type: + description: + - OSPF metric type for default routes + - OSPF Link State type + type: int + choices: [1, 2] + nssa_only: + description: Limit default advertisement to this NSSA area + type: bool + no_redistribution: + description: No redistribution into this NSSA area + type: bool + no_summary: + description: Do not send summary LSA into NSSA + type: bool + translate: + description: + - Translate LSA + - Always translate LSAs on this ABR + - Suppress forwarding address in translated LSAs + type: str + choices: ['always', 'suppress-fa'] + ranges: + description: Summarize routes matching address/mask (border routers only) + type: list + elements: dict + suboptions: + address: + description: IP address to match + type: str + netmask: + description: IP mask for address + type: str + advertise: + description: + - Advertise this range (default) + - Since, advertise when enabled is not shown in running-config + idempotency won't be maintained for the play in the second or + next run of the play. + type: bool + cost: + description: User specified metric for this range + type: int + not_advertise: + description: DoNotAdvertise this range + type: bool + sham_link: + description: Define a sham link and its parameters + type: dict + suboptions: + source: + description: IPv6 address associated with sham-link source (X:X:X:X::X) + type: str + destination: + description: IPv6 address associated with sham-link destination (X:X:X:X::X) + type: str + authentication: + description: Authentication parameters + type: dict + suboptions: + key_chain: + description: Use a key-chain for cryptographic authentication keys + type: str + 'null': + description: Use no authentication + type: bool + cost: + description: + - Associate a cost with the sham-link + - Cost of the sham-link + type: int + ttl_security: + description: + - TTL security check + - maximum number of hops allowed + type: int + stub: + description: + - Specify a stub area + - Backbone can not be configured as stub area + type: dict + suboptions: + set: + description: Enable a stub area + type: bool + no_summary: + description: Do not send summary LSA into stub area + type: bool + authentication: + description: + - Authentication parameters + - Authentication operation mode + type: dict + suboptions: + deployment: + description: Deployment mode of operation + type: bool + normal: + description: Normal mode of operation + type: bool + auto_cost: + description: Calculate OSPF interface cost according to bandwidth + type: dict + suboptions: + set: + description: Enable OSPF auto-cost + type: bool + reference_bandwidth: + description: + - Use reference bandwidth method to assign OSPF cost + - Note, refer vendor documentation for respective valid values + type: int + bfd: + description: BFD configuration commands + type: dict + suboptions: + all_interfaces: + description: Enable BFD on all interfaces + type: bool + disable: + description: Disable BFD on all interfaces + type: bool + capability: + description: + - Enable a specific feature + - Do not perform PE specific checks + type: bool + compatible: + description: OSPFv3 router compatibility list + type: dict + suboptions: + rfc1583: + description: compatible with RFC 1583 + type: bool + rfc1587: + description: compatible with RFC 1587 + type: bool + rfc5243: + description: supports DBD exchange optimization + type: bool + default_information: + description: Control distribution of default information + type: dict + suboptions: + originate: + description: Distribute a default route + type: bool + always: + description: Always advertise default route + type: bool + metric: + description: + - OSPF default metric + - Note, refer vendor documentation for respective valid values + type: int + metric_type: + description: + - OSPF metric type for default routes + - Note, please refer vendor documentation for respective valid range + type: int + route_map: + description: Route-map reference name + type: str + default_metric: + description: Set metric of redistributed routes + type: int + discard_route: + description: Enable or disable discard-route installation + type: dict + suboptions: + sham_link: + description: Discard route for sham-link routes + type: bool + external: + description: Discard route for summarised redistributed routes + type: bool + internal: + description: Discard route for summarised inter-area routes + type: bool + distance: + description: + - Define an administrative distance + - Note, please refer vendor documentation for respective valid range + type: int + distribute_list: + description: Filter networks in routing updates + type: dict + suboptions: + acls: + description: IP access list + type: list + elements: dict + suboptions: + name: + description: IP access list name/number + type: str + required: true + direction: + description: Filter incoming and outgoing routing updates. + type: str + required: true + choices: ['in', 'out'] + interface: + description: + - Interface configuration (GigabitEthernet A/B) + - Valid with incoming traffic + type: str + protocol: + description: + - Protocol config (bgp 1). + - Valid with outgoing traffic + type: str + prefix: + description: Filter prefixes in routing updates + type: dict + suboptions: + name: + description: Name of an IP prefix-list + type: str + required: true + gateway_name: + description: Gateway name for filtering incoming updates based on gateway + type: str + direction: + description: Filter incoming and outgoing routing updates. + type: str + required: true + choices: ['in', 'out'] + interface: + description: + - Interface configuration (GigabitEthernet A/B) + - Valid with incoming traffic + type: str + protocol: + description: + - Protocol config (bgp 1). + - Valid with outgoing traffic + type: str + route_map: + description: Filter prefixes in routing updates + type: dict + suboptions: + name: + description: Route-map name + type: str + required: true + event_log: + description: Event Logging + type: dict + suboptions: + enable: + description: Enable event Logging + type: bool + one_shot: + description: Disable Logging When Log Buffer Becomes Full + type: bool + pause: + description: Pause Event Logging + type: bool + size: + description: + - Maximum Number of Events Stored in the Event Log + - Note, refer vendor documentation for respective valid values + type: int + graceful_restart: + description: + - Graceful-restart options + - helper support + type: dict + suboptions: + enable: + description: helper support enabled + type: bool + disable: + description: disable helper support + type: bool + strict_lsa_checking: + description: enable helper strict LSA checking + type: bool + interface_id: + description: Source of the interface ID + type: dict + suboptions: + ios_if_index: + description: IOS interface number + type: bool + snmp_if_index: + description: SNMP MIB ifIndex + type: bool + limit: + description: Limit a specific OSPF feature + type: dict + suboptions: + dc: + description: Demand circuit retransmissions + type: dict + suboptions: + number: + description: The maximum number of retransmissions + type: int + disable: + description: Disble the feature + type: bool + non_dc: + description: Non-demand-circuit retransmissions + type: dict + suboptions: + number: + description: The maximum number of retransmissions + type: int + disable: + description: Disble the feature + type: bool + local_rib_criteria: + description: Enable or disable usage of local RIB as route criteria + type: dict + suboptions: + enable: + description: Enable usage of local RIB as route criteria + type: bool + forwarding_address: + description: Local RIB used to validate external/NSSA forwarding addresses + type: bool + inter_area_summary: + description: Local RIB used as criteria for inter-area summaries + type: bool + nssa_translation: + description: Local RIB used as criteria for NSSA translation + type: bool + log_adjacency_changes: + description: Log changes in adjacency state + type: dict + suboptions: + set: + description: Log changes in adjacency state + type: bool + detail: + description: Log all state changes + type: bool + manet: + description: Specify MANET OSPF parameters + type: dict + suboptions: + cache: + description: Specify MANET cache sizes + type: dict + suboptions: + acknowledgement: + description: + - Specify MANET acknowledgement cache size + - Maximum number of acknowledgements in cache + type: int + update: + description: + - Specify MANET LSA cache size + - Maximum number of LSAs in cache + type: int + hello: + description: Unicast Hellos rather than multicast + type: dict + suboptions: + multicast: + description: Multicast Hello requests and responses rather than unicast + type: bool + unicast: + description: Unicast Hello requests and responses rather than multicast + type: bool + peering: + description: MANET OSPF Smart Peering + type: dict + suboptions: + set: + description: Enable selective peering + type: bool + disable: + description: Disable selective peering + type: bool + per_interface: + description: Select peers per interface rather than per node + type: bool + redundancy: + description: + - Redundant paths + - Number of redundant OSPF paths + type: int + willingness: + description: Specify and Relay willingness value + type: int + max_lsa: + description: Maximum number of non self-generated LSAs to accept + type: dict + suboptions: + number: + description: + - Maximum number of non self-generated LSAs to accept + - Note, refer vendor documentation for respective valid values + type: int + threshold_value: + description: + - Threshold value (%) at which to generate a warning msg + - Note, refer vendor documentation for respective valid values + type: int + ignore_count: + description: + - Maximum number of times adjacencies can be suppressed + - Note, refer vendor documentation for respective valid values + type: int + ignore_time: + description: + - Number of minutes during which all adjacencies are suppressed + - Note, refer vendor documentation for respective valid values + type: int + reset_time: + description: + - Number of minutes after which ignore-count is reset to zero + - Note, refer vendor documentation for respective valid values + type: int + warning_only: + description: Only give a warning message when limit is exceeded + type: bool + max_metric: + description: + - Set maximum metric + - Maximum metric in self-originated router-LSAs + type: dict + suboptions: + disable: + description: disable maximum metric in self-originated router-LSAs + type: bool + external_lsa: + description: + - Override external-lsa metric with max-metric value + - Overriding metric in external-LSAs + - Note, refer vendor documentation for respective valid values + type: int + inter_area_lsas: + description: + - Override inter-area-lsas metric with max-metric value + - Overriding metric in inter-area-LSAs + - Note, refer vendor documentation for respective valid values + type: int + on_startup: + description: Set maximum metric temporarily after reboot + type: dict + suboptions: + time: + description: + - Time, in seconds, router-LSAs are originated with max-metric + - Note, please refer vendor documentation for respective valid range + type: int + wait_for_bgp: + description: Let BGP decide when to originate router-LSA with normal metric + type: bool + stub_prefix_lsa: + description: Set maximum metric for stub links in prefix LSAs + type: bool + maximum_paths: + description: + - Forward packets over multiple paths + - Number of paths + type: int + passive_interface: + description: Suppress routing updates on an interface + type: str + prefix_suppression: + description: Prefix suppression + type: dict + suboptions: + enable: + description: Enable prefix suppression + type: bool + disable: + description: Disable prefix suppression + type: bool + queue_depth: + description: Hello/Router process queue depth + type: dict + suboptions: + hello: + description: OSPF Hello process queue depth + type: dict + suboptions: + max_packets: + description: maximum number of packets in the queue + type: int + unlimited: + description: Unlimited queue depth + type: bool + update: + description: OSPF Router process queue depth + type: dict + suboptions: + max_packets: + description: maximum number of packets in the queue + type: int + unlimited: + description: Unlimited queue depth + type: bool + router_id: + description: + - Router-id address for this OSPF process + - OSPF router-id in IP address format (A.B.C.D) + type: str + shutdown: + description: Shutdown the router process + type: dict + suboptions: + enable: + description: Shutdown the router process + type: bool + disable: + description: Disable Shutdown + type: bool + summary_prefix: + description: Configure IP address summaries + type: dict + suboptions: + address: + description: + - IP summary address (A.B.C.D) + - IP prefix <network>/<length> (A.B.C.D/nn) + type: str + mask: + description: IP Summary mask + type: str + not_advertise: + description: Do not advertise or translate + type: bool + nssa_only: + description: Limit summary to NSSA areas + type: bool + tag: + description: Set tag + type: int + timers: + description: Adjust routing timers + type: dict + suboptions: + lsa: + description: + - OSPF LSA timers, arrival timer + - The minimum interval in milliseconds between accepting the same LSA + - Note, refer vendor documentation for respective valid values + type: int + manet: + description: OSPF MANET timers + type: dict + suboptions: + cache: + description: Specify MANET cache sizes + type: dict + suboptions: + acknowledgement: + description: Specify MANET acknowledgement cache size + type: int + redundancy: + description: Specify MANET LSA cache size + type: int + hello: + description: + - Unicast Hellos rather than multicast + - Unicast Hello requests and responses rather than multicast + type: bool + peering: + description: MANET OSPF Smart Peering + type: dict + suboptions: + set: + description: Enable selective peering + type: bool + per_interface: + description: Select peers per interface rather than per node + type: bool + redundancy: + description: + - Redundant paths + - Number of redundant OSPF paths + type: int + willingness: + description: Specify and Relay willingness value + type: int + pacing: + description: OSPF pacing timers + type: dict + suboptions: + flood: + description: + - OSPF flood pacing timer + - The minimum interval in msec to pace limit flooding on interface + - Note, refer vendor documentation for respective valid values + type: int + lsa_group: + description: + - OSPF LSA group pacing timer + - Interval in sec between group of LSA being refreshed or maxaged + - Note, refer vendor documentation for respective valid values + type: int + retransmission: + description: + - OSPF retransmission pacing timer + - The minimum interval in msec between neighbor retransmissions + - Note, refer vendor documentation for respective valid values + type: int + throttle: + description: OSPF throttle timers + type: dict + suboptions: + lsa: + description: OSPF LSA throttle timers + type: dict + suboptions: + first_delay: + description: + - Delay to generate first occurrence of LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + min_delay: + description: + - Minimum delay between originating the same LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + max_delay: + description: + - Maximum delay between originating the same LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + spf: + description: OSPF SPF throttle timers + - Delay between receiving a change to SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: dict + suboptions: + receive_delay: + description: + - Delay between receiving a change to SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + between_delay: + description: + - Delay between first and second SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + max_delay: + description: + - Maximum wait time in milliseconds for SPF calculations + - Note, refer vendor documentation for respective valid values + type: int + adjacency: + description: Control adjacency formation + type: dict + suboptions: + min_adjacency: + description: + - Initial number of adjacencies allowed to be forming in an area + - Please refer vendor documentation for valid values + type: int + max_adjacency: + description: + - Maximum number of adjacencies allowed to be forming + - Please refer vendor documentation for valid values + type: int + none: + description: No initial + type: bool + areas: + description: OSPF area parameters + type: list + elements: dict + suboptions: + area_id: + description: + - OSPF area ID as a decimal value. Please refer vendor documentation + of Valid values. + - OSPF area ID in IP address format(e.g. A.B.C.D) + type: str + authentication: + description: Authentication parameters + type: dict + suboptions: + key_chain: + description: Use a key-chain for cryptographic authentication keys + type: str + ipsec: + description: Use IPsec authentication + type: dict + suboptions: + spi: + description: Set the SPI (Security Parameters Index) + type: int + md5: + description: Use MD5 authentication + type: int + sha1: + description: Use SHA-1 authentication + type: int + hex_string: + description: SHA-1 key (40 chars) + type: str + default_cost: + description: + - Set the summary default-cost of a NSSA/stub area + - Stub's advertised external route metric + - Note, please refer vendor documentation for respective valid values + type: int + nssa: + description: Specify a NSSA area + type: dict + suboptions: + set: + description: Enable a NSSA area + type: bool + default_information_originate: + description: Originate Type 7 default into NSSA area + type: dict + suboptions: + metric: + description: OSPF default metric + type: int + metric_type: + description: + - OSPF metric type for default routes + - OSPF Link State type + type: int + choices: [1, 2] + nssa_only: + description: Limit default advertisement to this NSSA area + type: bool + no_redistribution: + description: No redistribution into this NSSA area + type: bool + no_summary: + description: Do not send summary LSA into NSSA + type: bool + translate: + description: + - Translate LSA + - Always translate LSAs on this ABR + - Suppress forwarding address in translated LSAs + type: str + choices: ['always', 'suppress-fa'] + stub: + description: + - Specify a stub area + - Backbone can not be configured as stub area + type: dict + suboptions: + set: + description: Enable a stub area + type: bool + no_summary: + description: Do not send summary LSA into stub area + type: bool + authentication: + description: + - Authentication parameter mode + - Deployment mode of operation + type: bool + auto_cost: + description: Calculate OSPF interface cost according to bandwidth + type: dict + suboptions: + set: + description: Enable OSPF auto-cost + type: bool + reference_bandwidth: + description: + - Use reference bandwidth method to assign OSPF cost + - Note, refer vendor documentation for respective valid values + type: int + bfd: + description: + - BFD configuration commands + - Enable BFD on all interfaces + type: bool + compatible: + description: OSPFv3 router compatibility list + type: dict + suboptions: + rfc1583: + description: compatible with RFC 1583 + type: bool + rfc1587: + description: compatible with RFC 1587 + type: bool + rfc5243: + description: supports DBD exchange optimization + type: bool + event_log: + description: Event Logging + type: dict + suboptions: + enable: + description: Enable event Logging + type: bool + one_shot: + description: Disable Logging When Log Buffer Becomes Full + type: bool + pause: + description: Pause Event Logging + type: bool + size: + description: + - Maximum Number of Events Stored in the Event Log + - Note, refer vendor documentation for respective valid values + type: int + graceful_restart: + description: Graceful-restart options for helper support + type: dict + suboptions: + disable: + description: disable helper support + type: bool + strict_lsa_checking: + description: enable helper strict LSA checking + type: bool + help: + description: Description of the interactive help system + type: bool + interface_id: + description: + - Source of the interface ID + - SNMP MIB ifIndex + type: bool + limit: + description: Limit a specific OSPF feature and LS update, DBD, and LS request retransmissions + type: dict + suboptions: + dc: + description: Demand circuit retransmissions + type: dict + suboptions: + number: + description: The maximum number of retransmissions + type: int + disable: + description: Disble the feature + type: bool + non_dc: + description: Non-demand-circuit retransmissions + type: dict + suboptions: + number: + description: The maximum number of retransmissions + type: int + disable: + description: Disble the feature + type: bool + local_rib_criteria: + description: Enable or disable usage of local RIB as route criteria + type: dict + suboptions: + enable: + description: Enable usage of local RIB as route criteria + type: bool + forwarding_address: + description: Local RIB used to validate external/NSSA forwarding addresses + type: bool + inter_area_summary: + description: Local RIB used as criteria for inter-area summaries + type: bool + nssa_translation: + description: Local RIB used as criteria for NSSA translation + type: bool + log_adjacency_changes: + description: Log changes in adjacency state + type: dict + suboptions: + set: + description: Log changes in adjacency state + type: bool + detail: + description: Log all state changes + type: bool + manet: + description: Specify MANET OSPF parameters + type: dict + suboptions: + cache: + description: Specify MANET cache sizes + type: dict + suboptions: + acknowledgement: + description: Specify MANET acknowledgement cache size + type: int + redundancy: + description: Specify MANET LSA cache size + type: int + hello: + description: + - Unicast Hellos rather than multicast + - Unicast Hello requests and responses rather than multicast + type: bool + peering: + description: MANET OSPF Smart Peering + type: dict + suboptions: + set: + description: Enable selective peering + type: bool + per_interface: + description: Select peers per interface rather than per node + type: bool + redundancy: + description: + - Redundant paths + - Number of redundant OSPF paths + type: int + willingness: + description: Specify and Relay willingness value + type: int + max_lsa: + description: Maximum number of non self-generated LSAs to accept + type: dict + suboptions: + number: + description: + - Maximum number of non self-generated LSAs to accept + - Note, refer vendor documentation for respective valid values + type: int + threshold_value: + description: + - Threshold value (%) at which to generate a warning msg + - Note, refer vendor documentation for respective valid values + type: int + ignore_count: + description: + - Maximum number of times adjacencies can be suppressed + - Note, refer vendor documentation for respective valid values + type: int + ignore_time: + description: + - Number of minutes during which all adjacencies are suppressed + - Note, refer vendor documentation for respective valid values + type: int + reset_time: + description: + - Number of minutes after which ignore-count is reset to zero + - Note, refer vendor documentation for respective valid values + type: int + warning_only: + description: Only give a warning message when limit is exceeded + type: bool + max_metric: + description: Set maximum metric + type: dict + suboptions: + router_lsa: + description: Maximum metric in self-originated router-LSAs + type: bool + required: true + external_lsa: + description: + - Override external-lsa metric with max-metric value + - Overriding metric in external-LSAs + - Note, refer vendor documentation for respective valid values + type: int + include_stub: + description: Set maximum metric for stub links in router-LSAs + type: bool + on_startup: + description: Set maximum metric temporarily after reboot + type: dict + suboptions: + time: + description: + - Time, in seconds, router-LSAs are originated with max-metric + - Note, please refer vendor documentation for respective valid range + type: int + wait_for_bgp: + description: Let BGP decide when to originate router-LSA with normal metric + type: bool + summary_lsa: + description: + - Override summary-lsa metric with max-metric value + - Note, please refer vendor documentation for respective valid range + type: int + passive_interface: + description: Suppress routing updates on an interface + type: str + prefix_suppression: + description: Enable prefix suppression + type: bool + queue_depth: + description: Hello/Router process queue depth + type: dict + suboptions: + hello: + description: OSPF Hello process queue depth + type: dict + suboptions: + max_packets: + description: maximum number of packets in the queue + type: int + unlimited: + description: Unlimited queue depth + type: bool + router_id: + description: + - Router-id address for this OSPF process + - OSPF router-id in IP address format (A.B.C.D) + type: str + shutdown: + description: Shutdown the router process + type: bool + timers: + description: Adjust routing timers + type: dict + suboptions: + lsa: + description: + - OSPF LSA timers, arrival timer + - The minimum interval in milliseconds between accepting the same LSA + - Note, refer vendor documentation for respective valid values + type: int + manet: + description: OSPF MANET timers + type: dict + suboptions: + cache: + description: Specify MANET cache sizes + type: dict + suboptions: + acknowledgement: + description: Specify MANET acknowledgement cache size + type: int + redundancy: + description: Specify MANET LSA cache size + type: int + hello: + description: + - Unicast Hellos rather than multicast + - Unicast Hello requests and responses rather than multicast + type: bool + peering: + description: MANET OSPF Smart Peering + type: dict + suboptions: + set: + description: Enable selective peering + type: bool + per_interface: + description: Select peers per interface rather than per node + type: bool + redundancy: + description: + - Redundant paths + - Number of redundant OSPF paths + type: int + willingness: + description: Specify and Relay willingness value + type: int + pacing: + description: OSPF pacing timers + type: dict + suboptions: + flood: + description: + - OSPF flood pacing timer + - The minimum interval in msec to pace limit flooding on interface + - Note, refer vendor documentation for respective valid values + type: int + lsa_group: + description: + - OSPF LSA group pacing timer + - Interval in sec between group of LSA being refreshed or maxaged + - Note, refer vendor documentation for respective valid values + type: int + retransmission: + description: + - OSPF retransmission pacing timer + - The minimum interval in msec between neighbor retransmissions + - Note, refer vendor documentation for respective valid values + type: int + throttle: + description: OSPF throttle timers + type: dict + suboptions: + lsa: + description: OSPF LSA throttle timers + type: dict + suboptions: + first_delay: + description: + - Delay to generate first occurrence of LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + min_delay: + description: + - Minimum delay between originating the same LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + max_delay: + description: + - Maximum delay between originating the same LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + spf: + description: OSPF SPF throttle timers + - Delay between receiving a change to SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: dict + suboptions: + receive_delay: + description: + - Delay between receiving a change to SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + between_delay: + description: + - Delay between first and second SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + max_delay: + description: + - Maximum wait time in milliseconds for SPF calculations + - Note, refer vendor documentation for respective 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 IOS device by + executing the command B(sh running-config | section ^router 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 + - 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged + +""" +EXAMPLES = """ + +# Using deleted + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +- name: Delete provided OSPF V3 processes + cisco.ios.ios_ospfv3: + config: + processes: + - process_id: 1 + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no router ospfv3 1" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +# Using deleted without any config passed (NOTE: This will delete all OSPFV3 configuration from device) + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +- name: Delete all OSPF processes + cisco.ios.ios_ospfv3: + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no router ospfv3 200", +# "no router ospfv3 1" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^router ospfv3 +# router-ios# + +# Using merged + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router-ios# + +- name: Merge provided OSPFV3 configuration + cisco.ios.ios_ospfv3: + config: + processes: + - process_id: 1 + max_metric: + router_lsa: true + on_startup: + time: 110 + address_family: + - afi: ipv4 + unicast: true + vrf: blue + adjacency: + min_adjacency: 50 + max_adjacency: 50 + areas: + - area_id: 25 + nssa: + default_information_originate: + metric: 25 + nssa_only: true + areas: + - area_id: "10" + nssa: + default_information_originate: + metric: 10 + timers: + throttle: + lsa: + first_delay: 12 + min_delay: 14 + max_delay: 16 + - process_id: 200 + address_family: + - afi: ipv4 + unicast: true + adjacency: + min_adjacency: 200 + max_adjacency: 200 + max_metric: + router_lsa: true + on_startup: + time: 100 + auto_cost: + reference_bandwidth: 4 + state: merged + +# Commands Fired: +# --------------- +# +# "commands": [ +# "router ospfv3 1", +# "max-metric router-lsa on-startup 110", +# "area 10 nssa default-information-originate metric 10", +# "address-family ipv4 unicast vrf blue", +# "adjacency stagger 50 50", +# "area 25 nssa default-information-originate metric 25 nssa-only", +# "exit-address-family", +# "router ospfv3 200", +# "auto-cost reference-bandwidth 4", +# "max-metric router-lsa on-startup 100", +# "address-family ipv4 unicast", +# "adjacency stagger 200 200", +# "exit-address-family" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +# Using overridden + +# Before state: +# ------------- +# +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +- name: Override provided OSPFV3 configuration + cisco.ios.ios_ospfv3: + config: + processes: + - process_id: 200 + max_metric: + router_lsa: true + on_startup: + time: 200 + address_family: + - afi: ipv4 + unicast: true + adjacency: + min_adjacency: 50 + max_adjacency: 50 + areas: + - area_id: 200 + nssa: + default_information_originate: + metric: 200 + nssa_only: true + areas: + - area_id: "10" + nssa: + default_information_originate: + metric: 10 + state: overridden + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no router ospfv3 1", +# "router ospfv3 200", +# "no auto-cost reference-bandwidth 4", +# "max-metric router-lsa on-startup 200", +# "area 10 nssa default-information-originate metric 10", +# "address-family ipv4 unicast", +# "adjacency stagger 50 50", +# "area 200 nssa default-information-originate metric 200 nssa-only", +# "exit-address-family" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 200 +# max-metric router-lsa on-startup 200 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast +# adjacency stagger 50 50 +# area 200 nssa default-information-originate metric 200 nssa-only +# exit-address-family + +# Using replaced + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +- name: Replaced provided OSPFV3 configuration + cisco.ios.ios_ospfv3: + config: + processes: + - process_id: 200 + max_metric: + router_lsa: true + on_startup: + time: 200 + address_family: + - afi: ipv4 + unicast: true + adjacency: + min_adjacency: 50 + max_adjacency: 50 + areas: + - area_id: 200 + nssa: + default_information_originate: + metric: 200 + nssa_only: true + areas: + - area_id: "10" + nssa: + default_information_originate: + metric: 10 + state: replaced + +# Commands Fired: +# --------------- +# "commands": [ +# "router ospfv3 200", +# "no auto-cost reference-bandwidth 4", +# "max-metric router-lsa on-startup 200", +# "area 10 nssa default-information-originate metric 10", +# "address-family ipv4 unicast", +# "adjacency stagger 50 50", +# "area 200 nssa default-information-originate metric 200 nssa-only", +# "exit-address-family" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 200 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast +# adjacency stagger 50 50 +# area 200 nssa default-information-originate metric 200 nssa-only +# exit-address-family + +# Using Gathered + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +- name: Gather OSPFV3 provided configurations + cisco.ios.ios_ospfv3: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": { +# "processes": [ +# { +# "address_family": [ +# { +# "adjacency": { +# "max_adjacency": 50, +# "min_adjacency": 50 +# }, +# "afi": "ipv4", +# "areas": [ +# { +# "area_id": "25", +# "nssa": { +# "default_information_originate": { +# "metric": 25, +# "nssa_only": true +# } +# } +# } +# ], +# "unicast": true, +# "vrf": "blue" +# } +# ], +# "areas": [ +# { +# "area_id": "10", +# "nssa": { +# "default_information_originate": { +# "metric": 10 +# } +# } +# } +# ], +# "max_metric": { +# "on_startup": { +# "time": 110 +# }, +# "router_lsa": true +# }, +# "process_id": 1 +# }, +# { +# "address_family": [ +# { +# "adjacency": { +# "max_adjacency": 200, +# "min_adjacency": 200 +# }, +# "afi": "ipv4", +# "unicast": true +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 4 +# }, +# "max_metric": { +# "on_startup": { +# "time": 100 +# }, +# "router_lsa": true +# }, +# "process_id": 200 +# } +# ] +# } + +# After state: +# ------------ +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_ospfv3: + config: + processes: + - process_id: 1 + max_metric: + router_lsa: true + on_startup: + time: 110 + address_family: + - afi: ipv4 + unicast: true + vrf: blue + adjacency: + min_adjacency: 50 + max_adjacency: 50 + areas: + - area_id: 25 + nssa: + default_information_originate: + metric: 25 + nssa_only: true + areas: + - area_id: "10" + nssa: + default_information_originate: + metric: 10 + timers: + throttle: + lsa: + first_delay: 12 + min_delay: 14 + max_delay: 16 + - process_id: 200 + address_family: + - afi: ipv4 + unicast: true + adjacency: + min_adjacency: 200 + max_adjacency: 200 + max_metric: + router_lsa: true + on_startup: + time: 100 + auto_cost: + reference_bandwidth: 4 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "router ospfv3 1", +# "max-metric router-lsa on-startup 110", +# "area 10 nssa default-information-originate metric 10", +# "address-family ipv4 unicast vrf blue", +# "adjacency stagger 50 50", +# "area 25 nssa default-information-originate metric 25 nssa-only", +# "exit-address-family", +# "router ospfv3 200", +# "auto-cost reference-bandwidth 4", +# "max-metric router-lsa on-startup 100", +# "address-family ipv4 unicast", +# "adjacency stagger 200 200", +# "exit-address-family" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +- name: Parse the provided configuration with the exisiting running configuration + cisco.ios.ios_ospfv3: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": { +# "processes": [ +# { +# "address_family": [ +# { +# "adjacency": { +# "max_adjacency": 50, +# "min_adjacency": 50 +# }, +# "afi": "ipv4", +# "areas": [ +# { +# "area_id": "25", +# "nssa": { +# "default_information_originate": { +# "metric": 25, +# "nssa_only": true +# } +# } +# } +# ], +# "unicast": true, +# "vrf": "blue" +# } +# ], +# "areas": [ +# { +# "area_id": "10", +# "nssa": { +# "default_information_originate": { +# "metric": 10 +# } +# } +# } +# ], +# "max_metric": { +# "on_startup": { +# "time": 110 +# }, +# "router_lsa": true +# }, +# "process_id": 1 +# } +# ] +# } + +""" + +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['router ospfv3 1', 'address-family ipv4 unicast vrf blue', 'adjacency stagger 50 50'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ospfv3.ospfv3 import ( + Ospfv3Args, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.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", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=Ospfv3Args.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + + result = Ospfv3(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ping.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ping.py new file mode 100644 index 00000000..b67edc57 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_ping.py @@ -0,0 +1,242 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_ping +short_description: Tests reachability using ping from Cisco IOS network devices +description: +- Tests reachability using ping from switch to a remote destination. +- For a general purpose network module, see the M(net_ping) module. +- For Windows targets, use the M(win_ping) module instead. +- For targets running Python, use the M(ping) module instead. +version_added: 1.0.0 +author: +- Jacob McGill (@jmcgill298) +extends_documentation_fragment: +- cisco.ios.ios +options: + count: + description: + - Number of packets to send. + type: int + dest: + description: + - The IP Address or hostname (resolvable by switch) of the remote node. + required: true + type: str + df_bit: + description: + - Set the DF bit. + default: false + type: bool + size: + description: + - Size of packets to send. + type: int + source: + description: + - The source IP Address. + type: str + state: + description: + - Determines if the expected result is success or fail. + choices: + - absent + - present + default: present + type: str + vrf: + description: + - The VRF to use for forwarding. + type: str +notes: +- For a general purpose network module, see the M(net_ping) module. +- For Windows targets, use the M(win_ping) module instead. +- For targets running Python, use the M(ping) module instead. +""" +EXAMPLES = """ +- name: Test reachability to 10.10.10.10 using default vrf + cisco.ios.ios_ping: + dest: 10.10.10.10 + +- name: Test reachability to 10.20.20.20 using prod vrf + cisco.ios.ios_ping: + dest: 10.20.20.20 + vrf: prod + +- name: Test unreachability to 10.30.30.30 using default vrf + cisco.ios.ios_ping: + dest: 10.30.30.30 + state: absent + +- name: Test reachability to 10.40.40.40 using prod vrf and setting count and source + cisco.ios.ios_ping: + dest: 10.40.40.40 + source: loopback0 + vrf: prod + count: 20 + +- name: Test reachability to 10.50.50.50 using df-bit and size + cisco.ios.ios_ping: + dest: 10.50.50.50 + df_bit: true + size: 1400 +""" +RETURN = """ +commands: + description: Show the command sent. + returned: always + type: list + sample: ["ping vrf prod 10.40.40.40 count 20 source loopback0"] +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: Show RTT stats. + returned: always + type: dict + sample: {"avg": 2, "max": 8, "min": 1} +""" +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + run_commands, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) +import re + + +def main(): + """ main entry point for module execution + """ + argument_spec = dict( + count=dict(type="int"), + dest=dict(type="str", required=True), + df_bit=dict(type="bool", default=False), + size=dict(type="int"), + source=dict(type="str"), + state=dict( + type="str", choices=["absent", "present"], default="present" + ), + vrf=dict(type="str"), + ) + argument_spec.update(ios_argument_spec) + module = AnsibleModule(argument_spec=argument_spec) + count = module.params["count"] + dest = module.params["dest"] + df_bit = module.params["df_bit"] + size = module.params["size"] + source = module.params["source"] + vrf = module.params["vrf"] + warnings = list() + results = {} + if warnings: + results["warnings"] = warnings + results["commands"] = [build_ping(dest, count, source, vrf, size, df_bit)] + ping_results = run_commands(module, commands=results["commands"]) + ping_results_list = ping_results[0].split("\n") + stats = "" + for line in ping_results_list: + if line.startswith("Success"): + stats = line + success, rx, tx, rtt = parse_ping(stats) + loss = abs(100 - int(success)) + results["packet_loss"] = str(loss) + "%" + results["packets_rx"] = int(rx) + results["packets_tx"] = int(tx) + # Convert rtt values to int + for k, v in rtt.items(): + if rtt[k] is not None: + rtt[k] = int(v) + results["rtt"] = rtt + validate_results(module, loss, results) + module.exit_json(**results) + + +def build_ping( + dest, count=None, source=None, vrf=None, size=None, df_bit=False +): + """ + Function to build the command to send to the terminal for the switch + to execute. All args come from the module's unique params. + """ + if vrf is not None: + cmd = "ping vrf {0} {1}".format(vrf, dest) + else: + cmd = "ping {0}".format(dest) + if count is not None: + cmd += " repeat {0}".format(str(count)) + if size is not None: + cmd += " size {0}".format(size) + if df_bit: + cmd += " df-bit" + if source is not None: + cmd += " source {0}".format(source) + return cmd + + +def parse_ping(ping_stats): + """ + Function used to parse the statistical information from the ping response. + Example: "Success rate is 100 percent (5/5), round-trip min/avg/max = 1/2/8 ms" + Returns the percent of packet loss, received packets, transmitted packets, and RTT dict. + """ + rate_re = re.compile( + "^\\w+\\s+\\w+\\s+\\w+\\s+(?P<pct>\\d+)\\s+\\w+\\s+\\((?P<rx>\\d+)/(?P<tx>\\d+)\\)" + ) + rtt_re = re.compile( + ".*,\\s+\\S+\\s+\\S+\\s+=\\s+(?P<min>\\d+)/(?P<avg>\\d+)/(?P<max>\\d+)\\s+\\w+\\s*$|.*\\s*$" + ) + rate = rate_re.match(ping_stats) + rtt = rtt_re.match(ping_stats) + return ( + rate.group("pct"), + rate.group("rx"), + rate.group("tx"), + rtt.groupdict(), + ) + + +def validate_results(module, loss, results): + """ + This function is used to validate whether the ping results were unexpected per "state" param. + """ + state = module.params["state"] + if state == "present" and loss == 100: + module.fail_json(msg="Ping failed unexpectedly", **results) + elif state == "absent" and loss < 100: + module.fail_json(msg="Ping succeeded unexpectedly", **results) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_static_route.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_static_route.py new file mode 100644 index 00000000..a3b5a957 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_static_route.py @@ -0,0 +1,372 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_static_route +author: Ricardo Carrillo Cruz (@rcarrillocruz) +short_description: (deprecated, removed after 2022-06-01) Manage static IP routes + on Cisco IOS network devices +description: +- This module provides declarative management of static IP routes on Cisco IOS network + devices. +version_added: 1.0.0 +deprecated: + alternative: ios_static_routes + why: Newer and updated modules released with more functionality. + removed_at_date: '2022-06-01' +notes: +- Tested against IOS 15.6 +options: + prefix: + description: + - Network prefix of the static route. + type: str + mask: + description: + - Network prefix mask of the static route. + type: str + next_hop: + description: + - Next hop IP of the static route. + type: str + vrf: + description: + - VRF of the static route. + type: str + interface: + description: + - Interface of the static route. + type: str + name: + description: + - Name of the static route + type: str + aliases: + - description + admin_distance: + description: + - Admin distance of the static route. + type: str + tag: + description: + - Set tag of the static route. + type: str + track: + description: + - Tracked item to depend on for the static route. + type: str + aggregate: + description: List of static route definitions. + type: list + elements: dict + suboptions: + prefix: + description: + - Network prefix of the static route. + type: str + required: true + mask: + description: + - Network prefix mask of the static route. + type: str + next_hop: + description: + - Next hop IP of the static route. + type: str + vrf: + description: + - VRF of the static route. + type: str + interface: + description: + - Interface of the static route. + type: str + name: + description: + - Name of the static route + aliases: + - description + type: str + admin_distance: + description: + - Admin distance of the static route. + type: str + tag: + description: + - Set tag of the static route. + type: str + track: + description: + - Tracked item to depend on for the static route. + type: str + state: + description: + - State of the static route configuration. + choices: + - present + - absent + type: str + state: + description: + - State of the static route configuration. + default: present + choices: + - present + - absent + type: str +extends_documentation_fragment: +- cisco.ios.ios +""" +EXAMPLES = """ +- name: configure static route + cisco.ios.ios_static_route: + prefix: 192.168.2.0 + mask: 255.255.255.0 + next_hop: 10.0.0.1 + +- name: configure black hole in vrf blue depending on tracked item 10 + cisco.ios.ios_static_route: + prefix: 192.168.2.0 + mask: 255.255.255.0 + vrf: blue + interface: null0 + track: 10 + +- name: configure ultimate route with name and tag + cisco.ios.ios_static_route: + prefix: 192.168.2.0 + mask: 255.255.255.0 + interface: GigabitEthernet1 + name: hello world + tag: 100 + +- name: remove configuration + cisco.ios.ios_static_route: + prefix: 192.168.2.0 + mask: 255.255.255.0 + next_hop: 10.0.0.1 + state: absent + +- name: Add static route aggregates + cisco.ios.ios_static_route: + aggregate: + - {prefix: 172.16.32.0, mask: 255.255.255.0, next_hop: 10.0.0.8} + - {prefix: 172.16.33.0, mask: 255.255.255.0, next_hop: 10.0.0.8} + +- name: Remove static route aggregates + cisco.ios.ios_static_route: + aggregate: + - {prefix: 172.16.32.0, mask: 255.255.255.0, next_hop: 10.0.0.8} + - {prefix: 172.16.33.0, mask: 255.255.255.0, next_hop: 10.0.0.8} + state: absent +""" +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - ip route 192.168.2.0 255.255.255.0 10.0.0.1 +""" +from copy import deepcopy +from re import findall +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, + validate_ip_address, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) + + +def map_obj_to_commands(want, have): + commands = list() + for w in want: + state = w["state"] + del w["state"] + # Try to match an existing config with the desired config + for h in have: + # Try to match an existing config with the desired config + if not w.get("admin_distance") and h.get("admin_distance"): + del h["admin_distance"] + diff = list(set(w.items()) ^ set(h.items())) + if not diff: + break + # Try to match an existing config with the desired config + if ( + len(diff) == 2 + and diff[0][0] == diff[1][0] == "name" + and (not w["name"] or h["name"].startswith(w["name"])) + ): + break + # If no matches found, clear `h` + else: + h = None + command = "ip route" + prefix = w["prefix"] + mask = w["mask"] + vrf = w.get("vrf") + if vrf: + command = " ".join((command, "vrf", vrf, prefix, mask)) + else: + command = " ".join((command, prefix, mask)) + for key in [ + "interface", + "next_hop", + "admin_distance", + "tag", + "name", + "track", + ]: + if w.get(key): + if key == "name" and len(w.get(key).split()) > 1: + command = " ".join((command, key, '"%s"' % w.get(key))) + elif key in ("name", "tag", "track"): + command = " ".join((command, key, w.get(key))) + else: + command = " ".join((command, w.get(key))) + if state == "absent" and h: + commands.append("no %s" % command) + elif state == "present" and not h: + commands.append(command) + return commands + + +def map_config_to_obj(module): + obj = [] + out = get_config(module, flags="| include ip route") + for line in out.splitlines(): + # Split by whitespace but do not split quotes, needed for name parameter + splitted_line = findall('[^"\\s]\\S*|".+?"', line) + if splitted_line[2] == "vrf": + route = {"vrf": splitted_line[3]} + del splitted_line[:4] # Removes the words ip route vrf vrf_name + else: + route = {} + del splitted_line[:2] # Removes the words ip route + prefix = splitted_line[0] + mask = splitted_line[1] + route.update({"prefix": prefix, "mask": mask, "admin_distance": "1"}) + next_word = None + for word in splitted_line[2:]: + if next_word: + route[next_word] = word.strip('"') + next_word = None + elif validate_ip_address(word): + route.update(next_hop=word) + elif word.isdigit(): + route.update(admin_distance=word) + elif word in ("tag", "name", "track"): + next_word = word + else: + route.update(interface=word) + obj.append(route) + return obj + + +def map_params_to_obj(module, required_together=None): + keys = [ + "prefix", + "mask", + "state", + "next_hop", + "vrf", + "interface", + "name", + "admin_distance", + "track", + "tag", + ] + obj = [] + aggregate = module.params.get("aggregate") + if aggregate: + for item in aggregate: + route = item.copy() + for key in keys: + if route.get(key) is None: + route[key] = module.params.get(key) + route = dict((k, v) for k, v in route.items() if v is not None) + module._check_required_together(required_together, route) + obj.append(route) + else: + module._check_required_together(required_together, module.params) + route = dict() + for key in keys: + if module.params.get(key) is not None: + route[key] = module.params.get(key) + obj.append(route) + return obj + + +def main(): + """ main entry point for module execution + """ + element_spec = dict( + prefix=dict(type="str"), + mask=dict(type="str"), + next_hop=dict(type="str"), + vrf=dict(type="str"), + interface=dict(type="str"), + name=dict(type="str", aliases=["description"]), + admin_distance=dict(type="str"), + track=dict(type="str"), + tag=dict(type="str"), + state=dict(default="present", choices=["present", "absent"]), + ) + aggregate_spec = deepcopy(element_spec) + aggregate_spec["prefix"] = dict(required=True) + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + argument_spec = dict( + aggregate=dict(type="list", elements="dict", options=aggregate_spec) + ) + argument_spec.update(element_spec) + argument_spec.update(ios_argument_spec) + required_one_of = [["aggregate", "prefix"]] + required_together = [["prefix", "mask"]] + mutually_exclusive = [["aggregate", "prefix"]] + module = AnsibleModule( + argument_spec=argument_spec, + required_one_of=required_one_of, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + warnings = list() + result = {"changed": False} + if warnings: + result["warnings"] = warnings + want = map_params_to_obj(module, required_together=required_together) + have = map_config_to_obj(module) + commands = map_obj_to_commands(want, have) + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_static_routes.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_static_routes.py new file mode 100644 index 00000000..f8115095 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_static_routes.py @@ -0,0 +1,692 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +""" +The module file for ios_static_routes +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_static_routes +short_description: Static routes resource module +description: This module configures and manages the static routes on IOS platforms. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSv Version 15.2 on VIRL. +options: + config: + description: A dictionary of static route options + type: list + elements: dict + suboptions: + vrf: + description: + - IP VPN Routing/Forwarding instance name. + - NOTE, In case of IPV4/IPV6 VRF routing table should pre-exist before configuring. + - NOTE, if the vrf information is not provided then the routes shall be configured + under global vrf. + type: str + address_families: + elements: dict + description: + - Address family to use for the static routes + type: list + suboptions: + afi: + description: + - Top level address family indicator. + required: true + type: str + choices: + - ipv4 + - ipv6 + routes: + description: Configuring static route + type: list + elements: dict + suboptions: + dest: + description: Destination prefix with its subnet mask + type: str + required: true + topology: + description: + - Configure static route for a Topology Routing/Forwarding instance + - NOTE, VRF and Topology can be used together only with Multicast + and Topology should pre-exist before it can be used + type: str + next_hops: + description: + - next hop address or interface + type: list + elements: dict + suboptions: + forward_router_address: + description: Forwarding router's address + type: str + interface: + description: Interface for directly connected static routes + type: str + dhcp: + description: Default gateway obtained from DHCP + type: bool + distance_metric: + description: Distance metric for this route + type: int + global: + description: Next hop address is global + type: bool + name: + description: Specify name of the next hop + type: str + multicast: + description: multicast route + type: bool + permanent: + description: permanent route + type: bool + tag: + description: + - Set tag for this route + - Refer to vendor documentation for valid values. + type: int + track: + description: + - Install route depending on tracked item with tracked object + number. + - Tracking does not support multicast + - 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 IOS + device by executing the command B(show running-config | include ip route|ipv6 route). + - 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 + - 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route + +- name: Merge provided configuration with device configuration + cisco.ios.ios_static_routes: + config: + - vrf: blue + address_families: + - afi: ipv4 + routes: + - dest: 192.0.2.0/24 + next_hops: + - forward_router_address: 192.0.2.1 + name: merged_blue + tag: 50 + track: 150 + - address_families: + - afi: ipv4 + routes: + - dest: 198.51.100.0/24 + next_hops: + - forward_router_address: 198.51.101.1 + name: merged_route_1 + distance_metric: 110 + tag: 40 + multicast: true + - forward_router_address: 198.51.101.2 + name: merged_route_2 + distance_metric: 30 + - forward_router_address: 198.51.101.3 + name: merged_route_3 + - afi: ipv6 + routes: + - dest: 2001:DB8:0:3::/64 + next_hops: + - forward_router_address: 2001:DB8:0:3::2 + name: merged_v6 + tag: 105 + state: merged + +# Commands fired: +# --------------- +# ip route vrf blue 192.0.2.0 255.255.255.0 10.0.0.8 name merged_blue track 150 tag 50 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name merged_route_1 tag 40 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name merged_route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name merged_route_3 +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name merged_v6 tag 105 + +# After state: +# ------------ +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name merged_blue track 150 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name merged_route_3 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name merged_route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name merged_route_1 multicast +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name merged_v6 + +# Using replaced + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105 + +- name: Replace provided configuration with device configuration + cisco.ios.ios_static_routes: + config: + - address_families: + - afi: ipv4 + routes: + - dest: 198.51.100.0/24 + next_hops: + - forward_router_address: 198.51.101.1 + name: replaced_route + distance_metric: 175 + tag: 70 + multicast: true + state: replaced + +# Commands fired: +# --------------- +# no ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40 +# no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 175 name replaced_route track 150 tag 70 + +# After state: +# ------------ +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 175 name replaced_route track 150 tag 70 +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 + +# Using overridden + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105 + +- name: Override provided configuration with device configuration + cisco.ios.ios_static_routes: + config: + - vrf: blue + address_families: + - afi: ipv4 + routes: + - dest: 192.0.2.0/24 + next_hops: + - forward_router_address: 192.0.2.1 + name: override_vrf + tag: 50 + track: 150 + state: overridden + +# Commands fired: +# --------------- +# no ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40 +# no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# no ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 198.51.101.8 name test_vrf track 150 tag 50 +# no ipv6 route FD5D:12C9:2201:1::/64 FD5D:12C9:2202::2 name test_v6 tag 105 +# ip route vrf blue 192.0.2.0 255.255.255.0 198.51.101.4 name override_vrf track 150 tag 50 + +# After state: +# ------------ +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name override_vrf track 150 + +# Using Deleted + +# Example 1: +# ---------- +# To delete the exact static routes, with all the static routes explicitly mentioned in want + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105 + +- name: Delete provided configuration from the device configuration + cisco.ios.ios_static_routes: + config: + - vrf: ansible_temp_vrf + address_families: + - afi: ipv4 + routes: + - dest: 192.0.2.0/24 + next_hops: + - forward_router_address: 192.0.2.1 + name: test_vrf + tag: 50 + track: 150 + - address_families: + - afi: ipv4 + routes: + - dest: 198.51.100.0/24 + next_hops: + - forward_router_address: 198.51.101.1 + name: route_1 + distance_metric: 110 + tag: 40 + multicast: true + - forward_router_address: 198.51.101.2 + name: route_2 + distance_metric: 30 + - forward_router_address: 198.51.101.3 + name: route_3 + - afi: ipv6 + routes: + - dest: 2001:DB8:0:3::/64 + next_hops: + - forward_router_address: 2001:DB8:0:3::2 + name: test_v6 + tag: 105 + state: deleted + +# Commands fired: +# --------------- +# no ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 198.51.101.8 name test_vrf track 150 tag 50 +# no ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40 +# no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# no ipv6 route FD5D:12C9:2201:1::/64 FD5D:12C9:2202::2 name test_v6 tag 105 + +# After state: +# ------------ +# +# vios#show running-config | include ip route|ipv6 route + +# Example 2: +# ---------- +# To delete the destination specific static routes + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105 + +- name: Delete provided configuration from the device configuration + cisco.ios.ios_static_routes: + config: + - address_families: + - afi: ipv4 + routes: + - dest: 198.51.100.0/24 + state: deleted + +# Commands fired: +# --------------- +# no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# no ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast + +# After state: +# ------------ +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150 +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 + + +# Example 3: +# ---------- +# To delete the vrf specific static routes + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105 + +- name: Delete provided configuration from the device configuration + cisco.ios.ios_static_routes: + config: + - vrf: ansible_temp_vrf + state: deleted + +# Commands fired: +# --------------- +# no ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50 + +# After state: +# ------------ +# +# vios#show running-config | include ip route|ipv6 route +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured resource module attributes from each configured interface)" + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105 + +- name: Delete ALL configured IOS static routes + cisco.ios.ios_static_routes: + state: deleted + +# Commands fired: +# --------------- +# no ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150 +# no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# no ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast +# no ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 + +# After state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# + +# Using gathered + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105 + +- name: Gather listed static routes with provided configurations + cisco.ios.ios_static_routes: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "address_families": [ +# { +# "afi": "ipv4", +# "routes": [ +# { +# "dest": "192.0.2.0/24", +# "next_hops": [ +# { +# "forward_router_address": "192.0.2.1", +# "name": "test_vrf", +# "tag": 50, +# "track": 150 +# } +# ] +# } +# ] +# } +# ], +# "vrf": "ansible_temp_vrf" +# }, +# { +# "address_families": [ +# { +# "afi": "ipv6", +# "routes": [ +# { +# "dest": "2001:DB8:0:3::/64", +# "next_hops": [ +# { +# "forward_router_address": "2001:DB8:0:3::2", +# "name": "test_v6", +# "tag": 105 +# } +# ] +# } +# ] +# }, +# { +# "afi": "ipv4", +# "routes": [ +# { +# "dest": "198.51.100.0/24", +# "next_hops": [ +# { +# "distance_metric": 110, +# "forward_router_address": "198.51.101.1", +# "multicast": true, +# "name": "route_1", +# "tag": 40 +# }, +# { +# "distance_metric": 30, +# "forward_router_address": "198.51.101.2", +# "name": "route_2" +# }, +# { +# "forward_router_address": "198.51.101.3", +# "name": "route_3" +# } +# ] +# } +# ] +# } +# ] +# } +# ] + +# After state: +# ------------ +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105 + +# Using rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_static_routes: + config: + - vrf: ansible_temp_vrf + address_families: + - afi: ipv4 + routes: + - dest: 192.0.2.0/24 + next_hops: + - forward_router_address: 192.0.2.1 + name: test_vrf + tag: 50 + track: 150 + - address_families: + - afi: ipv4 + routes: + - dest: 198.51.100.0/24 + next_hops: + - forward_router_address: 198.51.101.1 + name: route_1 + distance_metric: 110 + tag: 40 + multicast: true + - forward_router_address: 198.51.101.2 + name: route_2 + distance_metric: 30 + - forward_router_address: 198.51.101.3 + name: route_3 + - afi: ipv6 + routes: + - dest: 2001:DB8:0:3::/64 + next_hops: + - forward_router_address: 2001:DB8:0:3::2 + name: test_v6 + tag: 105 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50", +# "ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40", +# "ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2", +# "ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3", +# "ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105" +# ] +""" +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: ['ip route vrf test 172.31.10.0 255.255.255.0 10.10.10.2 name new_test multicast'] +rendered: + description: The set of CLI commands generated from the value in C(config) option + returned: When C(state) is I(rendered) + type: list + sample: ['interface Ethernet1/1', 'mtu 1800'] +gathered: + description: + - The configuration as structured data transformed for the running configuration + fetched from remote host + returned: When C(state) is I(gathered) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +parsed: + description: + - The configuration as structured data transformed for the value of + C(running_config) option + returned: When C(state) is I(parsed) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +""" +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.static_routes.static_routes import ( + Static_RoutesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.static_routes.static_routes import ( + Static_Routes, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + module = AnsibleModule( + argument_spec=Static_RoutesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive, + ) + result = Static_Routes(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_system.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_system.py new file mode 100644 index 00000000..208270bc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_system.py @@ -0,0 +1,382 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_system +author: Peter Sprygada (@privateip) +short_description: Manage the system attributes on Cisco IOS devices +description: +- This module provides declarative management of node system attributes on Cisco IOS + devices. It provides an option to configure host system parameters or remove those + parameters from the device active configuration. +version_added: 1.0.0 +extends_documentation_fragment: +- cisco.ios.ios +notes: +- Tested against IOS 15.6 +options: + hostname: + description: + - Configure the device hostname parameter. This option takes an ASCII string value. + type: str + domain_name: + description: + - Configure the IP domain name on the remote device to the provided value. Value + should be in the dotted name form and will be appended to the C(hostname) to + create a fully-qualified domain name. + type: list + elements: raw + domain_search: + description: + - Provides the list of domain suffixes to append to the hostname for the purpose + of doing name resolution. This argument accepts a list of names and will be + reconciled with the current active configuration on the running node. + type: list + elements: raw + lookup_source: + description: + - Provides one or more source interfaces to use for performing DNS lookups. The + interface provided in C(lookup_source) must be a valid interface configured + on the device. + type: str + lookup_enabled: + description: + - Administrative control for enabling or disabling DNS lookups. When this argument + is set to True, lookups are performed and when it is set to False, lookups are + not performed. + type: bool + name_servers: + description: + - List of DNS name servers by IP address to use to perform name resolution lookups. This + argument accepts either a list of DNS servers See examples. + type: list + elements: raw + state: + description: + - State of the configuration values in the device's current active configuration. When + set to I(present), the values should be configured in the device active configuration + and when set to I(absent) the values should not be in the device active configuration + default: present + choices: + - present + - absent + type: str +""" +EXAMPLES = """ +- name: configure hostname and domain name + cisco.ios.ios_system: + hostname: ios01 + domain_name: test.example.com + domain_search: + - ansible.com + - redhat.com + - cisco.com + +- name: remove configuration + cisco.ios.ios_system: + state: absent + +- name: configure DNS lookup sources + cisco.ios.ios_system: + lookup_source: MgmtEth0/0/CPU0/0 + lookup_enabled: yes + +- name: configure name servers + cisco.ios.ios_system: + name_servers: + - 8.8.8.8 + - 8.8.4.4 +""" +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - hostname ios01 + - ip domain name test.example.com +""" +import re +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + ComplexList, +) + +_CONFIGURED_VRFS = None + + +def has_vrf(module, vrf): + global _CONFIGURED_VRFS + if _CONFIGURED_VRFS is not None: + return vrf in _CONFIGURED_VRFS + config = get_config(module) + _CONFIGURED_VRFS = re.findall("vrf definition (\\S+)", config) + return vrf in _CONFIGURED_VRFS + + +def requires_vrf(module, vrf): + if not has_vrf(module, vrf): + module.fail_json(msg="vrf %s is not configured" % vrf) + + +def diff_list(want, have): + adds = [w for w in want if w not in have] + removes = [h for h in have if h not in want] + return adds, removes + + +def map_obj_to_commands(want, have, module): + commands = list() + state = module.params["state"] + + def needs_update(x): + return want.get(x) is not None and want.get(x) != have.get(x) + + if state == "absent": + if have["hostname"] != "Router": + commands.append("no hostname") + if have["lookup_source"]: + commands.append( + "no ip domain lookup source-interface %s" + % have["lookup_source"] + ) + if have["lookup_enabled"] is False: + commands.append("ip domain lookup") + vrfs = set() + for item in have["domain_name"]: + if item["vrf"] and item["vrf"] not in vrfs: + vrfs.add(item["vrf"]) + commands.append("no ip domain name vrf %s" % item["vrf"]) + elif None not in vrfs: + vrfs.add(None) + commands.append("no ip domain name") + vrfs = set() + for item in have["domain_search"]: + if item["vrf"] and item["vrf"] not in vrfs: + vrfs.add(item["vrf"]) + commands.append("no ip domain list vrf %s" % item["vrf"]) + elif None not in vrfs: + vrfs.add(None) + commands.append("no ip domain list") + vrfs = set() + for item in have["name_servers"]: + if item["vrf"] and item["vrf"] not in vrfs: + vrfs.add(item["vrf"]) + commands.append("no ip name-server vrf %s" % item["vrf"]) + elif None not in vrfs: + vrfs.add(None) + commands.append("no ip name-server") + elif state == "present": + if needs_update("hostname"): + commands.append("hostname %s" % want["hostname"]) + if needs_update("lookup_source"): + commands.append( + "ip domain lookup source-interface %s" % want["lookup_source"] + ) + if needs_update("lookup_enabled"): + cmd = "ip domain lookup" + if want["lookup_enabled"] is False: + cmd = "no %s" % cmd + commands.append(cmd) + if want["domain_name"]: + adds, removes = diff_list(want["domain_name"], have["domain_name"]) + for item in removes: + if item["vrf"]: + commands.append( + "no ip domain name vrf %s %s" + % (item["vrf"], item["name"]) + ) + else: + commands.append("no ip domain name %s" % item["name"]) + for item in adds: + if item["vrf"]: + requires_vrf(module, item["vrf"]) + commands.append( + "ip domain name vrf %s %s" + % (item["vrf"], item["name"]) + ) + else: + commands.append("ip domain name %s" % item["name"]) + if want["domain_search"]: + adds, removes = diff_list( + want["domain_search"], have["domain_search"] + ) + for item in removes: + if item["vrf"]: + commands.append( + "no ip domain list vrf %s %s" + % (item["vrf"], item["name"]) + ) + else: + commands.append("no ip domain list %s" % item["name"]) + for item in adds: + if item["vrf"]: + requires_vrf(module, item["vrf"]) + commands.append( + "ip domain list vrf %s %s" + % (item["vrf"], item["name"]) + ) + else: + commands.append("ip domain list %s" % item["name"]) + if want["name_servers"]: + adds, removes = diff_list( + want["name_servers"], have["name_servers"] + ) + for item in removes: + if item["vrf"]: + commands.append( + "no ip name-server vrf %s %s" + % (item["vrf"], item["server"]) + ) + else: + commands.append("no ip name-server %s" % item["server"]) + for item in adds: + if item["vrf"]: + requires_vrf(module, item["vrf"]) + commands.append( + "ip name-server vrf %s %s" + % (item["vrf"], item["server"]) + ) + else: + commands.append("ip name-server %s" % item["server"]) + return commands + + +def parse_hostname(config): + match = re.search("^hostname (\\S+)", config, re.M) + return match.group(1) + + +def parse_domain_name(config): + match = re.findall( + "^ip domain[- ]name (?:vrf (\\S+) )*(\\S+)", config, re.M + ) + matches = list() + for vrf, name in match: + if not vrf: + vrf = None + matches.append({"name": name, "vrf": vrf}) + return matches + + +def parse_domain_search(config): + match = re.findall( + "^ip domain[- ]list (?:vrf (\\S+) )*(\\S+)", config, re.M + ) + matches = list() + for vrf, name in match: + if not vrf: + vrf = None + matches.append({"name": name, "vrf": vrf}) + return matches + + +def parse_name_servers(config): + match = re.findall("^ip name-server (?:vrf (\\S+) )*(.*)", config, re.M) + matches = list() + for vrf, servers in match: + if not vrf: + vrf = None + for server in servers.split(): + matches.append({"server": server, "vrf": vrf}) + return matches + + +def parse_lookup_source(config): + match = re.search( + "ip domain[- ]lookup source-interface (\\S+)", config, re.M + ) + if match: + return match.group(1) + + +def map_config_to_obj(module): + config = get_config(module) + return { + "hostname": parse_hostname(config), + "domain_name": parse_domain_name(config), + "domain_search": parse_domain_search(config), + "lookup_source": parse_lookup_source(config), + "lookup_enabled": "no ip domain lookup" not in config + and "no ip domain-lookup" not in config, + "name_servers": parse_name_servers(config), + } + + +def map_params_to_obj(module): + obj = { + "hostname": module.params["hostname"], + "lookup_source": module.params["lookup_source"], + "lookup_enabled": module.params["lookup_enabled"], + } + domain_name = ComplexList(dict(name=dict(key=True), vrf=dict()), module) + domain_search = ComplexList(dict(name=dict(key=True), vrf=dict()), module) + name_servers = ComplexList(dict(server=dict(key=True), vrf=dict()), module) + for arg, cast in [ + ("domain_name", domain_name), + ("domain_search", domain_search), + ("name_servers", name_servers), + ]: + if module.params[arg]: + obj[arg] = cast(module.params[arg]) + else: + obj[arg] = None + return obj + + +def main(): + """ Main entry point for Ansible module execution + """ + argument_spec = dict( + hostname=dict(), + domain_name=dict(type="list", elements="raw"), + domain_search=dict(type="list", elements="raw"), + name_servers=dict(type="list", elements="raw"), + lookup_source=dict(), + lookup_enabled=dict(type="bool"), + state=dict(choices=["present", "absent"], default="present"), + ) + argument_spec.update(ios_argument_spec) + module = AnsibleModule( + argument_spec=argument_spec, supports_check_mode=True + ) + result = {"changed": False} + warnings = list() + result["warnings"] = warnings + want = map_params_to_obj(module) + have = map_config_to_obj(module) + commands = map_obj_to_commands(want, have, module) + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_user.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_user.py new file mode 100644 index 00000000..8c94ef96 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_user.py @@ -0,0 +1,616 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_user +author: Trishna Guha (@trishnaguha) +short_description: Manage the aggregate of local users on Cisco IOS device +description: +- This module provides declarative management of the local usernames configured on + network devices. It allows playbooks to manage either individual usernames or the + aggregate of usernames in the current running config. It also supports purging usernames + from the configuration that are not explicitly defined. +version_added: 1.0.0 +notes: +- Tested against IOS 15.6 +options: + aggregate: + description: + - The set of username objects to be configured on the remote Cisco IOS device. + The list entries can either be the username or a hash of username and properties. + This argument is mutually exclusive with the C(name) argument. + aliases: + - users + - collection + type: list + elements: dict + suboptions: + name: + description: + - The username to be configured on the Cisco IOS device. This argument accepts + a string value and is mutually exclusive with the C(aggregate) argument. Please + note that this option is not same as C(provider username). + type: str + required: true + configured_password: + description: + - The password to be configured on the Cisco IOS device. The password needs to + be provided in clear and it will be encrypted on the device. Please note that + this option is not same as C(provider password). + type: str + update_password: + description: + - Since passwords are encrypted in the device running config, this argument will + instruct the module when to change the password. When set to C(always), the + password will always be updated in the device and when set to C(on_create) the + password will be updated only if the username is created. + choices: + - on_create + - always + type: str + password_type: + description: + - This argument determines whether a 'password' or 'secret' will be configured. + choices: + - secret + - password + type: str + hashed_password: + description: + - This option allows configuring hashed passwords on Cisco IOS devices. + type: dict + suboptions: + type: + description: + - Specifies the type of hash (e.g., 5 for MD5, 8 for PBKDF2, etc.) + - For this to work, the device needs to support the desired hash type + type: int + required: true + value: + description: + - The actual hashed password to be configured on the device + required: true + type: str + privilege: + description: + - The C(privilege) argument configures the privilege level of the user when logged + into the system. This argument accepts integer values in the range of 1 to 15. + type: int + view: + description: + - Configures the view for the username in the device running configuration. The + argument accepts a string value defining the view name. This argument does not + check if the view has been configured on the device. + aliases: + - role + type: str + sshkey: + description: + - Specifies one or more SSH public key(s) to configure for the given username. + - This argument accepts a valid SSH key value. + type: list + elements: str + nopassword: + description: + - Defines the username without assigning a password. This will allow the user + to login to the system without being authenticated by a password. + type: bool + state: + description: + - Configures the state of the username definition as it relates to the device + operational configuration. When set to I(present), the username(s) should be + configured in the device active configuration and when set to I(absent) the + username(s) should not be in the device active configuration + choices: + - present + - absent + type: str + name: + description: + - The username to be configured on the Cisco IOS device. This argument accepts + a string value and is mutually exclusive with the C(aggregate) argument. Please + note that this option is not same as C(provider username). + type: str + configured_password: + description: + - The password to be configured on the Cisco IOS device. The password needs to + be provided in clear and it will be encrypted on the device. Please note that + this option is not same as C(provider password). + type: str + update_password: + description: + - Since passwords are encrypted in the device running config, this argument will + instruct the module when to change the password. When set to C(always), the + password will always be updated in the device and when set to C(on_create) the + password will be updated only if the username is created. + default: always + choices: + - on_create + - always + type: str + password_type: + description: + - This argument determines whether a 'password' or 'secret' will be configured. + default: secret + choices: + - secret + - password + type: str + hashed_password: + description: + - This option allows configuring hashed passwords on Cisco IOS devices. + type: dict + suboptions: + type: + description: + - Specifies the type of hash (e.g., 5 for MD5, 8 for PBKDF2, etc.) + - For this to work, the device needs to support the desired hash type + type: int + required: true + value: + description: + - The actual hashed password to be configured on the device + required: true + type: str + privilege: + description: + - The C(privilege) argument configures the privilege level of the user when logged + into the system. This argument accepts integer values in the range of 1 to 15. + type: int + view: + description: + - Configures the view for the username in the device running configuration. The + argument accepts a string value defining the view name. This argument does not + check if the view has been configured on the device. + aliases: + - role + type: str + sshkey: + description: + - Specifies one or more SSH public key(s) to configure for the given username. + - This argument accepts a valid SSH key value. + type: list + elements: str + nopassword: + description: + - Defines the username without assigning a password. This will allow the user + to login to the system without being authenticated by a password. + type: bool + purge: + description: + - Instructs the module to consider the resource definition absolute. It will remove + any previously configured usernames on the device with the exception of the + `admin` user (the current defined set of users). + type: bool + default: false + state: + description: + - Configures the state of the username definition as it relates to the device + operational configuration. When set to I(present), the username(s) should be + configured in the device active configuration and when set to I(absent) the + username(s) should not be in the device active configuration + default: present + choices: + - present + - absent + type: str +extends_documentation_fragment: +- cisco.ios.ios +""" +EXAMPLES = """ +- name: create a new user + cisco.ios.ios_user: + name: ansible + nopassword: true + sshkey: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" + state: present + +- name: create a new user with multiple keys + cisco.ios.ios_user: + name: ansible + sshkey: + - "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" + - "{{ lookup('file', '~/path/to/public_key') }}" + state: present + +- name: remove all users except admin + cisco.ios.ios_user: + purge: yes + +- name: remove all users except admin and these listed users + cisco.ios.ios_user: + aggregate: + - name: testuser1 + - name: testuser2 + - name: testuser3 + purge: yes + +- name: set multiple users to privilege level 15 + cisco.ios.ios_user: + aggregate: + - name: netop + - name: netend + privilege: 15 + state: present + +- name: set user view/role + cisco.ios.ios_user: + name: netop + view: network-operator + state: present + +- name: Change Password for User netop + cisco.ios.ios_user: + name: netop + configured_password: '{{ new_password }}' + update_password: always + state: present + +- name: Aggregate of users + cisco.ios.ios_user: + aggregate: + - name: ansibletest2 + - name: ansibletest3 + view: network-admin + +- name: Add a user specifying password type + cisco.ios.ios_user: + name: ansibletest4 + configured_password: '{{ new_password }}' + password_type: password + +- name: Add a user with MD5 hashed password + cisco.ios.ios_user: + name: ansibletest5 + hashed_password: + type: 5 + value: $3$8JcDilcYgFZi.yz4ApaqkHG2.8/ + +- name: Delete users with aggregate + cisco.ios.ios_user: + aggregate: + - name: ansibletest1 + - name: ansibletest2 + - name: ansibletest3 + state: absent +""" +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - username ansible secret password + - username admin secret admin +""" +import base64 +import hashlib +import re +from copy import deepcopy +from functools import partial +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) +from ansible.module_utils.six import iteritems + + +def validate_privilege(value, module): + if value and not 1 <= value <= 15: + module.fail_json( + msg="privilege must be between 1 and 15, got %s" % value + ) + + +def user_del_cmd(username): + return { + "command": "no username %s" % username, + "prompt": "This operation will remove all username related configurations with same name", + "answer": "y", + "newline": False, + } + + +def sshkey_fingerprint(sshkey): + # IOS will accept a MD5 fingerprint of the public key + # and is easier to configure in a single line + # we calculate this fingerprint here + if not sshkey: + return None + if " " in sshkey: + # ssh-rsa AAA...== comment + keyparts = sshkey.split(" ") + keyparts[1] = ( + hashlib.md5(base64.b64decode(keyparts[1])).hexdigest().upper() + ) + return " ".join(keyparts) + else: + # just the key, assume rsa type + return ( + "ssh-rsa %s" + % hashlib.md5(base64.b64decode(sshkey)).hexdigest().upper() + ) + + +def map_obj_to_commands(updates, module): + commands = list() + update_password = module.params["update_password"] + password_type = module.params["password_type"] + + def needs_update(want, have, x): + return want.get(x) and want.get(x) != have.get(x) + + def add(command, want, x): + command.append("username %s %s" % (want["name"], x)) + + def add_hashed_password(command, want, x): + command.append( + "username %s secret %s %s" + % (want["name"], x.get("type"), x.get("value")) + ) + + def add_ssh(command, want, x=None): + command.append("ip ssh pubkey-chain") + if x: + command.append("username %s" % want["name"]) + for item in x: + command.append("key-hash %s" % item) + command.append("exit") + else: + command.append("no username %s" % want["name"]) + command.append("exit") + + for update in updates: + want, have = update + if want["state"] == "absent": + if have["sshkey"]: + add_ssh(commands, want) + else: + commands.append(user_del_cmd(want["name"])) + if needs_update(want, have, "view"): + add(commands, want, "view %s" % want["view"]) + if needs_update(want, have, "privilege"): + add(commands, want, "privilege %s" % want["privilege"]) + if needs_update(want, have, "sshkey"): + add_ssh(commands, want, want["sshkey"]) + if needs_update(want, have, "configured_password"): + if update_password == "always" or not have: + if have and password_type != have["password_type"]: + module.fail_json( + msg="Can not have both a user password and a user secret." + + " Please choose one or the other." + ) + add( + commands, + want, + "%s %s" % (password_type, want["configured_password"]), + ) + if needs_update(want, have, "hashed_password"): + add_hashed_password(commands, want, want["hashed_password"]) + if needs_update(want, have, "nopassword"): + if want["nopassword"]: + add(commands, want, "nopassword") + else: + add(commands, want, user_del_cmd(want["name"])) + return commands + + +def parse_view(data): + match = re.search("view (\\S+)", data, re.M) + if match: + return match.group(1) + + +def parse_sshkey(data, user): + sshregex = "username %s(\\n\\s+key-hash .+$)+" % user + sshcfg = re.search(sshregex, data, re.M) + key_list = [] + if sshcfg: + match = re.findall( + "key-hash (\\S+ \\S+(?: .+)?)$", sshcfg.group(), re.M + ) + if match: + key_list = match + return key_list + + +def parse_privilege(data): + match = re.search("privilege (\\S+)", data, re.M) + if match: + return int(match.group(1)) + + +def parse_password_type(data): + type = None + if data and data.split()[-3] in ["password", "secret"]: + type = data.split()[-3] + return type + + +def map_config_to_obj(module): + data = get_config(module, flags=["| section username"]) + match = re.findall("(?:^(?:u|\\s{2}u))sername (\\S+)", data, re.M) + if not match: + return list() + instances = list() + for user in set(match): + regex = "username %s .+$" % user + cfg = re.findall(regex, data, re.M) + cfg = "\n".join(cfg) + obj = { + "name": user, + "state": "present", + "nopassword": "nopassword" in cfg, + "configured_password": None, + "hashed_password": None, + "password_type": parse_password_type(cfg), + "sshkey": parse_sshkey(data, user), + "privilege": parse_privilege(cfg), + "view": parse_view(cfg), + } + instances.append(obj) + return instances + + +def get_param_value(key, item, module): + # if key doesn't exist in the item, get it from module.params + if not item.get(key): + value = module.params[key] + # if key does exist, do a type check on it to validate it + else: + value_type = module.argument_spec[key].get("type", "str") + type_checker = module._CHECK_ARGUMENT_TYPES_DISPATCHER[value_type] + type_checker(item[key]) + value = item[key] + # validate the param value (if validator func exists) + validator = globals().get("validate_%s" % key) + if all((value, validator)): + validator(value, module) + return value + + +def map_params_to_obj(module): + users = module.params["aggregate"] + if not users: + if not module.params["name"] and module.params["purge"]: + return list() + elif not module.params["name"]: + module.fail_json(msg="username is required") + else: + aggregate = [{"name": module.params["name"]}] + else: + aggregate = list() + for item in users: + if not isinstance(item, dict): + aggregate.append({"name": item}) + elif "name" not in item: + module.fail_json(msg="name is required") + else: + aggregate.append(item) + objects = list() + for item in aggregate: + get_value = partial(get_param_value, item=item, module=module) + item["configured_password"] = get_value("configured_password") + item["hashed_password"] = get_value("hashed_password") + item["nopassword"] = get_value("nopassword") + item["privilege"] = get_value("privilege") + item["view"] = get_value("view") + item["sshkey"] = render_key_list(get_value("sshkey")) + item["state"] = get_value("state") + objects.append(item) + return objects + + +def render_key_list(ssh_keys): + key_list = [] + if ssh_keys: + for item in ssh_keys: + key_list.append(sshkey_fingerprint(item)) + return key_list + + +def update_objects(want, have): + updates = list() + for entry in want: + item = next((i for i in have if i["name"] == entry["name"]), None) + if all((item is None, entry["state"] == "present")): + updates.append((entry, {})) + elif item: + for key, value in iteritems(entry): + if value and value != item[key]: + updates.append((entry, item)) + return updates + + +def main(): + """ main entry point for module execution + """ + hashed_password_spec = dict( + type=dict(type="int", required=True), + value=dict(no_log=True, required=True), + ) + element_spec = dict( + name=dict(), + configured_password=dict(no_log=True), + hashed_password=dict( + no_log=True, type="dict", options=hashed_password_spec + ), + nopassword=dict(type="bool"), + update_password=dict( + default="always", choices=["on_create", "always"] + ), + password_type=dict(default="secret", choices=["secret", "password"]), + privilege=dict(type="int"), + view=dict(aliases=["role"]), + sshkey=dict(type="list", elements="str"), + state=dict(default="present", choices=["present", "absent"]), + ) + aggregate_spec = deepcopy(element_spec) + aggregate_spec["name"] = dict(required=True) + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + argument_spec = dict( + aggregate=dict( + type="list", + elements="dict", + options=aggregate_spec, + aliases=["users", "collection"], + ), + purge=dict(type="bool", default=False), + ) + argument_spec.update(element_spec) + argument_spec.update(ios_argument_spec) + mutually_exclusive = [ + ("name", "aggregate"), + ("nopassword", "hashed_password", "configured_password"), + ] + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + warnings = list() + result = {"changed": False, "warnings": warnings} + want = map_params_to_obj(module) + have = map_config_to_obj(module) + commands = map_obj_to_commands(update_objects(want, have), module) + if module.params["purge"]: + want_users = [x["name"] for x in want] + have_users = [x["name"] for x in have] + for item in set(have_users).difference(want_users): + if item != "admin": + commands.append(user_del_cmd(item)) + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_vlan.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_vlan.py new file mode 100644 index 00000000..f18f3027 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_vlan.py @@ -0,0 +1,416 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_vlan +author: Trishna Guha (@trishnaguha) +short_description: (deprecated, removed after 2022-06-01) Manage VLANs on IOS network + devices +description: +- This module provides declarative management of VLANs on Cisco IOS network devices. +version_added: 1.0.0 +deprecated: + alternative: ios_vlans + why: Newer and updated modules released with more functionality in Ansible 2.9 + removed_at_date: '2022-06-01' +notes: +- Tested against IOS 15.2 +options: + name: + description: + - Name of the VLAN. + type: str + vlan_id: + description: + - ID of the VLAN. Range 1-4094. + type: int + interfaces: + description: + - List of interfaces that should be associated to the VLAN. + type: list + elements: str + associated_interfaces: + description: + - This is a intent option and checks the operational state of the for given vlan + C(name) for associated interfaces. If the value in the C(associated_interfaces) + does not match with the operational state of vlan interfaces on device it will + result in failure. + type: list + elements: str + delay: + description: + - Delay the play should wait to check for declarative intent params values. + default: 10 + type: int + aggregate: + description: List of VLANs definitions. + type: list + elements: dict + suboptions: + name: + description: + - Name of the VLAN. + type: str + vlan_id: + description: + - ID of the VLAN. Range 1-4094. + required: true + type: str + interfaces: + description: + - List of interfaces that should be associated to the VLAN. + type: list + elements: str + associated_interfaces: + description: + - This is a intent option and checks the operational state of the for given vlan + C(name) for associated interfaces. If the value in the C(associated_interfaces) + does not match with the operational state of vlan interfaces on device it will + result in failure. + type: list + elements: str + delay: + description: + - Delay the play should wait to check for declarative intent params values. + type: int + state: + description: + - State of the VLAN configuration. + type: str + choices: + - present + - absent + - active + - suspend + purge: + description: + - Purge VLANs not defined in the I(aggregate) parameter. + default: false + type: bool + state: + description: + - State of the VLAN configuration. + default: present + choices: + - present + - absent + - active + - suspend + type: str +extends_documentation_fragment: +- cisco.ios.ios + + +""" +EXAMPLES = """ +- name: Create vlan + cisco.ios.ios_vlan: + vlan_id: 100 + name: test-vlan + state: present + +- name: Add interfaces to VLAN + cisco.ios.ios_vlan: + vlan_id: 100 + interfaces: + - GigabitEthernet0/0 + - GigabitEthernet0/1 + +- name: Check if interfaces is assigned to VLAN + cisco.ios.ios_vlan: + vlan_id: 100 + associated_interfaces: + - GigabitEthernet0/0 + - GigabitEthernet0/1 + +- name: Delete vlan + cisco.ios.ios_vlan: + vlan_id: 100 + state: absent + +- name: Add vlan using aggregate + cisco.ios.ios_vlan: + aggregate: + - {vlan_id: 100, name: test-vlan, interfaces: [GigabitEthernet0/1, GigabitEthernet0/2], + delay: 15, state: suspend} + - {vlan_id: 101, name: test-vlan, interfaces: GigabitEthernet0/3} + +- name: Move interfaces to a different VLAN + cisco.ios.ios_vlan: + vlan_id: 102 + interfaces: + - GigabitEthernet0/0 + - GigabitEthernet0/1 +""" +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - vlan 100 + - name test-vlan +""" +import re +import time +from copy import deepcopy +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + load_config, + run_commands, + normalize_interface, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) + + +def search_obj_in_list(vlan_id, lst): + for o in lst: + if o["vlan_id"] == vlan_id: + return o + + +def map_obj_to_commands(updates, module): + commands = list() + want, have = updates + purge = module.params["purge"] + for w in want: + vlan_id = w["vlan_id"] + name = w["name"] + interfaces = w["interfaces"] + state = w["state"] + obj_in_have = search_obj_in_list(vlan_id, have) + if state == "absent": + if obj_in_have: + commands.append("no vlan {0}".format(vlan_id)) + elif state == "present": + if not obj_in_have: + commands.append("vlan {0}".format(vlan_id)) + if name: + commands.append("name {0}".format(name)) + if interfaces: + for i in interfaces: + commands.append("interface {0}".format(i)) + commands.append("switchport mode access") + commands.append( + "switchport access vlan {0}".format(vlan_id) + ) + else: + if name: + if name != obj_in_have["name"]: + commands.append("vlan {0}".format(vlan_id)) + commands.append("name {0}".format(name)) + if interfaces: + if not obj_in_have["interfaces"]: + for i in interfaces: + commands.append("vlan {0}".format(vlan_id)) + commands.append("interface {0}".format(i)) + commands.append("switchport mode access") + commands.append( + "switchport access vlan {0}".format(vlan_id) + ) + elif set(interfaces) != set(obj_in_have["interfaces"]): + missing_interfaces = list( + set(interfaces) - set(obj_in_have["interfaces"]) + ) + for i in missing_interfaces: + commands.append("vlan {0}".format(vlan_id)) + commands.append("interface {0}".format(i)) + commands.append("switchport mode access") + commands.append( + "switchport access vlan {0}".format(vlan_id) + ) + superfluous_interfaces = list( + set(obj_in_have["interfaces"]) - set(interfaces) + ) + for i in superfluous_interfaces: + commands.append("vlan {0}".format(vlan_id)) + commands.append("interface {0}".format(i)) + commands.append("switchport mode access") + commands.append( + "no switchport access vlan {0}".format(vlan_id) + ) + else: + commands.append("vlan {0}".format(vlan_id)) + if name: + commands.append("name {0}".format(name)) + commands.append("state {0}".format(state)) + if purge: + for h in have: + obj_in_want = search_obj_in_list(h["vlan_id"], want) + if not obj_in_want and h["vlan_id"] != "1": + commands.append("no vlan {0}".format(h["vlan_id"])) + return commands + + +def map_params_to_obj(module): + obj = [] + aggregate = module.params.get("aggregate") + if aggregate: + for item in aggregate: + for key in item: + if item.get(key) is None: + item[key] = module.params[key] + d = item.copy() + d["vlan_id"] = str(d["vlan_id"]) + obj.append(d) + else: + obj.append( + { + "vlan_id": str(module.params["vlan_id"]), + "name": module.params["name"], + "interfaces": module.params["interfaces"], + "associated_interfaces": module.params[ + "associated_interfaces" + ], + "state": module.params["state"], + } + ) + return obj + + +def parse_to_logical_rows(out): + started_yielding = False + cur_row = [] + for l in out.splitlines()[2:]: + if not l: + """Skip empty lines.""" + continue + if "0" < l[0] <= "9": + """Line starting with a number.""" + if started_yielding: + yield cur_row + cur_row = [] # Reset it to hold a next chunk + started_yielding = True + cur_row.append(l) + yield cur_row + + +def map_ports_str_to_list(ports_str): + return list( + filter( + bool, + (normalize_interface(p.strip()) for p in ports_str.split(", ")), + ) + ) + + +def parse_to_obj(logical_rows): + first_row = logical_rows[0] + rest_rows = logical_rows[1:] + obj = re.match( + "(?P<vlan_id>\\d+)\\s+(?P<name>[^\\s]+)\\s+(?P<state>[^\\s]+)\\s*(?P<interfaces>.*)", + first_row, + ).groupdict() + if obj["state"] == "suspended": + obj["state"] = "suspend" + obj["interfaces"] = map_ports_str_to_list(obj["interfaces"]) + obj["interfaces"].extend( + prts_r for prts in rest_rows for prts_r in map_ports_str_to_list(prts) + ) + return obj + + +def parse_vlan_brief(vlan_out): + return [parse_to_obj(r) for r in parse_to_logical_rows(vlan_out)] + + +def map_config_to_obj(module): + return parse_vlan_brief(run_commands(module, ["show vlan brief"])[0]) + + +def check_declarative_intent_params(want, module, result): + have = None + is_delay = False + for w in want: + if w.get("associated_interfaces") is None: + continue + if result["changed"] and not is_delay: + time.sleep(module.params["delay"]) + is_delay = True + if have is None: + have = map_config_to_obj(module) + for i in w["associated_interfaces"]: + obj_in_have = search_obj_in_list(w["vlan_id"], have) + if ( + obj_in_have + and "interfaces" in obj_in_have + and i not in obj_in_have["interfaces"] + ): + module.fail_json( + msg="Interface %s not configured on vlan %s" + % (i, w["vlan_id"]) + ) + + +def main(): + """ main entry point for module execution + """ + element_spec = dict( + vlan_id=dict(type="int"), + name=dict(), + interfaces=dict(type="list", elements="str"), + associated_interfaces=dict(type="list", elements="str"), + delay=dict(default=10, type="int"), + state=dict( + default="present", + choices=["present", "absent", "active", "suspend"], + ), + ) + aggregate_spec = deepcopy(element_spec) + aggregate_spec["vlan_id"] = dict(required=True) + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + argument_spec = dict( + aggregate=dict(type="list", elements="dict", options=aggregate_spec), + purge=dict(default=False, type="bool"), + ) + argument_spec.update(element_spec) + argument_spec.update(ios_argument_spec) + required_one_of = [["vlan_id", "aggregate"]] + mutually_exclusive = [["vlan_id", "aggregate"]] + module = AnsibleModule( + argument_spec=argument_spec, + required_one_of=required_one_of, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + warnings = list() + result = {"changed": False} + if warnings: + result["warnings"] = warnings + want = map_params_to_obj(module) + have = map_config_to_obj(module) + commands = map_obj_to_commands((want, have), module) + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + check_declarative_intent_params(want, module, result) + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_vlans.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_vlans.py new file mode 100644 index 00000000..d215a62e --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_vlans.py @@ -0,0 +1,748 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +""" +The module file for ios_vlans +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_vlans +short_description: VLANs resource module +description: This module provides declarative management of VLANs on Cisco IOS network + devices. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: +- Tested against Cisco IOSl2 device with Version 15.2 on VIRL. +options: + config: + description: A dictionary of VLANs options + type: list + elements: dict + suboptions: + name: + description: + - Ascii name of the VLAN. + - NOTE, I(name) should not be named/appended with I(default) as it is reserved + for device default vlans. + type: str + vlan_id: + description: + - ID of the VLAN. Range 1-4094 + type: int + required: true + mtu: + description: + - VLAN Maximum Transmission Unit. + - Refer to vendor documentation for valid values. + type: int + state: + description: + - Operational state of the VLAN + type: str + choices: + - active + - suspend + remote_span: + description: + - Configure as Remote SPAN VLAN + type: bool + shutdown: + description: + - Shutdown VLAN switching. + type: str + choices: + - enabled + - disabled + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device + by executing the command B(show vlan). + - 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 + - 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 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 running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + default: merged +""" +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 + +- name: Merge provided configuration with device configuration + cisco.ios.ios_vlans: + config: + - name: Vlan_10 + vlan_id: 10 + state: active + shutdown: disabled + remote_span: 10 + - name: Vlan_20 + vlan_id: 20 + mtu: 610 + state: active + shutdown: enabled + - name: Vlan_30 + vlan_id: 30 + state: suspend + shutdown: enabled + state: merged + +# After state: +# ------------ +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +# Using overridden + +# Before state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +- name: Override device configuration of all VLANs with provided configuration + cisco.ios.ios_vlans: + config: + - name: Vlan_10 + vlan_id: 10 + mtu: 1000 + state: overridden + +# After state: +# ------------ +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 10 Vlan_10 active +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 10 enet 100010 1000 - - - - - 0 0 + +# Using replaced + +# Before state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +- name: Replaces device configuration of listed VLANs with provided configuration + cisco.ios.ios_vlans: + config: + - vlan_id: 20 + name: Test_VLAN20 + mtu: 700 + shutdown: disabled + - vlan_id: 30 + name: Test_VLAN30 + mtu: 1000 + state: replaced + +# After state: +# ------------ +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 Test_VLAN20 active +# 30 Test_VLAN30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 700 - - - - - 0 0 +# 30 enet 100030 1000 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +# Using deleted + +# Before state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +- name: Delete attributes of given VLANs + cisco.ios.ios_vlans: + config: + - vlan_id: 10 + - vlan_id: 20 + state: deleted + +# After state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured vlans attributes)" + +# Before state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +- name: Delete attributes of ALL VLANs + cisco.ios.ios_vlans: + state: deleted + +# After state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 + +# Using Gathered + +# Before state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +- name: Gather listed vlans with provided configurations + cisco.ios.ios_vlans: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "mtu": 1500, +# "name": "default", +# "shutdown": "disabled", +# "state": "active", +# "vlan_id": 1 +# }, +# { +# "mtu": 1500, +# "name": "VLAN0010", +# "shutdown": "disabled", +# "state": "active", +# "vlan_id": 10 +# }, +# { +# "mtu": 1500, +# "name": "VLAN0020", +# "shutdown": "disabled", +# "state": "active", +# "vlan_id": 20 +# }, +# { +# "mtu": 1500, +# "name": "VLAN0030", +# "shutdown": "disabled", +# "state": "active", +# "vlan_id": 30 +# }, +# { +# "mtu": 1500, +# "name": "fddi-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1002 +# }, +# { +# "mtu": 1500, +# "name": "token-ring-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1003 +# }, +# { +# "mtu": 1500, +# "name": "fddinet-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1004 +# }, +# { +# "mtu": 1500, +# "name": "trnet-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1005 +# } +# ] + +# After state: +# ------------ +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_vlans: + config: + - name: Vlan_10 + vlan_id: 10 + state: active + shutdown: disabled + remote_span: 10 + - name: Vlan_20 + vlan_id: 20 + mtu: 610 + state: active + shutdown: enabled + - name: Vlan_30 + vlan_id: 30 + state: suspend + shutdown: enabled + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "vlan 10", +# "name Vlan_10", +# "state active", +# "remote-span", +# "no shutdown", +# "vlan 20", +# "name Vlan_20", +# "state active", +# "mtu 610", +# "shutdown", +# "vlan 30", +# "name Vlan_30", +# "state suspend", +# "shutdown" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 1500 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 + +- name: Parse the commands for provided configuration + cisco.ios.ios_vlans: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "mtu": 1500, +# "name": "default", +# "shutdown": "disabled", +# "state": "active", +# "vlan_id": 1 +# }, +# { +# "mtu": 1500, +# "name": "vlan_10", +# "shutdown": "disabled", +# "state": "active", +# "vlan_id": 10 +# }, +# { +# "mtu": 1500, +# "name": "vlan_20", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 20 +# }, +# { +# "mtu": 1500, +# "name": "vlan_30", +# "shutdown": "enabled", +# "state": "suspend", +# "vlan_id": 30 +# }, +# { +# "mtu": 1500, +# "name": "fddi-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1002 +# }, +# { +# "mtu": 1500, +# "name": "token-ring-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1003 +# }, +# { +# "mtu": 1500, +# "name": "fddinet-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1004 +# }, +# { +# "mtu": 1500, +# "name": "trnet-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1005 +# } +# ] + +""" +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: ['vlan 20', 'name vlan_20', 'mtu 600', 'remote-span'] +""" +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vlans.vlans import ( + VlansArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.vlans.vlans import ( + Vlans, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "overridden", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=VlansArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Vlans(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_vrf.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_vrf.py new file mode 100644 index 00000000..b86df5a0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/modules/ios_vrf.py @@ -0,0 +1,750 @@ +#!/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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: ios_vrf +author: Peter Sprygada (@privateip) +short_description: Manage the collection of VRF definitions on Cisco IOS devices +description: +- This module provides declarative management of VRF definitions on Cisco IOS devices. It + allows playbooks to manage individual or the entire VRF collection. It also supports + purging VRF definitions from the configuration that are not explicitly defined. +version_added: 1.0.0 +extends_documentation_fragment: +- cisco.ios.ios +notes: +- Tested against IOS 15.6 +options: + vrfs: + description: + - The set of VRF definition objects to be configured on the remote IOS device. Ths + list entries can either be the VRF name or a hash of VRF definitions and attributes. This + argument is mutually exclusive with the C(name) argument. + type: list + elements: raw + name: + description: + - The name of the VRF definition to be managed on the remote IOS device. The + VRF definition name is an ASCII string name used to uniquely identify the VRF. This + argument is mutually exclusive with the C(vrfs) argument + type: str + description: + description: + - Provides a short description of the VRF definition in the current active configuration. The + VRF definition value accepts alphanumeric characters used to provide additional + information about the VRF. + type: str + rd: + description: + - The router-distinguisher value uniquely identifies the VRF to routing processes + on the remote IOS system. The RD value takes the form of C(A:B) where C(A) + and C(B) are both numeric values. + type: str + interfaces: + description: + - Identifies the set of interfaces that should be configured in the VRF. Interfaces + must be routed interfaces in order to be placed into a VRF. + type: list + elements: str + associated_interfaces: + description: + - This is a intent option and checks the operational state of the for given vrf + C(name) for associated interfaces. If the value in the C(associated_interfaces) + does not match with the operational state of vrf interfaces on device it will + result in failure. + type: list + elements: str + delay: + description: + - Time in seconds to wait before checking for the operational state on remote + device. + default: 10 + type: int + purge: + description: + - Instructs the module to consider the VRF definition absolute. It will remove + any previously configured VRFs on the device. + default: false + type: bool + state: + description: + - Configures the state of the VRF definition as it relates to the device operational + configuration. When set to I(present), the VRF should be configured in the + device active configuration and when set to I(absent) the VRF should not be + in the device active configuration + default: present + choices: + - present + - absent + type: str + route_both: + description: + - Adds an export and import list of extended route target communities to the VRF. + type: list + elements: str + route_export: + description: + - Adds an export list of extended route target communities to the VRF. + type: list + elements: str + route_import: + description: + - Adds an import list of extended route target communities to the VRF. + type: list + elements: str + route_both_ipv4: + description: + - Adds an export and import list of extended route target communities in address-family + configuration submode to the VRF. + type: list + elements: str + route_export_ipv4: + description: + - Adds an export list of extended route target communities in address-family configuration + submode to the VRF. + type: list + elements: str + route_import_ipv4: + description: + - Adds an import list of extended route target communities in address-family configuration + submode to the VRF. + type: list + elements: str + route_both_ipv6: + description: + - Adds an export and import list of extended route target communities in address-family + configuration submode to the VRF. + type: list + elements: str + route_export_ipv6: + description: + - Adds an export list of extended route target communities in address-family configuration + submode to the VRF. + type: list + elements: str + route_import_ipv6: + description: + - Adds an import list of extended route target communities in address-family configuration + submode to the VRF. + type: list + elements: str +""" +EXAMPLES = """ +- name: configure a vrf named management + cisco.ios.ios_vrf: + name: management + description: oob mgmt vrf + interfaces: + - Management1 + +- name: remove a vrf named test + cisco.ios.ios_vrf: + name: test + state: absent + +- name: configure set of VRFs and purge any others + cisco.ios.ios_vrf: + vrfs: + - red + - blue + - green + purge: yes + +- name: Creates a list of import RTs for the VRF with the same parameters + cisco.ios.ios_vrf: + name: test_import + rd: 1:100 + route_import: + - 1:100 + - 3:100 + +- name: Creates a list of import RTs in address-family configuration submode for the + VRF with the same parameters + cisco.ios.ios_vrf: + name: test_import_ipv4 + rd: 1:100 + route_import_ipv4: + - 1:100 + - 3:100 + +- name: Creates a list of import RTs in address-family configuration submode for the + VRF with the same parameters + cisco.ios.ios_vrf: + name: test_import_ipv6 + rd: 1:100 + route_import_ipv6: + - 1:100 + - 3:100 + +- name: Creates a list of export RTs for the VRF with the same parameters + cisco.ios.ios_vrf: + name: test_export + rd: 1:100 + route_export: + - 1:100 + - 3:100 + +- name: Creates a list of export RTs in address-family configuration submode for the + VRF with the same parameters + cisco.ios.ios_vrf: + name: test_export_ipv4 + rd: 1:100 + route_export_ipv4: + - 1:100 + - 3:100 + +- name: Creates a list of export RTs in address-family configuration submode for the + VRF with the same parameters + cisco.ios.ios_vrf: + name: test_export_ipv6 + rd: 1:100 + route_export_ipv6: + - 1:100 + - 3:100 + +- name: Creates a list of import and export route targets for the VRF with the same + parameters + cisco.ios.ios_vrf: + name: test_both + rd: 1:100 + route_both: + - 1:100 + - 3:100 + +- name: Creates a list of import and export route targets in address-family configuration + submode for the VRF with the same parameters + cisco.ios.ios_vrf: + name: test_both_ipv4 + rd: 1:100 + route_both_ipv4: + - 1:100 + - 3:100 + +- name: Creates a list of import and export route targets in address-family configuration + submode for the VRF with the same parameters + cisco.ios.ios_vrf: + name: test_both_ipv6 + rd: 1:100 + route_both_ipv6: + - 1:100 + - 3:100 +""" +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - vrf definition ansible + - description management vrf + - rd: 1:100 +start: + description: The time the job started + returned: always + type: str + sample: "2016-11-16 10:38:15.126146" +end: + description: The time the job ended + returned: always + type: str + sample: "2016-11-16 10:38:25.595612" +delta: + description: The time elapsed to perform all operations + returned: always + type: str + sample: "0:00:10.469466\" +""" +import re +import time +from functools import partial +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import exec_command +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + load_config, + get_config, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + ios_argument_spec, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( + NetworkConfig, +) +from ansible.module_utils.six import iteritems + + +def get_interface_type(interface): + if interface.upper().startswith("ET"): + return "ethernet" + elif interface.upper().startswith("VL"): + return "svi" + elif interface.upper().startswith("LO"): + return "loopback" + elif interface.upper().startswith("MG"): + return "management" + elif interface.upper().startswith("MA"): + return "management" + elif interface.upper().startswith("PO"): + return "portchannel" + elif interface.upper().startswith("NV"): + return "nve" + else: + return "unknown" + + +def add_command_to_vrf(name, cmd, commands): + if "vrf definition %s" % name not in commands: + commands.extend(["vrf definition %s" % name]) + commands.append(cmd) + + +def map_obj_to_commands(updates, module): + commands = list() + for update in updates: + want, have = update + + def needs_update(want, have, x): + if isinstance(want.get(x), list) and isinstance(have.get(x), list): + return ( + want.get(x) + and want.get(x) != have.get(x) + and not all(elem in have.get(x) for elem in want.get(x)) + ) + return want.get(x) and want.get(x) != have.get(x) + + if want["state"] == "absent": + commands.append("no vrf definition %s" % want["name"]) + continue + if not have.get("state"): + commands.extend(["vrf definition %s" % want["name"]]) + ipv6 = ( + len( + [ + k + for k, v in module.params.items() + if (k.endswith("_ipv6") or k.endswith("_both")) and v + ] + ) + != 0 + ) + ipv4 = ( + len( + [ + k + for k, v in module.params.items() + if (k.endswith("_ipv4") or k.endswith("_both")) and v + ] + ) + != 0 + ) + if ipv4: + commands.extend(["address-family ipv4", "exit"]) + if ipv6: + commands.extend(["address-family ipv6", "exit"]) + if needs_update(want, have, "description"): + cmd = "description %s" % want["description"] + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "rd"): + cmd = "rd %s" % want["rd"] + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "route_import"): + for route in want["route_import"]: + cmd = "route-target import %s" % route + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "route_export"): + for route in want["route_export"]: + cmd = "route-target export %s" % route + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "route_import_ipv4"): + cmd = "address-family ipv4" + add_command_to_vrf(want["name"], cmd, commands) + for route in want["route_import_ipv4"]: + cmd = "route-target import %s" % route + add_command_to_vrf(want["name"], cmd, commands) + cmd = "exit-address-family" + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "route_export_ipv4"): + cmd = "address-family ipv4" + add_command_to_vrf(want["name"], cmd, commands) + for route in want["route_export_ipv4"]: + cmd = "route-target export %s" % route + add_command_to_vrf(want["name"], cmd, commands) + cmd = "exit-address-family" + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "route_import_ipv6"): + cmd = "address-family ipv6" + add_command_to_vrf(want["name"], cmd, commands) + for route in want["route_import_ipv6"]: + cmd = "route-target import %s" % route + add_command_to_vrf(want["name"], cmd, commands) + cmd = "exit-address-family" + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "route_export_ipv6"): + cmd = "address-family ipv6" + add_command_to_vrf(want["name"], cmd, commands) + for route in want["route_export_ipv6"]: + cmd = "route-target export %s" % route + add_command_to_vrf(want["name"], cmd, commands) + cmd = "exit-address-family" + add_command_to_vrf(want["name"], cmd, commands) + if want["interfaces"] is not None: + for intf in set(have.get("interfaces", [])).difference( + want["interfaces"] + ): + commands.extend( + [ + "interface %s" % intf, + "no vrf forwarding %s" % want["name"], + ] + ) + for intf in set(want["interfaces"]).difference( + have.get("interfaces", []) + ): + cfg = get_config(module) + configobj = NetworkConfig(indent=1, contents=cfg) + children = configobj["interface %s" % intf].children + intf_config = "\n".join(children) + commands.extend( + ["interface %s" % intf, "vrf forwarding %s" % want["name"]] + ) + match = re.search("ip address .+", intf_config, re.M) + if match: + commands.append(match.group()) + return commands + + +def parse_description(configobj, name): + cfg = configobj["vrf definition %s" % name] + cfg = "\n".join(cfg.children) + match = re.search("description (.+)$", cfg, re.M) + if match: + return match.group(1) + + +def parse_rd(configobj, name): + cfg = configobj["vrf definition %s" % name] + cfg = "\n".join(cfg.children) + match = re.search("rd (.+)$", cfg, re.M) + if match: + return match.group(1) + + +def parse_interfaces(configobj): + vrf_cfg = "vrf forwarding" + interfaces = dict() + for intf in set(re.findall("^interface .+", str(configobj), re.M)): + for line in configobj[intf].children: + if vrf_cfg in line: + try: + interfaces[line.split()[-1]].append(intf.split(" ")[1]) + except KeyError: + interfaces[line.split()[-1]] = [intf.split(" ")[1]] + return interfaces + + +def parse_import(configobj, name): + cfg = configobj["vrf definition %s" % name] + cfg = "\n".join(cfg.children) + matches = re.findall("route-target\\s+import\\s+(.+)", cfg, re.M) + return matches + + +def parse_export(configobj, name): + cfg = configobj["vrf definition %s" % name] + cfg = "\n".join(cfg.children) + matches = re.findall("route-target\\s+export\\s+(.+)", cfg, re.M) + return matches + + +def parse_both(configobj, name, address_family="global"): + rd_pattern = re.compile("(?P<rd>.+:.+)") + matches = list() + export_match = None + import_match = None + if address_family == "global": + export_match = parse_export(configobj, name) + import_match = parse_import(configobj, name) + elif address_family == "ipv4": + export_match = parse_export_ipv4(configobj, name) + import_match = parse_import_ipv4(configobj, name) + elif address_family == "ipv6": + export_match = parse_export_ipv6(configobj, name) + import_match = parse_import_ipv6(configobj, name) + if import_match and export_match: + for ex in export_match: + exrd = rd_pattern.search(ex) + exrd = exrd.groupdict().get("rd") + for im in import_match: + imrd = rd_pattern.search(im) + imrd = imrd.groupdict().get("rd") + if exrd == imrd: + matches.extend([exrd]) if exrd not in matches else None + matches.extend([imrd]) if imrd not in matches else None + return matches + + +def parse_import_ipv4(configobj, name): + cfg = configobj["vrf definition %s" % name] + try: + subcfg = cfg["address-family ipv4"] + subcfg = "\n".join(subcfg.children) + matches = re.findall("route-target\\s+import\\s+(.+)", subcfg, re.M) + return matches + except KeyError: + pass + + +def parse_export_ipv4(configobj, name): + cfg = configobj["vrf definition %s" % name] + try: + subcfg = cfg["address-family ipv4"] + subcfg = "\n".join(subcfg.children) + matches = re.findall("route-target\\s+export\\s+(.+)", subcfg, re.M) + return matches + except KeyError: + pass + + +def parse_import_ipv6(configobj, name): + cfg = configobj["vrf definition %s" % name] + try: + subcfg = cfg["address-family ipv6"] + subcfg = "\n".join(subcfg.children) + matches = re.findall("route-target\\s+import\\s+(.+)", subcfg, re.M) + return matches + except KeyError: + pass + + +def parse_export_ipv6(configobj, name): + cfg = configobj["vrf definition %s" % name] + try: + subcfg = cfg["address-family ipv6"] + subcfg = "\n".join(subcfg.children) + matches = re.findall("route-target\\s+export\\s+(.+)", subcfg, re.M) + return matches + except KeyError: + pass + + +def map_config_to_obj(module): + config = get_config(module) + configobj = NetworkConfig(indent=1, contents=config) + match = re.findall("^vrf definition (\\S+)", config, re.M) + if not match: + return list() + instances = list() + interfaces = parse_interfaces(configobj) + for item in set(match): + obj = { + "name": item, + "state": "present", + "description": parse_description(configobj, item), + "rd": parse_rd(configobj, item), + "interfaces": interfaces.get(item), + "route_import": parse_import(configobj, item), + "route_export": parse_export(configobj, item), + "route_both": parse_both(configobj, item), + "route_import_ipv4": parse_import_ipv4(configobj, item), + "route_export_ipv4": parse_export_ipv4(configobj, item), + "route_both_ipv4": parse_both( + configobj, item, address_family="ipv4" + ), + "route_import_ipv6": parse_import_ipv6(configobj, item), + "route_export_ipv6": parse_export_ipv6(configobj, item), + "route_both_ipv6": parse_both( + configobj, item, address_family="ipv6" + ), + } + instances.append(obj) + return instances + + +def get_param_value(key, item, module): + # if key doesn't exist in the item, get it from module.params + if not item.get(key): + value = module.params[key] + # if key does exist, do a type check on it to validate it + else: + value_type = module.argument_spec[key].get("type", "str") + type_checker = module._CHECK_ARGUMENT_TYPES_DISPATCHER[value_type] + type_checker(item[key]) + value = item[key] + # validate the param value (if validator func exists) + validator = globals().get("validate_%s" % key) + if validator: + validator(value, module) + return value + + +def map_params_to_obj(module): + vrfs = module.params.get("vrfs") + if not vrfs: + if not module.params["name"] and module.params["purge"]: + return list() + elif not module.params["name"]: + module.fail_json(msg="name is required") + collection = [{"name": module.params["name"]}] + else: + collection = list() + for item in vrfs: + if not isinstance(item, dict): + collection.append({"name": item}) + elif "name" not in item: + module.fail_json(msg="name is required") + else: + collection.append(item) + objects = list() + for item in collection: + get_value = partial(get_param_value, item=item, module=module) + item["description"] = get_value("description") + item["rd"] = get_value("rd") + item["interfaces"] = get_value("interfaces") + item["state"] = get_value("state") + item["route_import"] = get_value("route_import") + item["route_export"] = get_value("route_export") + item["route_both"] = get_value("route_both") + item["route_import_ipv4"] = get_value("route_import_ipv4") + item["route_export_ipv4"] = get_value("route_export_ipv4") + item["route_both_ipv4"] = get_value("route_both_ipv4") + item["route_import_ipv6"] = get_value("route_import_ipv6") + item["route_export_ipv6"] = get_value("route_export_ipv6") + item["route_both_ipv6"] = get_value("route_both_ipv6") + both_addresses_family = ["", "_ipv6", "_ipv4"] + for address_family in both_addresses_family: + if item["route_both%s" % address_family]: + if not item["route_export%s" % address_family]: + item["route_export%s" % address_family] = list() + if not item["route_import%s" % address_family]: + item["route_import%s" % address_family] = list() + item["route_export%s" % address_family].extend( + get_value("route_both%s" % address_family) + ) + item["route_import%s" % address_family].extend( + get_value("route_both%s" % address_family) + ) + item["associated_interfaces"] = get_value("associated_interfaces") + objects.append(item) + return objects + + +def update_objects(want, have): + updates = list() + for entry in want: + item = next((i for i in have if i["name"] == entry["name"]), None) + if all((item is None, entry["state"] == "present")): + updates.append((entry, {})) + else: + for key, value in iteritems(entry): + if value: + try: + if isinstance(value, list): + if sorted(value) != sorted(item[key]): + if (entry, item) not in updates: + updates.append((entry, item)) + elif value != item[key]: + if (entry, item) not in updates: + updates.append((entry, item)) + except TypeError: + pass + return updates + + +def check_declarative_intent_params(want, module, result): + if module.params["associated_interfaces"]: + if result["changed"]: + time.sleep(module.params["delay"]) + name = module.params["name"] + rc, out, err = exec_command( + module, "show vrf | include {0}".format(name) + ) + if rc == 0: + data = out.strip().split() + if not data: + return + vrf = data[0] + interface = data[-1] + for w in want: + if w["name"] == vrf: + if w.get("associated_interfaces") is None: + continue + for i in w["associated_interfaces"]: + if get_interface_type(i) is not get_interface_type( + interface + ): + module.fail_json( + msg="Interface %s not configured on vrf %s" + % (interface, name) + ) + + +def main(): + """ main entry point for module execution + """ + argument_spec = dict( + vrfs=dict(type="list", elements="raw"), + name=dict(), + description=dict(), + rd=dict(), + route_export=dict(type="list", elements="str"), + route_import=dict(type="list", elements="str"), + route_both=dict(type="list", elements="str"), + route_export_ipv4=dict(type="list", elements="str"), + route_import_ipv4=dict(type="list", elements="str"), + route_both_ipv4=dict(type="list", elements="str"), + route_export_ipv6=dict(type="list", elements="str"), + route_import_ipv6=dict(type="list", elements="str"), + route_both_ipv6=dict(type="list", elements="str"), + interfaces=dict(type="list", elements="str"), + associated_interfaces=dict(type="list", elements="str"), + delay=dict(default=10, type="int"), + purge=dict(type="bool", default=False), + state=dict(default="present", choices=["present", "absent"]), + ) + argument_spec.update(ios_argument_spec) + mutually_exclusive = [("name", "vrfs")] + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = {"changed": False} + warnings = list() + result["warnings"] = warnings + want = map_params_to_obj(module) + have = map_config_to_obj(module) + commands = map_obj_to_commands(update_objects(want, have), module) + if module.params["purge"]: + want_vrfs = [x["name"] for x in want] + have_vrfs = [x["name"] for x in have] + for item in set(have_vrfs).difference(want_vrfs): + cmd = "no vrf definition %s" % item + if cmd not in commands: + commands.append(cmd) + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + check_declarative_intent_params(want, module, result) + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/terminal/__init__.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/terminal/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/terminal/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/ios/plugins/terminal/ios.py b/collections-debian-merged/ansible_collections/cisco/ios/plugins/terminal/ios.py new file mode 100644 index 00000000..f519952c --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/ios/plugins/terminal/ios.py @@ -0,0 +1,116 @@ +# +# (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 <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import json +import re + +from ansible.errors import AnsibleConnectionFailure +from ansible.module_utils._text import to_text, to_bytes +from ansible.plugins.terminal import TerminalBase +from ansible.utils.display import Display + +display = Display() + + +class TerminalModule(TerminalBase): + + terminal_stdout_re = [ + re.compile(br"[\r\n]?[\w\+\-\.:\/\[\]]+(?:\([^\)]+\)){0,3}(?:[>#]) ?$") + ] + + terminal_stderr_re = [ + re.compile(br"% ?Error"), + # re.compile(br"^% \w+", re.M), + re.compile(br"% ?Bad secret"), + re.compile(br"[\r\n%] Bad passwords"), + re.compile(br"invalid input", re.I), + re.compile(br"(?:incomplete|ambiguous) command", re.I), + re.compile(br"connection timed out", re.I), + re.compile(br"[^\r\n]+ not found"), + re.compile(br"'[^']' +returned error code: ?\d+"), + re.compile(br"Bad mask", re.I), + re.compile(br"% ?(\S+) ?overlaps with ?(\S+)", re.I), + re.compile(br"[%\S] ?Error: ?[\s]+", re.I), + re.compile(br"[%\S] ?Informational: ?[\s]+", re.I), + re.compile(br"Command authorization failed"), + re.compile(br"Command Rejected: ?[\s]+", re.I), + ] + + def on_open_shell(self): + try: + self._exec_cli_command(b"terminal length 0") + except AnsibleConnectionFailure: + raise AnsibleConnectionFailure("unable to set terminal parameters") + + try: + self._exec_cli_command(b"terminal width 512") + try: + self._exec_cli_command(b"terminal width 0") + except AnsibleConnectionFailure: + pass + except AnsibleConnectionFailure: + display.display( + "WARNING: Unable to set terminal width, command responses may be truncated" + ) + + def on_become(self, passwd=None): + if self._get_prompt().endswith(b"#"): + return + + cmd = {u"command": u"enable"} + if passwd: + # Note: python-3.5 cannot combine u"" and r"" together. Thus make + # an r string and use to_text to ensure it's text on both py2 and py3. + cmd[u"prompt"] = to_text( + r"[\r\n]?(?:.*)?[Pp]assword: ?$", errors="surrogate_or_strict" + ) + cmd[u"answer"] = passwd + cmd[u"prompt_retry_check"] = True + try: + self._exec_cli_command( + to_bytes(json.dumps(cmd), errors="surrogate_or_strict") + ) + prompt = self._get_prompt() + if prompt is None or not prompt.endswith(b"#"): + raise AnsibleConnectionFailure( + "failed to elevate privilege to enable mode still at prompt [%s]" + % prompt + ) + except AnsibleConnectionFailure as e: + prompt = self._get_prompt() + raise AnsibleConnectionFailure( + "unable to elevate privilege to enable mode, at prompt [%s] with error: %s" + % (prompt, e.message) + ) + + def on_unbecome(self): + prompt = self._get_prompt() + if prompt is None: + # if prompt is None most likely the terminal is hung up at a prompt + return + + if b"(config" in prompt: + self._exec_cli_command(b"end") + self._exec_cli_command(b"disable") + + elif prompt.endswith(b"#"): + self._exec_cli_command(b"disable") |