diff options
Diffstat (limited to 'collections-debian-merged/ansible_collections/cisco/iosxr/plugins')
148 files changed, 41137 insertions, 0 deletions
diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/action/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/action/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/action/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/action/iosxr.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/action/iosxr.py new file mode 100644 index 00000000..2d5819bc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/action/iosxr.py @@ -0,0 +1,162 @@ +# +# (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.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + iosxr_provider_spec, +) +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.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 ["iosxr_config", "config"] else False + ) + force_cli = module_name in ( + "iosxr_netconf", + "iosxr_config", + "iosxr_command", + "iosxr_facts", + ) + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if self._play_context.connection == "local": + provider = load_provider(iosxr_provider_spec, self._task.args) + pc = copy.deepcopy(self._play_context) + pc.network_os = "cisco.iosxr.iosxr" + if force_cli or provider["transport"] == "cli": + pc.connection = "ansible.netcommon.network_cli" + pc.port = int( + provider["port"] or self._play_context.port or 22 + ) + elif provider["transport"] == "netconf": + pc.connection = "ansible.netcommon.netconf" + pc.port = int( + provider["port"] or self._play_context.port or 830 + ) + else: + return { + "failed": True, + "msg": "Transport type %s is not valid for this module" + % provider["transport"], + } + + 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 + + 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.network_os = "iosxr" + if pc.connection.split(".")[-1] == "netconf": + pc.connection = "netconf" + else: + pc.connection = "network_cli" + + 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 + ] + ) + elif persistent_connection in ("netconf", "network_cli"): + if force_cli and persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for module %s" + % (self._play_context.connection, module_name), + } + provider = self._task.args.get("provider", {}) + if any(provider.values()): + display.warning( + "provider is unnecessary when using {0} and will be ignored".format( + self._play_context.connection + ) + ) + del self._task.args["provider"] + 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/iosxr/plugins/cliconf/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/cliconf/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/cliconf/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/cliconf/iosxr.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/cliconf/iosxr.py new file mode 100644 index 00000000..0a2d81c9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/cliconf/iosxr.py @@ -0,0 +1,366 @@ +# +# (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: iosxr +short_description: Use iosxr cliconf to run command on Cisco IOS XR platform +description: +- This iosxr plugin provides low level abstraction apis for sending and receiving + CLI commands from Cisco IOS XR network devices. +version_added: 1.0.0 +""" + +import re +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.connection import ConnectionError +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_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + sanitize_config, + mask_config_blocks_from_diff, +) +from ansible.plugins.cliconf import CliconfBase + + +class Cliconf(CliconfBase): + def get_device_info(self): + device_info = {} + + device_info["network_os"] = "iosxr" + reply = self.get("show version | utility head -n 20") + data = to_text(reply, errors="surrogate_or_strict").strip() + + match = re.search(r"Version (\S+)$", data, re.M) + if match: + device_info["network_os_version"] = match.group(1) + + match = re.search(r'image file is "(.+)"', data) + if match: + device_info["network_os_image"] = match.group(1) + + model_search_strs = [ + r"^[Cc]isco (.+) \(revision", + r"^[Cc]isco (\S+ \S+).+bytes of .*memory", + ] + for item in model_search_strs: + match = re.search(item, data, re.M) + if match: + device_info["network_os_model"] = match.group(1) + break + + match = re.search(r"^(.+) uptime", data, re.M) + if match: + device_info["network_os_hostname"] = match.group(1) + + return device_info + + def configure(self, admin=False, exclusive=False): + prompt = to_text( + self._connection.get_prompt(), errors="surrogate_or_strict" + ).strip() + if not prompt.endswith(")#"): + if admin and "admin-" not in prompt: + self.send_command("admin") + if exclusive: + self.send_command("configure exclusive") + return + self.send_command("configure terminal") + + def abort(self, admin=False): + prompt = to_text( + self._connection.get_prompt(), errors="surrogate_or_strict" + ).strip() + if prompt.endswith(")#"): + self.send_command("abort") + if admin and "admin-" in prompt: + self.send_command("exit") + + def get_config(self, source="running", format="text", flags=None): + if source not in ["running"]: + raise ValueError( + "fetching configuration from %s is not supported" % source + ) + + lookup = {"running": "running-config"} + + cmd = "show {0} ".format(lookup[source]) + cmd += " ".join(to_list(flags)) + cmd = cmd.strip() + + return self.send_command(cmd) + + def edit_config( + self, + candidate=None, + commit=True, + admin=False, + exclusive=False, + replace=None, + comment=None, + label=None, + ): + operations = self.get_device_operations() + self.check_edit_config_capability( + operations, candidate, commit, replace, comment + ) + + resp = {} + results = [] + requests = [] + + self.configure(admin=admin, exclusive=exclusive) + + if replace: + candidate = "load {0}".format(replace) + + for line in to_list(candidate): + if not isinstance(line, Mapping): + line = {"command": line} + cmd = line["command"] + results.append(self.send_command(**line)) + requests.append(cmd) + + # Before any commit happend, we can get a real configuration + # diff from the device and make it available by the iosxr_config module. + # This information can be usefull either in check mode or normal mode. + resp["show_commit_config_diff"] = self.get("show commit changes diff") + + if commit: + self.commit(comment=comment, label=label, replace=replace) + else: + self.discard_changes() + + self.abort(admin=admin) + + resp["request"] = requests + resp["response"] = results + return resp + + def get_diff( + self, + candidate=None, + running=None, + diff_match="line", + diff_ignore_lines=None, + path=None, + diff_replace="line", + ): + 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 + sanitized_candidate = sanitize_config(candidate) + candidate_obj = NetworkConfig(indent=1, comment_tokens=["!"]) + candidate_obj.load(sanitized_candidate) + + if running and diff_match != "none": + # running configuration + running = mask_config_blocks_from_diff( + running, candidate, "ansible" + ) + running = sanitize_config(running) + + running_obj = NetworkConfig( + indent=1, + contents=running, + ignore_lines=diff_ignore_lines, + comment_tokens=["!"], + ) + configdiffobjs = candidate_obj.difference( + running_obj, path=path, match=diff_match, replace=diff_replace + ) + + else: + configdiffobjs = candidate_obj.items + + diff["config_diff"] = ( + dumps(configdiffobjs, "commands") if configdiffobjs else "" + ) + return diff + + def get( + self, + command=None, + prompt=None, + answer=None, + sendonly=False, + newline=True, + output=None, + check_all=False, + ): + if output: + raise ValueError( + "'output' value %s is not supported for get" % output + ) + return self.send_command( + command=command, + prompt=prompt, + answer=answer, + sendonly=sendonly, + newline=newline, + check_all=check_all, + ) + + def commit(self, comment=None, label=None, replace=None): + cmd_obj = {} + if replace: + cmd_obj["command"] = "commit replace" + cmd_obj[ + "prompt" + ] = "This commit will replace or remove the entire running configuration" + cmd_obj["answer"] = "yes" + else: + if comment and label: + cmd_obj["command"] = "commit label {0} comment {1}".format( + label, comment + ) + elif comment: + cmd_obj["command"] = "commit comment {0}".format(comment) + elif label: + cmd_obj["command"] = "commit label {0}".format(label) + else: + cmd_obj["command"] = "commit show-error" + # In some cases even a normal commit, i.e., !replace, + # throws a prompt and we need to handle it before + # proceeding further + cmd_obj["prompt"] = "(C|c)onfirm" + cmd_obj["answer"] = "y" + + self.send_command(**cmd_obj) + + def run_commands(self, commands=None, check_rc=True): + if commands is None: + raise ValueError("'commands' value is required") + responses = list() + for cmd in to_list(commands): + if not isinstance(cmd, Mapping): + cmd = {"command": cmd} + + output = cmd.pop("output", None) + if output: + raise ValueError( + "'output' value %s is not supported for run_commands" + % output + ) + + try: + out = self.send_command(**cmd) + except AnsibleConnectionFailure as e: + if check_rc: + raise + out = getattr(e, "err", e) + + if out is not None: + try: + out = to_text(out, errors="surrogate_or_strict").strip() + except UnicodeError: + raise ConnectionError( + message=u"Failed to decode output from %s: %s" + % (cmd, to_text(out)) + ) + + try: + out = json.loads(out) + except ValueError: + pass + + responses.append(out) + return responses + + def discard_changes(self): + self.send_command("abort") + + def get_device_operations(self): + return { + "supports_diff_replace": True, + "supports_commit": True, + "supports_rollback": False, + "supports_defaults": False, + "supports_onbox_diff": False, + "supports_commit_comment": True, + "supports_multiline_delimiter": False, + "supports_diff_match": True, + "supports_diff_ignore_lines": True, + "supports_generate_diff": True, + "supports_replace": True, + "supports_admin": True, + "supports_commit_label": True, + } + + def get_option_values(self): + return { + "format": ["text"], + "diff_match": ["line", "strict", "exact", "none"], + "diff_replace": ["line", "block", "config"], + "output": [], + } + + def get_capabilities(self): + result = super(Cliconf, self).get_capabilities() + result["rpc"] += [ + "commit", + "discard_changes", + "get_diff", + "configure", + "exit", + ] + result["device_operations"] = self.get_device_operations() + result.update(self.get_option_values()) + return json.dumps(result) + + def set_cli_prompt_context(self): + """ + Make sure we are in the operational cli mode + :return: None + """ + if self._connection.connected: + self._update_cli_prompt_context( + config_context=")#", exit_command="abort" + ) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/doc_fragments/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/doc_fragments/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/doc_fragments/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/doc_fragments/iosxr.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/doc_fragments/iosxr.py new file mode 100644 index 00000000..9e552ded --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/doc_fragments/iosxr.py @@ -0,0 +1,73 @@ +# -*- 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(Network Guide, ../network/getting_started/network_differences.html#multiple-communication-protocols). + - 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 + transport: + description: + - Specifies the type of connection based transport. + type: str + default: cli + choices: + - cli + - netconf +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/iosxr/plugins/filter/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/filter/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/filter/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/acl_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/acl_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/acl_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/acl_interfaces/acl_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/acl_interfaces/acl_interfaces.py new file mode 100644 index 00000000..9e847a16 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/acl_interfaces/acl_interfaces.py @@ -0,0 +1,85 @@ +# +# -*- 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 iosxr_acl_interfaces module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Acl_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the iosxr_acl_interfaces module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "running_config": {"type": "str"}, + "config": { + "elements": "dict", + "options": { + "access_groups": { + "elements": "dict", + "options": { + "acls": { + "elements": "dict", + "options": { + "direction": { + "choices": ["in", "out"], + "type": "str", + "required": True, + }, + "name": {"type": "str", "required": True}, + }, + "type": "list", + }, + "afi": { + "choices": ["ipv4", "ipv6"], + "type": "str", + "required": True, + }, + }, + "type": "list", + }, + "name": {"type": "str", "required": True}, + }, + "type": "list", + }, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/acls/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/acls/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/acls/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/acls/acls.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/acls/acls.py new file mode 100644 index 00000000..0b3a7afc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/acls/acls.py @@ -0,0 +1,538 @@ +# +# -*- 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 iosxr_acls module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class AclsArgs(object): # pylint: disable=R0903 + """The arg spec for the iosxr_acls module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "running_config": {"type": "str"}, + "config": { + "elements": "dict", + "options": { + "acls": { + "elements": "dict", + "options": { + "name": {"type": "str"}, + "aces": { + "elements": "dict", + "mutually_exclusive": [ + ["grant", "remark", "line"] + ], + "options": { + "destination": { + "mutually_exclusive": [ + ["address", "any", "host", "prefix"], + [ + "wildcard_bits", + "any", + "host", + "prefix", + ], + ], + "options": { + "host": {"type": "str"}, + "address": {"type": "str"}, + "any": {"type": "bool"}, + "prefix": {"type": "str"}, + "port_protocol": { + "mutually_exclusive": [ + [ + "eq", + "gt", + "lt", + "neq", + "range", + ] + ], + "options": { + "eq": {"type": "str"}, + "gt": {"type": "str"}, + "lt": {"type": "str"}, + "neq": {"type": "str"}, + "range": { + "options": { + "end": {"type": "str"}, + "start": { + "type": "str" + }, + }, + "required_together": [ + ["start", "end"] + ], + "type": "dict", + }, + }, + "type": "dict", + }, + "wildcard_bits": {"type": "str"}, + }, + "required_together": [ + ["address", "wildcard_bits"] + ], + "type": "dict", + }, + "dscp": { + "mutually_exclusive": [ + ["eq", "gt", "lt", "neq", "range"] + ], + "type": "dict", + "options": { + "eq": {"type": "str"}, + "gt": {"type": "str"}, + "lt": {"type": "str"}, + "neq": {"type": "str"}, + "range": { + "options": { + "end": {"type": "str"}, + "start": {"type": "str"}, + }, + "required_together": [ + ["start", "end"] + ], + "type": "dict", + }, + }, + }, + "fragments": {"type": "bool"}, + "capture": {"type": "bool"}, + "destopts": {"type": "bool"}, + "authen": {"type": "bool"}, + "routing": {"type": "bool"}, + "hop_by_hop": {"type": "bool"}, + "grant": { + "type": "str", + "choices": ["permit", "deny"], + }, + "icmp_off": {"type": "bool"}, + "log": {"type": "bool"}, + "log_input": {"type": "bool"}, + "line": {"type": "str", "aliases": ["ace"]}, + "packet_length": { + "mutually_exclusive": [ + ["eq", "lt", "neq", "range"], + ["eq", "gt", "neq", "range"], + ], + "options": { + "eq": {"type": "int"}, + "gt": {"type": "int"}, + "lt": {"type": "int"}, + "neq": {"type": "int"}, + "range": { + "options": { + "end": {"type": "int"}, + "start": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "precedence": {"type": "str"}, + "protocol": {"type": "str"}, + "protocol_options": { + "mutually_exclusive": [ + ["icmp", "tcp", "igmp", "icmpv6"] + ], + "options": { + "icmpv6": { + "type": "dict", + "options": { + "address_unreachable": { + "type": "bool" + }, + "administratively_prohibited": { + "type": "bool" + }, + "beyond_scope_of_source_address": { + "type": "bool" + }, + "destination_unreachable": { + "type": "bool" + }, + "echo": {"type": "bool"}, + "echo_reply": {"type": "bool"}, + "erroneous_header_field": { + "type": "bool" + }, + "group_membership_query": { + "type": "bool" + }, + "group_membership_report": { + "type": "bool" + }, + "group_membership_termination": { + "type": "bool" + }, + "host_unreachable": { + "type": "bool" + }, + "nd_na": {"type": "bool"}, + "nd_ns": {"type": "bool"}, + "neighbor_redirect": { + "type": "bool" + }, + "no_route_to_destination": { + "type": "bool" + }, + "node_information_request_is_refused": { + "type": "bool" + }, + "node_information_successful_reply": { + "type": "bool" + }, + "packet_too_big": { + "type": "bool" + }, + "parameter_problem": { + "type": "bool" + }, + "port_unreachable": { + "type": "bool" + }, + "query_subject_is_IPv4address": { + "type": "bool" + }, + "query_subject_is_IPv6address": { + "type": "bool" + }, + "query_subject_is_domainname": { + "type": "bool" + }, + "reassembly_timeout": { + "type": "bool" + }, + "redirect": {"type": "bool"}, + "router_advertisement": { + "type": "bool" + }, + "router_renumbering": { + "type": "bool" + }, + "router_solicitation": { + "type": "bool" + }, + "rr_command": {"type": "bool"}, + "rr_result": {"type": "bool"}, + "rr_seqnum_reset": { + "type": "bool" + }, + "time_exceeded": { + "type": "bool" + }, + "ttl_exceeded": { + "type": "bool" + }, + "unknown_query_type": { + "type": "bool" + }, + "unreachable": { + "type": "bool" + }, + "unrecognized_next_header": { + "type": "bool" + }, + "unrecognized_option": { + "type": "bool" + }, + "whoareyou_reply": { + "type": "bool" + }, + "whoareyou_request": { + "type": "bool" + }, + }, + }, + "icmp": { + "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" + }, + }, + "type": "dict", + }, + "igmp": { + "options": { + "dvmrp": {"type": "bool"}, + "host_query": {"type": "bool"}, + "host_report": { + "type": "bool" + }, + "mtrace": {"type": "bool"}, + "mtrace_response": { + "type": "bool" + }, + "pim": {"type": "bool"}, + "trace": {"type": "bool"}, + }, + "type": "dict", + }, + "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", + }, + }, + "type": "dict", + }, + "remark": {"type": "str"}, + "sequence": {"type": "int"}, + "source": { + "mutually_exclusive": [ + ["address", "any", "host", "prefix"], + [ + "wildcard_bits", + "any", + "host", + "prefix", + ], + ], + "options": { + "host": {"type": "str"}, + "address": {"type": "str"}, + "any": {"type": "bool"}, + "prefix": {"type": "str"}, + "port_protocol": { + "mutually_exclusive": [ + [ + "eq", + "gt", + "lt", + "neq", + "range", + ] + ], + "options": { + "eq": {"type": "str"}, + "gt": {"type": "str"}, + "lt": {"type": "str"}, + "neq": {"type": "str"}, + "range": { + "options": { + "end": {"type": "str"}, + "start": { + "type": "str" + }, + }, + "required_together": [ + ["start", "end"] + ], + "type": "dict", + }, + }, + "type": "dict", + }, + "wildcard_bits": {"type": "str"}, + }, + "required_together": [ + ["address", "wildcard_bits"] + ], + "type": "dict", + }, + "ttl": { + "mutually_exclusive": [ + ["eq", "gt", "lt", "neq", "range"] + ], + "options": { + "eq": {"type": "int"}, + "gt": {"type": "int"}, + "lt": {"type": "int"}, + "neq": {"type": "int"}, + "range": { + "options": { + "end": {"type": "int"}, + "start": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + }, + "type": "list", + }, + }, + "type": "list", + }, + "afi": { + "choices": ["ipv4", "ipv6"], + "required": True, + "type": "str", + }, + }, + "type": "list", + }, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/facts/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/facts/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/facts/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/facts/facts.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/facts/facts.py new file mode 100644 index 00000000..af84f8be --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/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 iosxr facts module. +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class FactsArgs(object): # pylint: disable=R0903 + """ The arg spec for the iosxr 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/iosxr/plugins/module_utils/network/iosxr/argspec/interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/interfaces/interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/interfaces/interfaces.py new file mode 100644 index 00000000..e119c159 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/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": "int"}, + "mtu": {"type": "int"}, + "duplex": {"type": "str", "choices": ["full", "half"]}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/l2_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/l2_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/l2_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/l2_interfaces/l2_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/l2_interfaces/l2_interfaces.py new file mode 100644 index 00000000..fbe5e870 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/l2_interfaces/l2_interfaces.py @@ -0,0 +1,85 @@ +# +# -*- 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 iosxr_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}, + "native_vlan": {"type": "int"}, + "l2transport": {"type": "bool"}, + "l2protocol": { + "elements": "dict", + "options": { + "cdp": { + "type": "str", + "choices": ["drop", "forward", "tunnel"], + }, + "pvst": { + "type": "str", + "choices": ["drop", "forward", "tunnel"], + }, + "stp": { + "type": "str", + "choices": ["drop", "forward", "tunnel"], + }, + "vtp": { + "type": "str", + "choices": ["drop", "forward", "tunnel"], + }, + }, + "type": "list", + }, + "q_vlan": {"type": "list", "elements": "int"}, + "propagate": {"type": "bool"}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/l3_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/l3_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/l3_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/l3_interfaces/l3_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/l3_interfaces/l3_interfaces.py new file mode 100644 index 00000000..2e63dae9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/l3_interfaces/l3_interfaces.py @@ -0,0 +1,72 @@ +# +# -*- 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"}, + }, + }, + "ipv6": { + "elements": "dict", + "type": "list", + "options": {"address": {"type": "str"}}, + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lacp/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lacp/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lacp/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lacp/lacp.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lacp/lacp.py new file mode 100644 index 00000000..9b09053f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lacp/lacp.py @@ -0,0 +1,69 @@ +# +# -*- 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 iosxr_lacp module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class LacpArgs(object): # pylint: disable=R0903 + """The arg spec for the iosxr_lacp module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "system": { + "options": { + "mac": { + "type": "dict", + "options": {"address": {"type": "str"}}, + }, + "priority": {"type": "int"}, + }, + "type": "dict", + } + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "parsed", + "rendered", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lacp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lacp_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lacp_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lacp_interfaces/lacp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 00000000..8dfab9ff --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,77 @@ +# +# -*- 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 iosxr_lacp_interfaces module +""" + + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Lacp_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the iosxr_lacp_interfaces module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "churn_logging": { + "choices": ["actor", "partner", "both"], + "type": "str", + }, + "collector_max_delay": {"type": "int"}, + "name": {"type": "str"}, + "period": {"type": "int"}, + "switchover_suppress_flaps": {"type": "int"}, + "system": { + "options": { + "mac": {"type": "str"}, + "priority": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "overridden", + "parsed", + "rendered", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lag_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lag_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lag_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lag_interfaces/lag_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lag_interfaces/lag_interfaces.py new file mode 100644 index 00000000..cf076046 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lag_interfaces/lag_interfaces.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) + +############################################# +# 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 iosxr_lag_interfaces module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Lag_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the iosxr_lag_interfaces module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "links": { + "options": { + "max_active": {"type": "int"}, + "min_active": {"type": "int"}, + }, + "type": "dict", + }, + "load_balancing_hash": { + "choices": ["dst-ip", "src-ip"], + "type": "str", + }, + "members": { + "elements": "dict", + "options": { + "member": {"type": "str"}, + "mode": { + "choices": ["on", "active", "passive", "inherit"], + "type": "str", + }, + }, + "type": "list", + }, + "mode": { + "choices": ["on", "active", "passive"], + "type": "str", + }, + "name": {"required": True, "type": "str"}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "parsed", + "rendered", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lldp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lldp_global/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lldp_global/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lldp_global/lldp_global.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lldp_global/lldp_global.py new file mode 100644 index 00000000..97efae08 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lldp_global/lldp_global.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 iosxr_lldp_global module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Lldp_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the iosxr_lldp module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "holdtime": {"type": "int"}, + "reinit": {"type": "int"}, + "subinterfaces": {"type": "bool"}, + "timer": {"type": "int"}, + "tlv_select": { + "options": { + "management_address": {"type": "bool"}, + "port_description": {"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", + "parsed", + "rendered", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lldp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lldp_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lldp_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lldp_interfaces/lldp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lldp_interfaces/lldp_interfaces.py new file mode 100644 index 00000000..0dca5800 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/lldp_interfaces/lldp_interfaces.py @@ -0,0 +1,77 @@ +# +# -*- 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 iosxr_lldp_interfaces module +""" + + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Lldp_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the iosxr_lldp_interfaces module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "destination": { + "type": "dict", + "options": { + "mac_address": { + "type": "str", + "choices": [ + "ieee-nearest-bridge", + "ieee-nearest-non-tmpr-bridge", + ], + } + }, + }, + "name": {"type": "str"}, + "receive": {"type": "bool"}, + "transmit": {"type": "bool"}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "overridden", + "parsed", + "rendered", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospf_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospf_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospf_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospf_interfaces/ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 00000000..8bf33fd0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,379 @@ +# -*- 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 +# 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 iosxr_ospf_interfaces module +""" + + +class Ospf_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the iosxr_ospf_interfaces module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "running_config": {"type": "str"}, + "config": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "type": {"type": "str", "required": True}, + "address_family": { + "type": "list", + "elements": "dict", + "options": { + "afi": { + "type": "str", + "choices": ["ipv4", "ipv6"], + "required": True, + }, + "processes": { + "type": "list", + "elements": "dict", + "options": { + "process_id": { + "type": "str", + "required": True, + }, + "area": { + "type": "dict", + "options": {"area_id": {"type": "str"}}, + }, + }, + }, + "apply_group_option": { + "type": "dict", + "options": { + "group_name": {"type": "str"}, + "operation": { + "type": "str", + "choices": ["add", "remove", "append"], + }, + }, + }, + "authentication": { + "type": "dict", + "options": { + "message_digest": { + "type": "dict", + "options": {"keychain": {"type": "str"}}, + }, + "null_auth": {"type": "bool"}, + }, + }, + "authentication_key": { + "type": "dict", + "options": { + "password": {"type": "str"}, + "clear": {"type": "str"}, + "encrypted": {"type": "str"}, + }, + }, + "bfd": { + "type": "dict", + "options": { + "fast_detect": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "strict_mode": {"type": "bool"}, + }, + }, + "minimum_interval": {"type": "int"}, + "multiplier": {"type": "int"}, + }, + }, + "cost": {"type": "int"}, + "cost_fallback": { + "type": "dict", + "options": { + "cost": {"type": "int"}, + "threshold": {"type": "int"}, + }, + }, + "database_filter": { + "type": "dict", + "options": {"all_outgoing_lsa": {"type": "bool"}}, + }, + "dead_interval": {"type": "int"}, + "demand_circuit": {"type": "bool"}, + "fast_reroute": { + "type": "dict", + "options": { + "disabled": {"type": "bool"}, + "per_link": { + "type": "dict", + "options": { + "information_type": { + "type": "str", + "choices": [ + "exclude", + "lfa_candidate", + ], + }, + "use_candidate_only": {"type": "bool"}, + "interface": { + "type": "dict", + "options": { + "bvi": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "int"} + }, + }, + "bundle_ether": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "int"} + }, + }, + "pos_int": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "int"} + }, + }, + "fast_ethernet": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + "fiftygige": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + "fortygige": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + "fourhundredgige": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + "gigabitethernet": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + "hundredgige": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + "mgmteth": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + "multilink": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + "pw_ether": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "int"} + }, + }, + "pw_iw": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "int"} + }, + }, + "srp": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + "serial": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + "tengige": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + "twentyfivegige": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + "twohundredgige": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + "nve": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "int"} + }, + }, + "tunnel_ip": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "int"} + }, + }, + "tunnel_ipsec": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "int"} + }, + }, + "tunnel_mte": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "int"} + }, + }, + "tunnel_mpls": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"} + }, + }, + }, + }, + }, + }, + }, + }, + "flood_reduction": {"type": "bool"}, + "hello_interval": {"type": "int"}, + "link_down_fast_detect": {"type": "bool"}, + "message_digest_key": { + "type": "dict", + "options": { + "id": {"type": "int", "required": True}, + "md5": { + "type": "dict", + "required": True, + "options": { + "password": {"type": "str"}, + "clear": {"type": "bool"}, + "encrypted": {"type": "bool"}, + }, + }, + }, + }, + "mpls_ldp_sync": {"type": "bool"}, + "mtu_ignore": {"type": "bool"}, + "network": { + "type": "str", + "choices": [ + "broadcast", + "non-broadcast", + "point-to-multipoint", + "point-to-point", + ], + }, + "neighbors": { + "type": "list", + "elements": "dict", + "options": { + "neighbor_id": {"type": "str"}, + "cost": {"type": "int"}, + "db_filter_all_out": {"type": "bool"}, + "poll_interval": {"type": "int"}, + "priority": {"type": "int"}, + }, + }, + "packet_size": {"type": "int"}, + "passive": {"type": "bool"}, + "prefix_suppression": {"type": "bool"}, + "priority": {"type": "int"}, + "retransmit_interval": {"type": "int"}, + "security_ttl": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "hops": {"type": "int"}, + }, + }, + "transmit_delay": {"type": "int"}, + }, + }, + }, + }, + "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/iosxr/plugins/module_utils/network/iosxr/argspec/ospfv2/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospfv2/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospfv2/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospfv2/ospfv2.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospfv2/ospfv2.py new file mode 100644 index 00000000..fb41a1e3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospfv2/ospfv2.py @@ -0,0 +1,795 @@ +# +# -*- 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 iosxr_ospfv2 module +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Ospfv2Args(object): # pylint: disable=R0903 + """The arg spec for the iosxr_ospfv2 module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "processes": { + "elements": "dict", + "options": { + "address_family_unicast": {"type": "bool"}, + "adjacency_stagger": { + "options": { + "disable": {"type": "bool"}, + "max_adjacency": {"type": "int"}, + "min_adjacency": {"type": "int"}, + }, + "required_together": [ + ["min_adjacency", "max_adjacency"] + ], + "type": "dict", + }, + "apply_weight": { + "mutually_exclusive": [ + ["bandwidth", "default_weight"] + ], + "options": { + "bandwidth": {"type": "int"}, + "default_weight": {"type": "int"}, + }, + "type": "dict", + }, + "areas": { + "elements": "dict", + "options": { + "area_id": {"required": True, "type": "str"}, + "authentication": { + "mutually_exclusive": [ + [ + "keychain", + "message_digest", + "no_auth", + ] + ], + "options": { + "keychain": {"type": "str"}, + "message_digest": { + "options": { + "keychain": {"type": "str"} + }, + "type": "dict", + }, + "no_auth": {"type": "bool"}, + }, + "type": "dict", + }, + "authentication_key": { + "mutually_exclusive": [ + ["clear", "encrypted"] + ], + "options": { + "clear": {"type": "str"}, + "encrypted": {"type": "str"}, + "password": {"type": "str"}, + }, + "type": "dict", + }, + "bfd": { + "options": { + "fast_detect": { + "options": { + "set": {"type": "bool"}, + "strict_mode": { + "type": "bool" + }, + }, + "type": "dict", + }, + "minimum_interval": {"type": "int"}, + "multiplier": {"type": "int"}, + }, + "type": "dict", + }, + "cost": {"type": "int"}, + "dead_interval": {"type": "int"}, + "default_cost": {"type": "int"}, + "hello_interval": {"type": "int"}, + "mpls": { + "options": { + "ldp": { + "options": { + "auto_config": { + "type": "bool" + }, + "sync": {"type": "bool"}, + "sync_igp_shortcuts": { + "type": "bool" + }, + }, + "type": "dict", + }, + "traffic_eng": {"type": "bool"}, + }, + "type": "dict", + }, + "mtu_ignore": { + "choices": ["enable", "disable"], + "type": "str", + }, + "nssa": { + "options": { + "default_information_originate": { + "options": { + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + }, + "type": "dict", + }, + "no_redistribution": {"type": "bool"}, + "no_summary": {"type": "bool"}, + "set": {"type": "bool"}, + "translate": { + "options": { + "type7": { + "options": { + "always": { + "type": "bool" + } + }, + "type": "dict", + } + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "ranges": { + "elements": "dict", + "mutually_exclusive": [ + ["advertise", "not_advertise"] + ], + "options": { + "address": { + "required": True, + "type": "str", + }, + "advertise": {"type": "bool"}, + "not_advertise": {"type": "bool"}, + }, + "type": "list", + }, + "route_policy": { + "elements": "dict", + "options": { + "direction": { + "choices": ["in", "out"], + "type": "str", + }, + "parameters": { + "elements": "str", + "type": "list", + }, + }, + "type": "list", + }, + "stub": { + "options": { + "no_summary": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "transmit_delay": {"type": "int"}, + "virtual_link": { + "elements": "dict", + "options": { + "authentication": { + "mutually_exclusive": [ + [ + "keychain", + "message_digest", + "no_auth", + ] + ], + "options": { + "keychain": {"type": "str"}, + "message_digest": { + "options": { + "keychain": { + "type": "str" + } + }, + "type": "dict", + }, + "no_auth": {"type": "bool"}, + }, + "type": "dict", + }, + "authentication_key": { + "mutually_exclusive": [ + ["clear", "encrypted"] + ], + "options": { + "clear": {"type": "str"}, + "encrypted": {"type": "str"}, + "password": {"type": "str"}, + }, + "type": "dict", + }, + "dead_interval": {"type": "int"}, + "hello_interval": {"type": "int"}, + "id": { + "required": True, + "type": "str", + }, + "message_digest_key": { + "options": { + "id": { + "required": True, + "type": "int", + }, + "md5": { + "mutually_exclusive": [ + ["clear", "encrypted"] + ], + "options": { + "clear": { + "type": "bool" + }, + "encrypted": { + "type": "bool" + }, + "password": { + "type": "str" + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "retransmit_interval": {"type": "int"}, + "transmit_delay": {"type": "int"}, + }, + "type": "list", + }, + }, + "type": "list", + }, + "authentication": { + "mutually_exclusive": [ + ["keychain", "message_digest", "no_auth"] + ], + "options": { + "keychain": {"type": "str"}, + "message_digest": { + "options": { + "keychain": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "no_auth": {"type": "bool"}, + }, + "type": "dict", + }, + "authentication_key": { + "mutually_exclusive": [["clear", "encrypted"]], + "options": { + "clear": {"type": "bool"}, + "encrypted": {"type": "bool"}, + "password": {"type": "str"}, + }, + "type": "dict", + }, + "auto_cost": { + "mutually_exclusive": [ + ["reference_bandwidth", "disable"] + ], + "options": { + "disable": {"type": "bool"}, + "reference_bandwidth": {"type": "int"}, + }, + "type": "dict", + }, + "bfd": { + "options": { + "fast_detect": { + "options": { + "set": {"type": "bool"}, + "strict_mode": {"type": "bool"}, + }, + "type": "dict", + }, + "minimum_interval": {"type": "int"}, + "multiplier": {"type": "int"}, + }, + "type": "dict", + }, + "capability": { + "options": { + "opaque": { + "mutually_exclusive": [["set", "disable"]], + "options": { + "disable": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "type7": {"type": "str"}, + }, + "type": "dict", + }, + "cost": {"type": "int"}, + "database_filter": { + "choices": ["enable", "disable"], + "type": "str", + }, + "dead_interval": {"type": "int"}, + "default_information_originate": { + "options": { + "always": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_policy": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "default_metric": {"type": "int"}, + "demand_circuit": { + "choices": ["enable", "disable"], + "type": "str", + }, + "distance": { + "options": { + "admin_distance": { + "elements": "dict", + "options": { + "access_list": {"type": "str"}, + "source": {"type": "str"}, + "value": {"type": "int"}, + "wildcard": {"type": "str"}, + }, + "required_together": [ + ["value", "source", "wildcard"] + ], + "type": "list", + }, + "ospf_distance": { + "options": { + "external": {"type": "int"}, + "inter_area": {"type": "int"}, + "intra_area": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "distribute_bgp_ls": { + "options": { + "instance_id": {"type": "int"}, + "throttle": {"type": "int"}, + }, + "type": "dict", + }, + "distribute_link_state": { + "options": { + "instance_id": {"type": "int"}, + "throttle": {"type": "int"}, + }, + "type": "dict", + }, + "distribute_list": { + "elements": "dict", + "mutually_exclusive": [ + ["access_list", "route_policy"] + ], + "options": { + "access_list": {"type": "str"}, + "direction": { + "choices": ["in", "out"], + "type": "str", + }, + "outgoing_params": { + "options": { + "id": {"type": "str"}, + "route_type": { + "choices": [ + "bgp", + "connected", + "dagr", + "ospf", + "static", + ], + "type": "str", + }, + }, + "type": "dict", + }, + "route_policy": {"type": "str"}, + }, + "type": "list", + }, + "external_out": { + "choices": ["enable", "disable"], + "type": "str", + }, + "flood_reduction": { + "choices": ["enable", "disable"], + "type": "str", + }, + "hello_interval": {"type": "int"}, + "ignore_lsa_mospf": {"type": "bool"}, + "link_down_fast_detect": {"type": "bool"}, + "log_adjacency_changes": { + "options": { + "detail": {"type": "bool"}, + "disable": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "loopback_stub_network": { + "choices": ["enable", "disable"], + "type": "str", + }, + "max_lsa": { + "options": { + "ignore_count": {"type": "int"}, + "ignore_time": {"type": "int"}, + "reset_time": {"type": "int"}, + "threshold": {"type": "int"}, + "warning_only": {"type": "bool"}, + }, + "type": "dict", + }, + "max_metric": { + "options": { + "router_lsa": { + "options": { + "external_lsa": { + "options": { + "max_metric_value": { + "type": "int" + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "include_stub": {"type": "bool"}, + "on_startup": { + "options": { + "set": {"type": "bool"}, + "wait_for_bgp_asn": { + "type": "int" + }, + "wait_period": {"type": "int"}, + }, + "type": "dict", + }, + "set": {"type": "bool"}, + "summary_lsa": { + "options": { + "max_metric_value": { + "type": "int" + }, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + } + }, + "type": "dict", + }, + "message_digest_key": { + "options": { + "id": {"required": True, "type": "int"}, + "md5": { + "mutually_exclusive": [ + ["clear", "encrypted"] + ], + "options": { + "clear": {"type": "bool"}, + "encrypted": {"type": "bool"}, + "password": {"type": "str"}, + }, + "required": True, + "type": "dict", + }, + }, + "type": "dict", + }, + "microloop_avoidance": { + "mutually_exclusive": [ + [ + "protected", + "rib_update_delay", + "segment_routing", + ] + ], + "options": { + "protected": {"type": "bool"}, + "rib_update_delay": {"type": "int"}, + "segment_routing": {"type": "bool"}, + }, + "type": "dict", + }, + "monitor_convergence": { + "options": { + "prefix_list": {"type": "str"}, + "track_external_routes": {"type": "bool"}, + "track_ip_frr": {"type": "bool"}, + "track_summary_routes": {"type": "bool"}, + }, + "type": "dict", + }, + "mpls": { + "options": { + "ldp": { + "options": { + "auto_config": {"type": "bool"}, + "sync": {"type": "bool"}, + "sync_igp_shortcuts": {"type": "bool"}, + }, + "type": "dict", + }, + "traffic_eng": { + "options": { + "autoroute_exclude": { + "options": { + "parameters": { + "elements": "str", + "type": "list", + }, + "route_policy": { + "type": "str" + }, + }, + "type": "dict", + }, + "igp_intact": {"type": "bool"}, + "ldp_sync_update": {"type": "bool"}, + "multicast_intact": {"type": "bool"}, + "router_id": {"type": "str"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "mtu_ignore": { + "choices": ["enable", "disable"], + "type": "str", + }, + "network": { + "options": { + "broadcast": {"type": "bool"}, + "non_broadcast": {"type": "bool"}, + "point_to_multipoint": {"type": "bool"}, + "point_to_point": {"type": "bool"}, + }, + "type": "dict", + }, + "nsf": { + "options": { + "cisco": { + "options": { + "enforce_global": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "flush_delay_time": {"type": "int"}, + "ietf": { + "options": { + "helper_disable": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "interval": {"type": "int"}, + "lifetime": {"type": "int"}, + }, + "type": "dict", + }, + "nsr": {"type": "bool"}, + "packet_size": {"type": "int"}, + "passive": { + "choices": ["enable", "disable"], + "type": "str", + }, + "prefix_suppression": { + "options": { + "secondary_address": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "priority": {"type": "int"}, + "process_id": {"required": True, "type": "str"}, + "protocol_shutdown": { + "options": { + "host_mode": {"type": "bool"}, + "limit": { + "options": { + "high": {"type": "int"}, + "low": {"type": "int"}, + "medium": {"type": "int"}, + }, + "type": "dict", + }, + "on_reload": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "redistribute": { + "options": { + "id": {"type": "str"}, + "level": { + "choices": [1, 2, 12], + "type": "int", + }, + "lsa_type_summary": {"type": "bool"}, + "match": {"type": "str"}, + "metric": {"type": "int"}, + "metric_type": { + "choices": [1, 2], + "type": "int", + }, + "nssa_only": {"type": "bool"}, + "preserve_med": {"type": "bool"}, + "route_policy": { + "options": { + "name": {"type": "str"}, + "parameters": { + "elements": "str", + "type": "list", + }, + }, + "type": "dict", + }, + "route_type": { + "choices": [ + "application", + "bgp", + "connected", + "dagr", + "eigrp", + "isis", + "mobile", + "ospf", + "rip", + "static", + "subscriber", + ], + "type": "str", + }, + "tag": {"type": "int"}, + }, + "type": "dict", + }, + "retransmit_interval": {"type": "int"}, + "router_id": {"type": "str"}, + "security_ttl": { + "options": { + "hops": {"type": "int"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "summary_in": { + "choices": ["enable", "disable"], + "type": "str", + }, + "summary_prefix": { + "elements": "dict", + "options": { + "not_advertise": {"type": "bool"}, + "prefix": {"required": True, "type": "str"}, + "tag": {"type": "int"}, + }, + "type": "list", + }, + "timers": { + "options": { + "graceful_shutdown": { + "options": { + "initial_delay": {"type": "int"}, + "retain_routes": {"type": "int"}, + }, + "type": "dict", + }, + "lsa": { + "options": { + "group_pacing": {"type": "int"}, + "min_arrival": {"type": "int"}, + "refresh": {"type": "int"}, + }, + "type": "dict", + }, + "pacing_flood": {"type": "int"}, + "throttle": { + "options": { + "fast_reroute": {"type": "int"}, + "lsa_all": { + "options": { + "initial_delay": { + "type": "int" + }, + "max_delay": {"type": "int"}, + "min_delay": {"type": "int"}, + }, + "type": "dict", + }, + "spf": { + "options": { + "change_delay": { + "type": "int" + }, + "max_wait": {"type": "int"}, + "second_delay": { + "type": "int" + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "transmit_delay": {"type": "int"}, + "weight": {"type": "int"}, + }, + "type": "list", + } + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospfv3/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospfv3/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospfv3/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospfv3/ospfv3.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospfv3/ospfv3.py new file mode 100644 index 00000000..e9d82720 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/ospfv3/ospfv3.py @@ -0,0 +1,1376 @@ +# -*- 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 iosxr_ospfv3 module +""" + + +class Ospfv3Args(object): # pylint: disable=R0903 + """The arg spec for the iosxr_ospfv3 module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "running_config": {"type": "str"}, + "config": { + "type": "dict", + "options": { + "processes": { + "type": "list", + "elements": "dict", + "options": { + "process_id": {"type": "str", "required": True}, + "address_family_unicast": {"type": "bool"}, + "authentication": { + "type": "dict", + "options": { + "disable": {"type": "bool", "default": False}, + "ipsec": { + "type": "dict", + "options": { + "spi": {"type": "int"}, + "algorithim_type": { + "type": "str", + "choices": ["md5", "sha1"], + }, + "key": {"type": "str"}, + "clear_key": {"type": "str"}, + "password_key": {"type": "str"}, + }, + }, + }, + }, + "auto_cost": { + "type": "dict", + "options": { + "reference_bandwidth": {"type": "int"}, + "disable": {"type": "bool"}, + }, + }, + "bfd": { + "type": "dict", + "options": { + "fast_detect": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "strict_mode": {"type": "bool"}, + }, + }, + "minimum_interval": {"type": "int"}, + "multiplier": {"type": "int"}, + }, + }, + "areas": { + "type": "list", + "elements": "dict", + "options": { + "area_id": {"type": "str", "required": True}, + "authentication": { + "type": "dict", + "options": { + "disable": { + "type": "bool", + "default": False, + }, + "ipsec": { + "type": "dict", + "options": { + "spi": {"type": "int"}, + "algorithim_type": { + "type": "str", + "choices": ["md5", "sha1"], + }, + "key": {"type": "str"}, + "clear_key": {"type": "str"}, + "password_key": { + "type": "str" + }, + }, + }, + }, + }, + "bfd": { + "type": "dict", + "options": { + "fast_detect": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "strict_mode": { + "type": "bool" + }, + }, + }, + "minimum_interval": {"type": "int"}, + "multiplier": {"type": "int"}, + }, + }, + "cost": {"type": "int"}, + "database_filter": { + "type": "dict", + "options": { + "all_outgoing_lsa": {"type": "bool"} + }, + }, + "dead_interval": {"type": "int"}, + "default_cost": {"type": "int"}, + "demand_circuit": {"type": "bool"}, + "distrinbute_rib_prefix_list_name": { + "type": "str" + }, + "fast_reroute": { + "type": "dict", + "options": { + "disabled": {"type": "bool"}, + "per_link": { + "type": "dict", + "options": { + "information_type": { + "type": "str", + "choices": [ + "exclude", + "lfa_candidate", + ], + }, + "use_candidate_only": { + "type": "bool" + }, + "interface": { + "type": "dict", + "options": { + "bvi": { + "type": "list", + "elements": "int", + }, + "bundle_ether": { + "type": "list", + "elements": "int", + }, + "pos_int": { + "type": "list", + "elements": "int", + }, + "fast_ethernet": { + "type": "list", + "elements": "str", + }, + "fiftygige": { + "type": "list", + "elements": "str", + }, + "fortygige": { + "type": "list", + "elements": "str", + }, + "fourhundredgige": { + "type": "list", + "elements": "str", + }, + "gigabitethernet": { + "type": "list", + "elements": "str", + }, + "hundredgige": { + "type": "list", + "elements": "str", + }, + "mgmteth": { + "type": "list", + "elements": "str", + }, + "multilink": { + "type": "list", + "elements": "str", + }, + "pw_ether": { + "type": "list", + "elements": "int", + }, + "pw_iw": { + "type": "list", + "elements": "int", + }, + "srp": { + "type": "list", + "elements": "str", + }, + "serial": { + "type": "list", + "elements": "str", + }, + "tengige": { + "type": "list", + "elements": "str", + }, + "twentyfivegige": { + "type": "list", + "elements": "str", + }, + "twohundredgige": { + "type": "list", + "elements": "str", + }, + "nve": { + "type": "list", + "elements": "int", + }, + "tunnel_ip": { + "type": "list", + "elements": "int", + }, + "tunnel_ipsec": { + "type": "list", + "elements": "int", + }, + "tunnel_mte": { + "type": "list", + "elements": "int", + }, + "tunnel_mpls": { + "type": "int" + }, + }, + }, + }, + }, + "per_prefix": { + "type": "dict", + "options": { + "information_type": { + "type": "str", + "choices": [ + "exclude", + "lfa_candidate", + ], + }, + "use_candidate_only": { + "type": "bool" + }, + "interface": { + "type": "dict", + "options": { + "bvi": { + "type": "list", + "elements": "int", + }, + "bundle_ether": { + "type": "list", + "elements": "int", + }, + "pos_int": { + "type": "list", + "elements": "int", + }, + "fast_ethernet": { + "type": "list", + "elements": "str", + }, + "fiftygige": { + "type": "list", + "elements": "str", + }, + "fortygige": { + "type": "list", + "elements": "str", + }, + "fourhundredgige": { + "type": "list", + "elements": "str", + }, + "gigabitethernet": { + "type": "list", + "elements": "str", + }, + "hundredgige": { + "type": "list", + "elements": "str", + }, + "mgmteth": { + "type": "list", + "elements": "str", + }, + "multilink": { + "type": "list", + "elements": "str", + }, + "pw_ether": { + "type": "list", + "elements": "int", + }, + "pw_iw": { + "type": "list", + "elements": "int", + }, + "srp": { + "type": "list", + "elements": "str", + }, + "serial": { + "type": "list", + "elements": "str", + }, + "tengige": { + "type": "list", + "elements": "str", + }, + "twentyfivegige": { + "type": "list", + "elements": "str", + }, + "twohundredgige": { + "type": "list", + "elements": "str", + }, + "nve": { + "type": "list", + "elements": "int", + }, + "tunnel_ip": { + "type": "list", + "elements": "int", + }, + "tunnel_ipsec": { + "type": "list", + "elements": "int", + }, + "tunnel_mte": { + "type": "list", + "elements": "int", + }, + "tunnel_mpls": { + "type": "int" + }, + }, + }, + }, + }, + }, + }, + "flood_reduction": {"type": "bool"}, + "hello_interval": {"type": "int"}, + "instance_id": {"type": "int"}, + "mtu_ignore": {"type": "bool"}, + "mpls_ldp_sync": {"type": "bool"}, + "network": { + "type": "str", + "choices": [ + "broadcast", + "non-broadcast", + "point-to-multipoint", + "point-to-point", + ], + }, + "nssa": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "default_information_originate": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + }, + }, + "no_redistribution": {"type": "bool"}, + "no_summary": {"type": "bool"}, + "translate": { + "type": "dict", + "options": { + "type7": { + "type": "dict", + "options": { + "always": { + "type": "bool", + "required": True, + } + }, + } + }, + }, + }, + }, + "packet_size": {"type": "int"}, + "passive": {"type": "bool"}, + "prefix_suppression": {"type": "bool"}, + "priority": {"type": "int"}, + "ranges": { + "type": "list", + "elements": "dict", + "options": { + "address": { + "type": "str", + "required": True, + }, + "cost": {"type": "int"}, + "advertise": {"type": "bool"}, + "not_advertise": {"type": "bool"}, + }, + }, + "retransmit_interval": {"type": "int"}, + "stub": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "no_summary": {"type": "bool"}, + }, + }, + "transmit_delay": {"type": "int"}, + "virtual_link": { + "type": "list", + "elements": "dict", + "options": { + "id": { + "type": "str", + "required": True, + }, + "authentication": { + "type": "dict", + "options": { + "disable": { + "type": "bool", + "default": False, + }, + "ipsec": { + "type": "dict", + "options": { + "spi": {"type": "int"}, + "algorithim_type": { + "type": "str", + "choices": [ + "md5", + "sha1", + ], + }, + "key": {"type": "str"}, + "clear_key": { + "type": "str" + }, + "password_key": { + "type": "str" + }, + }, + }, + }, + }, + "dead_interval": {"type": "int"}, + "hello_interval": {"type": "int"}, + "retransmit_interval": {"type": "int"}, + "transmit_delay": {"type": "int"}, + "encryption": { + "type": "dict", + "options": { + "disable": { + "type": "bool", + "default": False, + }, + "ipsec": { + "type": "dict", + "options": { + "spi": {"type": "int"}, + "esp": { + "type": "dict", + "options": { + "triple_des": { + "type": "dict", + "options": { + "key": { + "type": "str" + }, + "clear_key": { + "type": "str" + }, + "password_key": { + "type": "str" + }, + }, + }, + "aes": { + "type": "dict", + "options": { + "algorithim_type": { + "type": "str", + "choices": [ + "192", + "256", + ], + }, + "key": { + "type": "str" + }, + "clear_key": { + "type": "str" + }, + "password_key": { + "type": "str" + }, + }, + }, + "des": { + "type": "dict", + "options": { + "key": { + "type": "str" + }, + "clear_key": { + "type": "str" + }, + "password_key": { + "type": "str" + }, + }, + }, + "null_encryption": { + "type": "dict", + "options": { + "authentication": { + "type": "dict", + "options": { + "algorithim_type": { + "type": "str", + "choices": [ + "md5", + "sha1", + ], + }, + "key": { + "type": "str" + }, + "clear_key": { + "type": "str" + }, + "password_key": { + "type": "str" + }, + }, + } + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "encryption": { + "type": "dict", + "options": { + "disable": { + "type": "bool", + "default": False, + }, + "ipsec": { + "type": "dict", + "options": { + "spi": {"type": "int"}, + "esp": { + "type": "dict", + "options": { + "triple_des": { + "type": "dict", + "options": { + "key": { + "type": "str" + }, + "clear_key": { + "type": "str" + }, + "password_key": { + "type": "str" + }, + }, + }, + "aes": { + "type": "dict", + "options": { + "algorithim_type": { + "type": "str", + "choices": [ + "192", + "256", + ], + }, + "key": { + "type": "str" + }, + "clear_key": { + "type": "str" + }, + "password_key": { + "type": "str" + }, + }, + }, + "des": { + "type": "dict", + "options": { + "key": { + "type": "str" + }, + "clear_key": { + "type": "str" + }, + "password_key": { + "type": "str" + }, + }, + }, + "null_encryption": { + "type": "dict", + "options": { + "authentication": { + "type": "dict", + "options": { + "algorithim_type": { + "type": "str", + "choices": [ + "md5", + "sha1", + ], + }, + "key": { + "type": "str" + }, + "clear_key": { + "type": "str" + }, + "password_key": { + "type": "str" + }, + }, + } + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "capability": { + "type": "dict", + "options": { + "type7": { + "type": "dict", + "options": { + "prefer": {"type": "bool"}, + "translate": {"type": "bool"}, + }, + } + }, + }, + "cost": {"type": "int"}, + "database_filter": { + "type": "dict", + "options": {"all_outgoing_lsa": {"type": "bool"}}, + }, + "dead_interval": {"type": "int"}, + "default_information_originate": { + "type": "dict", + "options": { + "always": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_policy": {"type": "str"}, + "tag": {"type": "int"}, + "set": {"type": "bool"}, + }, + }, + "default_metric": {"type": "int"}, + "demand_circuit": {"type": "bool"}, + "distance": { + "type": "dict", + "options": { + "admin_distance": {"type": "int"}, + "ospfv3_distance": { + "type": "dict", + "options": { + "external": {"type": "int"}, + "inter_area": {"type": "int"}, + "intra_area": {"type": "int"}, + }, + }, + }, + }, + "distribute_list": { + "type": "dict", + "options": { + "prefix_list": { + "type": "list", + "elements": "str", + "options": { + "name": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + } + }, + }, + "encryption": { + "type": "dict", + "options": { + "disable": {"type": "bool", "default": False}, + "ipsec": { + "type": "dict", + "options": { + "spi": {"type": "int"}, + "esp": { + "type": "dict", + "options": { + "triple_des": { + "type": "dict", + "options": { + "key": {"type": "str"}, + "clear_key": { + "type": "str" + }, + "password_key": { + "type": "str" + }, + }, + }, + "aes": { + "type": "dict", + "options": { + "algorithim_type": { + "type": "str", + "choices": [ + "192", + "256", + ], + }, + "key": {"type": "str"}, + "clear_key": { + "type": "str" + }, + "password_key": { + "type": "str" + }, + }, + }, + "des": { + "type": "dict", + "options": { + "key": {"type": "str"}, + "clear_key": { + "type": "str" + }, + "password_key": { + "type": "str" + }, + }, + }, + "null_encryption": { + "type": "dict", + "options": { + "authentication": { + "type": "dict", + "options": { + "algorithim_type": { + "type": "str", + "choices": [ + "md5", + "sha1", + ], + }, + "key": { + "type": "str" + }, + "clear_key": { + "type": "str" + }, + "password_key": { + "type": "str" + }, + }, + } + }, + }, + }, + }, + }, + }, + }, + }, + "fast_reroute": { + "type": "dict", + "options": { + "disabled": {"type": "bool"}, + "per_link": { + "type": "dict", + "options": { + "information_type": { + "type": "str", + "choices": [ + "exclude", + "lfa_candidate", + ], + }, + "use_candidate_only": {"type": "bool"}, + "interface": { + "type": "dict", + "options": { + "bvi": { + "type": "list", + "elements": "int", + }, + "bundle_ether": { + "type": "list", + "elements": "int", + }, + "pos_int": { + "type": "list", + "elements": "int", + }, + "fast_ethernet": { + "type": "list", + "elements": "str", + }, + "fiftygige": { + "type": "list", + "elements": "str", + }, + "fortygige": { + "type": "list", + "elements": "str", + }, + "fourhundredgige": { + "type": "list", + "elements": "str", + }, + "gigabitethernet": { + "type": "list", + "elements": "str", + }, + "hundredgige": { + "type": "list", + "elements": "str", + }, + "mgmteth": { + "type": "list", + "elements": "str", + }, + "multilink": { + "type": "list", + "elements": "str", + }, + "pw_ether": { + "type": "list", + "elements": "int", + }, + "pw_iw": { + "type": "list", + "elements": "int", + }, + "srp": { + "type": "list", + "elements": "str", + }, + "serial": { + "type": "list", + "elements": "str", + }, + "tengige": { + "type": "list", + "elements": "str", + }, + "twentyfivegige": { + "type": "list", + "elements": "str", + }, + "twohundredgige": { + "type": "list", + "elements": "str", + }, + "nve": { + "type": "list", + "elements": "int", + }, + "tunnel_ip": { + "type": "list", + "elements": "int", + }, + "tunnel_ipsec": { + "type": "list", + "elements": "int", + }, + "tunnel_mte": { + "type": "list", + "elements": "int", + }, + "tunnel_mpls": {"type": "int"}, + }, + }, + }, + }, + "per_prefix": { + "type": "dict", + "options": { + "information_type": { + "type": "str", + "choices": [ + "exclude", + "lfa_candidate", + ], + }, + "use_candidate_only": {"type": "bool"}, + "interface": { + "type": "dict", + "options": { + "bvi": { + "type": "list", + "elements": "int", + }, + "bundle_ether": { + "type": "list", + "elements": "int", + }, + "post_int": { + "type": "list", + "elements": "int", + }, + "fast_ethernet": { + "type": "list", + "elements": "str", + }, + "fiftygige": { + "type": "list", + "elements": "str", + }, + "fortygige": { + "type": "list", + "elements": "str", + }, + "fourhundredgige": { + "type": "list", + "elements": "str", + }, + "gigabitethernet": { + "type": "list", + "elements": "str", + }, + "hundredgige": { + "type": "list", + "elements": "str", + }, + "mgmteth": { + "type": "list", + "elements": "str", + }, + "multilink": { + "type": "list", + "elements": "str", + }, + "pw_ether": { + "type": "list", + "elements": "int", + }, + "pw_iw": { + "type": "list", + "elements": "int", + }, + "srp": { + "type": "list", + "elements": "str", + }, + "serial": { + "type": "list", + "elements": "str", + }, + "tengige": { + "type": "list", + "elements": "str", + }, + "twentyfivegige": { + "type": "list", + "elements": "str", + }, + "twohundredgige": { + "type": "list", + "elements": "str", + }, + "nve": { + "type": "list", + "elements": "int", + }, + "tunnel_ip": { + "type": "list", + "elements": "int", + }, + "tunnel_ipsec": { + "type": "list", + "elements": "int", + }, + "tunnel_mte": { + "type": "list", + "elements": "int", + }, + "tunnel_mpls": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "flood_reduction": {"type": "bool"}, + "graceful_restart": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "helper_disable": {"type": "bool"}, + "min_interval": {"type": "int"}, + "max_interval": {"type": "int"}, + }, + }, + "hello_interval": {"type": "int"}, + "ignore_mospf_type6_lsa": {"type": "bool"}, + "instance_id": {"type": "int"}, + "log_adjacency_changes": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "disable": {"type": "bool"}, + "detail": {"type": "bool"}, + }, + }, + "maximum": { + "type": "dict", + "options": { + "interfaces": {"type": "int"}, + "paths": {"type": "int"}, + "redistributed_prefixes": {"type": "int"}, + }, + }, + "mpls_ldp_sync": {"type": "bool"}, + "mtu_ignore": {"type": "bool"}, + "network": { + "type": "str", + "choices": [ + "broadcast", + "non-broadcast", + "point-to-multipoint", + "point-to-point", + ], + }, + "nsr": {"type": "bool"}, + "packet_size": {"type": "int"}, + "passive": {"type": "bool"}, + "prefix_suppression": {"type": "bool"}, + "priority": {"type": "int"}, + "protocol_shutdown": {"type": "bool"}, + "redistribute": { + "type": "dict", + "options": { + "application": { + "type": "list", + "elements": "dict", + "options": { + "id": { + "type": "str", + "required": True, + }, + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_policy": {"type": "str"}, + "tag": {"type": "int"}, + }, + }, + "bgp": { + "type": "list", + "elements": "dict", + "options": { + "id": { + "type": "int", + "required": True, + }, + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "preserved_med": {"type": "str"}, + "route_policy": {"type": "str"}, + "tag": {"type": "int"}, + }, + }, + "connected": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_policy": {"type": "str"}, + "tag": {"type": "int"}, + }, + }, + "eigrp": { + "type": "list", + "elements": "dict", + "options": { + "id": { + "type": "int", + "required": True, + }, + "set": {"type": "bool"}, + "match": { + "type": "str", + "choices": [ + "external", + "internal", + ], + }, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_policy": {"type": "str"}, + "tag": {"type": "int"}, + }, + }, + "isis": { + "type": "list", + "elements": "dict", + "options": { + "id": { + "type": "str", + "required": True, + }, + "set": {"type": "bool"}, + "level": { + "type": "str", + "choices": [ + "level-1", + "level-1-2", + "level-2", + ], + }, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_policy": {"type": "str"}, + "tag": {"type": "int"}, + }, + }, + "mobile": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_policy": {"type": "str"}, + "tag": {"type": "int"}, + }, + }, + "ospfv3": { + "type": "list", + "elements": "dict", + "options": { + "id": { + "type": "str", + "required": True, + }, + "set": {"type": "bool"}, + "match": { + "type": "dict", + "options": { + "external": { + "type": "int", + "choices": ["1", "2"], + }, + "nssa_external": { + "type": "int", + "choices": ["1", "2"], + }, + "internal": {"type": "bool"}, + }, + }, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_policy": {"type": "str"}, + "tag": {"type": "int"}, + }, + }, + "static": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_policy": {"type": "str"}, + "tag": {"type": "int"}, + }, + }, + "subscriber": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_policy": {"type": "str"}, + "tag": {"type": "int"}, + }, + }, + }, + }, + "retransmit_interval": {"type": "int"}, + "router_id": {"type": "str"}, + "spf_prefix_priority": { + "type": "dict", + "options": { + "disable": {"type": "bool"}, + "route_policy": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"}, + "value": {"type": "str"}, + }, + }, + }, + }, + "stub_router": { + "type": "dict", + "options": { + "router_lsa": { + "type": "dict", + "options": { + "advertise_with": { + "type": "str", + "choices": [ + "max-metric", + "r-bit", + "v6-bit", + ], + }, + "always": {"type": "bool"}, + "external_lsa": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + }, + }, + "include_stub": {"type": "bool"}, + "on_proc_migration": {"type": "int"}, + "on_proc_restart": {"type": "int"}, + "on_startup": { + "type": "dict", + "options": { + "time": {"type": "int"}, + "wait_for_bgp": { + "type": "bool" + }, + }, + }, + "on_switchover": {"type": "int"}, + "summary_lsa": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + }, + }, + }, + } + }, + }, + "summary_prefix": { + "type": "list", + "elements": "dict", + "options": { + "prefix": {"type": "str", "required": True}, + "not_advertise": {"type": "bool"}, + "tag": {"type": "int"}, + }, + }, + "timers": { + "type": "dict", + "options": { + "lsa_arrival": {"type": "int"}, + "pacing": { + "type": "dict", + "options": { + "flood": {"type": "int"}, + "lsa_group": {"type": "int"}, + "retransmission": {"type": "int"}, + }, + }, + "throttle": { + "type": "dict", + "options": { + "lsa": { + "type": "dict", + "options": { + "all_lsa_initial": { + "type": "int" + }, + "all_lsa_minimum": { + "type": "int" + }, + }, + }, + "spf": { + "type": "dict", + "options": { + "spf_initial": {"type": "int"}, + "spf_minimum": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "trace": { + "type": "dict", + "options": { + "size": {"type": "str"}, + "value": {"type": "int"}, + }, + }, + "transmit_delay": {"type": "int"}, + }, + } + }, + }, + "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/iosxr/plugins/module_utils/network/iosxr/argspec/static_routes/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/static_routes/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/static_routes/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/static_routes/static_routes.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/static_routes/static_routes.py new file mode 100644 index 00000000..362477b5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/argspec/static_routes/static_routes.py @@ -0,0 +1,103 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# +""" +The arg spec for the iosxr_static_routes module +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Static_routesArgs(object): # pylint: disable=R0903 + """The arg spec for the iosxr_static_routes module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "vrf": {"type": "str"}, + "address_families": { + "elements": "dict", + "options": { + "afi": { + "choices": ["ipv4", "ipv6"], + "required": True, + "type": "str", + }, + "safi": { + "choices": ["unicast", "multicast"], + "required": True, + "type": "str", + }, + "routes": { + "elements": "dict", + "options": { + "dest": {"required": True, "type": "str"}, + "next_hops": { + "elements": "dict", + "options": { + "admin_distance": {"type": "int"}, + "description": {"type": "str"}, + "dest_vrf": {"type": "str"}, + "forward_router_address": { + "type": "str" + }, + "interface": {"type": "str"}, + "metric": {"type": "int"}, + "tag": {"type": "int"}, + "track": {"type": "str"}, + "tunnel_id": {"type": "int"}, + "vrflabel": {"type": "int"}, + }, + "type": "list", + }, + }, + "type": "list", + }, + }, + "type": "list", + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/acl_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/acl_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/acl_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/acl_interfaces/acl_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/acl_interfaces/acl_interfaces.py new file mode 100644 index 00000000..924abba2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/acl_interfaces/acl_interfaces.py @@ -0,0 +1,126 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The iosxr_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.cisco.iosxr.plugins.module_utils.network.iosxr.rm_templates.acl_interfaces import ( + Acl_interfacesTemplate, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import ( + ResourceModule, +) + + +class Acl_interfaces(ResourceModule): + """ + The iosxr_acl_interfaces class + """ + + def __init__(self, module): + super(Acl_interfaces, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="acl_interfaces", + tmplt=Acl_interfacesTemplate(), + ) + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + 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 + """ + # convert list of dicts to dicts of dicts + wantd = {entry["name"]: entry for entry in self.want} + haved = {entry["name"]: entry for entry in self.have} + + # turn all lists of dicts into dicts prior to merge + for entry in wantd, haved: + self._list_to_dict(entry) + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to wantd + if self.state == "deleted": + haved = {k: v for k, v in haved.items() if k in wantd or not wantd} + wantd = {} + + # remove superfluous config + if self.state in ["overridden", "deleted"]: + for k, have in haved.items(): + if k not in wantd: + self._compare(want={}, have=have) + + for k, want in wantd.items(): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + begin = len(self.commands) + self._compare_lists(want=want, have=have) + if len(self.commands) != begin: + self.commands.insert( + begin, self._tmplt.render(want or have, "interface", False) + ) + + def _compare_lists(self, want, have): + wdict = want.get("access_groups", {}) + hdict = have.get("access_groups", {}) + + for afi in ("ipv4", "ipv6"): + wacls = wdict.pop(afi, {}).pop("acls", {}) + hacls = hdict.pop(afi, {}).pop("acls", {}) + + for key, entry in wacls.items(): + if entry != hacls.pop(key, {}): + entry["afi"] = afi + self.addcmd(entry, "access_groups", False) + # remove remaining items in have for replaced + for entry in hacls.values(): + entry["afi"] = afi + self.addcmd(entry, "access_groups", True) + + def _list_to_dict(self, entry): + for item in entry.values(): + for ag in item.get("access_groups", []): + ag["acls"] = { + subentry["direction"]: subentry + for subentry in ag.get("acls", []) + } + item["access_groups"] = { + subentry["afi"]: subentry + for subentry in item.get("access_groups", []) + } diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/acls/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/acls/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/acls/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/acls/acls.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/acls/acls.py new file mode 100644 index 00000000..c07b3b3e --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/acls/acls.py @@ -0,0 +1,518 @@ +# +# -*- 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 iosxr_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 + + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + flatten_dict, + prefix_to_address_wildcard, + is_ipv4_address, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + search_obj_in_list, + dict_diff, + remove_empties, +) +from ansible.module_utils.six import iteritems +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) + + +class Acls(ConfigBase): + """ + The iosxr_acls class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["acls"] + + def __init__(self, module): + super(Acls, self).__init__(module) + + def get_acls_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 + ) + acls_facts = facts["ansible_network_resources"].get("acls") + if not acls_facts: + return [] + return acls_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + warnings = list() + commands = list() + + if self.state in self.ACTION_STATES: + existing_acls_facts = self.get_acls_facts() + else: + existing_acls_facts = [] + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_acls_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_acls_facts = self.get_acls_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_acls_facts(data=running_config) + + if self.state in self.ACTION_STATES: + result["before"] = existing_acls_facts + if result["changed"]: + result["after"] = changed_acls_facts + + elif self.state == "gathered": + result["gathered"] = changed_acls_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_acls_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_acls_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"] + commands = [] + + 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.extend(self._state_overridden(want, have)) + + elif state == "deleted": + commands.extend(self._state_deleted(want, have)) + + else: + # Instead of passing entire want and have + # list of dictionaries to the respective + # _state_* methods we are passing the want + # and have dictionaries per AFI + for item in want: + afi = item["afi"] + obj_in_have = search_obj_in_list(afi, have, key="afi") or {} + + if state == "merged" or self.state == "rendered": + commands.extend( + self._state_merged(remove_empties(item), obj_in_have) + ) + + elif state == "replaced": + commands.extend( + self._state_replaced(remove_empties(item), obj_in_have) + ) + + return commands + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + for want_acl in want["acls"]: + have_acl = ( + search_obj_in_list(want_acl["name"], have.get("acls", [])) + or {} + ) + acl_updates = [] + + for have_ace in have_acl.get("aces", []): + want_ace = ( + search_obj_in_list( + have_ace["sequence"], want_acl["aces"], key="sequence" + ) + or {} + ) + if not want_ace: + acl_updates.append("no {0}".format(have_ace["sequence"])) + + for want_ace in want_acl.get("aces", []): + have_ace = ( + search_obj_in_list( + want_ace.get("sequence"), + have_acl.get("aces", []), + key="sequence", + ) + or {} + ) + set_cmd = self._set_commands(want_ace, have_ace) + if set_cmd: + acl_updates.append(set_cmd) + + if acl_updates: + acl_updates.insert( + 0, + "{0} access-list {1}".format( + want["afi"], want_acl["name"] + ), + ) + commands.extend(acl_updates) + + 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 = [] + + # Remove extraneous AFI that are present in config but not + # specified in `want` + for have_afi in have: + want_afi = ( + search_obj_in_list(have_afi["afi"], want, key="afi") or {} + ) + if not want_afi: + for acl in have_afi.get("acls", []): + commands.append( + "no {0} access-list {1}".format( + have_afi["afi"], acl["name"] + ) + ) + + # First we remove the extraneous ACLs from the AFIs that + # are present both in `want` and in `have` and then + # we call `_state_replaced` to update the ACEs within those ACLs + for want_afi in want: + want_afi = remove_empties(want_afi) + have_afi = ( + search_obj_in_list(want_afi["afi"], have, key="afi") or {} + ) + if have_afi: + for have_acl in have_afi.get("acls", []): + want_acl = ( + search_obj_in_list( + have_acl["name"], want_afi.get("acls", []) + ) + or {} + ) + if not want_acl: + commands.append( + "no {0} access-list {1}".format( + have_afi["afi"], have_acl["name"] + ) + ) + + commands.extend(self._state_replaced(want_afi, have_afi)) + + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + if not have: + have = {} + + for want_acl in want["acls"]: + have_acl = ( + search_obj_in_list(want_acl["name"], have.get("acls", {})) + or {} + ) + + acl_updates = [] + for want_ace in want_acl["aces"]: + have_ace = ( + search_obj_in_list( + want_ace.get("sequence"), + have_acl.get("aces", []), + key="sequence", + ) + or {} + ) + set_cmd = self._set_commands(want_ace, have_ace) + if set_cmd: + acl_updates.append(set_cmd) + + if acl_updates: + acl_updates.insert( + 0, + "{0} access-list {1}".format( + want["afi"], want_acl["name"] + ), + ) + commands.extend(acl_updates) + + 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 not want: + want = [{"afi": "ipv4"}, {"afi": "ipv6"}] + + for item in want: + item = remove_empties(item) + have_item = search_obj_in_list(item["afi"], have, key="afi") or {} + if "acls" not in item: + if have_item: + for acl in have_item["acls"]: + commands.append( + "no {0} access-list {1}".format( + have_item["afi"], acl["name"] + ) + ) + else: + for want_acl in item["acls"]: + have_acl = ( + search_obj_in_list( + want_acl["name"], have_item.get("acls", []) + ) + or {} + ) + if have_acl: + commands.append( + "no {0} access-list {1}".format( + have_item["afi"], have_acl["name"] + ) + ) + + return commands + + def _compute_commands(self, want_ace): + """This command creates an ACE line from an ACE dictionary + + :rtype: A string + :returns: An ACE generated from a structured ACE dictionary + """ + + def __compute_src_dest(dir_dict): + cmd = "" + if "any" in dir_dict: + cmd += "any " + elif "host" in dir_dict: + cmd += "host {0} ".format(dir_dict["host"]) + elif "prefix" in dir_dict: + cmd += "{0} ".format(dir_dict["prefix"]) + else: + cmd += "{0} {1} ".format( + dir_dict["address"], dir_dict["wildcard_bits"] + ) + + if "port_protocol" in dir_dict: + protocol_range = dir_dict["port_protocol"].get("range") + if protocol_range: + cmd += "range {0} {1} ".format( + protocol_range["start"], protocol_range["end"] + ) + else: + for key, value in iteritems(dir_dict["port_protocol"]): + cmd += "{0} {1} ".format(key, value) + + return cmd + + def __compute_protocol_options(protocol_dict): + cmd = "" + for value in protocol_options.values(): + for subkey, subvalue in iteritems(value): + if subvalue: + cmd += "{0} ".format(subkey.replace("_", "-")) + return cmd + + def __compute_match_options(want_ace): + cmd = "" + + if "precedence" in want_ace: + cmd += "precedence {0} ".format(want_ace["precedence"]) + + for x in ["dscp", "packet_length", "ttl"]: + if x in want_ace: + opt_range = want_ace[x].get("range") + if opt_range: + cmd += "{0} range {1} {2} ".format( + x.replace("_", "-"), + opt_range["start"], + opt_range["end"], + ) + else: + for key, value in iteritems(want_ace[x]): + cmd += "{0} {1} {2} ".format( + x.replace("_", "-"), key, value + ) + + for x in ( + "authen", + "capture", + "fragments", + "routing", + "log", + "log_input", + "icmp_off", + "destopts", + "hop_by_hop", + ): + if x in want_ace: + cmd += "{0} ".format(x.replace("_", "-")) + + return cmd + + cmd = "" + if "sequence" in want_ace: + cmd += "{0} ".format(want_ace["sequence"]) + + if "remark" in want_ace: + cmd += "remark {0}".format(want_ace["remark"]) + + elif "line" in want_ace: + cmd += want_ace["line"] + + else: + cmd += "{0} ".format(want_ace["grant"]) + if "protocol" in want_ace: + cmd += "{0} ".format(want_ace["protocol"]) + + cmd += __compute_src_dest(want_ace["source"]) + cmd += __compute_src_dest(want_ace["destination"]) + + protocol_options = want_ace.get("protocol_options", {}) + if protocol_options: + cmd += __compute_protocol_options(protocol_options) + + cmd += __compute_match_options(want_ace) + + return cmd.strip() + + def _set_commands(self, want_ace, have_ace): + """A helped method that checks if there is + a delta between the `have_ace` and `want_ace`. + If there is a delta then it calls `_compute_commands` + to create the ACE line. + + :rtype: A string + :returns: An ACE generated from a structured ACE dictionary + via a call to `_compute_commands` + """ + + if "line" in want_ace: + if want_ace["line"] != have_ace.get("line"): + return self._compute_commands(want_ace) + + else: + if ("prefix" in want_ace.get("source", {})) or ( + "prefix" in want_ace.get("destination", {}) + ): + self._prepare_for_diff(want_ace) + + protocol_opt_delta = {} + delta = dict_diff(have_ace, want_ace) + + # `dict_diff` doesn't work properly for `protocol_options` diff, + # so we need to handle it separately + if want_ace.get("protocol_options", {}): + protocol_opt_delta = set( + flatten_dict(have_ace.get("protocol_options", {})) + ) ^ set(flatten_dict(want_ace.get("protocol_options", {}))) + + if delta or protocol_opt_delta: + want_ace = self._dict_merge(have_ace, want_ace) + return self._compute_commands(want_ace) + + def _prepare_for_diff(self, ace): + """This method prepares the want ace dict + for diff calculation against the have ace dict. + + :param ace: The want ace to prepare for diff calculation + """ + # Convert prefixes to "address wildcard bits" format for IPv4 addresses + # Not valid for IPv6 addresses because those can only be specified as prefixes + # and are always rendered in running-config as prefixes too + for x in ["source", "destination"]: + prefix = ace.get(x, {}).get("prefix") + if prefix and is_ipv4_address(prefix): + del ace[x]["prefix"] + ace[x]["address"], ace[x][ + "wildcard_bits" + ] = prefix_to_address_wildcard(prefix) + + def _dict_merge(self, have_ace, want_ace): + for x in want_ace: + have_ace[x] = want_ace[x] + return have_ace diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/interfaces/interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/interfaces/interfaces.py new file mode 100644 index 00000000..ce3534b5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/interfaces/interfaces.py @@ -0,0 +1,327 @@ +# -*- 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 iosxr_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.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + get_interface_type, + dict_to_set, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + remove_command_from_config_list, + add_command_to_config_list, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + filter_dict_having_none_value, + remove_duplicate_interface, +) + + +class Interfaces(ConfigBase): + """ + The iosxr_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 module execution + """ + result = {"changed": False} + warnings = list() + commands = 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) + + if self.state in self.ACTION_STATES: + result["before"] = existing_interfaces_facts + if result["changed"]: + result["after"] = changed_interfaces_facts + + elif self.state == "gathered": + result["gathered"] = changed_interfaces_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_interfaces_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_interfaces_facts + resp = self.set_state(want, have) + + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + if ( + self.state in ("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"] + or interface["name"] in each["name"] + ): + break + else: + continue + have_dict = filter_dict_having_none_value(interface, each) + want = dict() + commands.extend(self._clear_config(want, 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"] + or interface["name"] in each["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) + want = dict() + commands.extend(self._clear_config(want, 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: + if self.state == "rendered": + commands.extend(self._set_config(interface, dict())) + else: + for each in have: + if ( + each["name"] == interface["name"] + or interface["name"] in each["name"] + ): + break + else: + 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"] + or 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 _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/iosxr/plugins/module_utils/network/iosxr/config/l2_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/l2_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/l2_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/l2_interfaces/l2_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/l2_interfaces/l2_interfaces.py new file mode 100644 index 00000000..a5596d18 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/l2_interfaces/l2_interfaces.py @@ -0,0 +1,373 @@ +# -*- 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 iosxr_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, + remove_empties, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + normalize_interface, + dict_to_set, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + remove_command_from_config_list, + add_command_to_config_list, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + filter_dict_having_none_value, + remove_duplicate_interface, +) + + +class L2_Interfaces(ConfigBase): + """ + The iosxr_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["l2_interfaces"] + + 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 module execution + """ + result = {"changed": False} + warnings = list() + commands = list() + + 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 + ) + + 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_l2_interfaces_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_l2_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: + interface["name"] = normalize_interface(interface["name"]) + for each in have: + if ( + each["name"] == interface["name"] + or interface["name"] in each["name"] + ): + break + else: + commands.extend(self._set_config(interface, {}, module)) + continue + interface = remove_empties(interface) + 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 = [] + not_in_have = set() + in_have = set() + for each in have: + for interface in want: + interface["name"] = normalize_interface(interface["name"]) + if each["name"] == interface["name"]: + in_have.add(interface["name"]) + break + if interface["name"] != each["name"]: + not_in_have.add(interface["name"]) + 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 + interface = remove_empties(interface) + 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)) + # Add the want interface that's not already configured in have interface + for each in not_in_have - in_have: + for every in want: + interface = "interface {0}".format(every["name"]) + if each and interface not in commands: + commands.extend(self._set_config(every, {}, 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: + interface["name"] = normalize_interface(interface["name"]) + interface = remove_empties(interface) + for each in have: + if ( + each["name"] == interface["name"] + or interface["name"] in each["name"] + ): + break + else: + commands.extend(self._set_config(interface, {}, 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: + interface["name"] = normalize_interface(interface["name"]) + for each in have: + if ( + each["name"] == interface["name"] + or 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 _set_config(self, want, have, module): + # Set the interface config based on the want and have config + commands = [] + interface = "interface " + want["name"] + l2_protocol_bool = False + # 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: + # For merging with already configured l2protocol + if have.get("l2protocol") and len(have.get("l2protocol")) > 1: + l2_protocol_diff = [] + for each in want.get("l2protocol"): + for every in have.get("l2protocol"): + if every == each: + break + if each not in have.get("l2protocol"): + l2_protocol_diff.append(each) + l2_protocol_bool = True + l2protocol = tuple(l2_protocol_diff) + else: + l2protocol = {} + + diff = dict(diff) + wants_native = diff.get("native_vlan") + l2transport = diff.get("l2transport") + q_vlan = diff.get("q_vlan") + propagate = diff.get("propagate") + if l2_protocol_bool is False: + l2protocol = diff.get("l2protocol") + + if wants_native: + cmd = "dot1q native vlan {0}".format(wants_native) + add_command_to_config_list(interface, cmd, commands) + + if l2transport or l2protocol: + for each in l2protocol: + each = dict(each) + if isinstance(each, dict): + cmd = "l2transport l2protocol {0} {1}".format( + list(each.keys())[0], list(each.values())[0] + ) + add_command_to_config_list(interface, cmd, commands) + if propagate and not have.get("propagate"): + cmd = "l2transport propagate remote-status" + add_command_to_config_list(interface, cmd, commands) + elif want.get("l2transport") is False and ( + want.get("l2protocol") or want.get("propagate") + ): + module.fail_json( + msg="L2transport L2protocol or Propagate can only be configured when " + "L2transport set to True!" + ) + + if q_vlan and "." in interface: + q_vlans = " ".join(map(str, want.get("q_vlan"))) + if q_vlans != have.get("q_vlan"): + cmd = "dot1q vlan {0}".format(q_vlans) + 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("native_vlan"): + remove_command_from_config_list( + interface, "dot1q native vlan", commands + ) + + if have.get("q_vlan"): + remove_command_from_config_list( + interface, "encapsulation dot1q", commands + ) + + if have.get("l2protocol") and ( + want.get("l2protocol") is None or want.get("propagate") is None + ): + if "no l2transport" not in commands: + remove_command_from_config_list( + interface, "l2transport", commands + ) + elif have.get("l2transport") and have.get("l2transport") != want.get( + "l2transport" + ): + if "no l2transport" not in commands: + remove_command_from_config_list( + interface, "l2transport", commands + ) + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/l3_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/l3_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/l3_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/l3_interfaces/l3_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/l3_interfaces/l3_interfaces.py new file mode 100644 index 00000000..47949a77 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/l3_interfaces/l3_interfaces.py @@ -0,0 +1,397 @@ +# -*- 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 iosxr_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, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + normalize_interface, + dict_to_set, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + remove_command_from_config_list, + add_command_to_config_list, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + filter_dict_having_none_value, + remove_duplicate_interface, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + validate_n_expand_ipv4, + validate_ipv6, +) + + +class L3_Interfaces(ConfigBase): + """ + The iosxr_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} + warnings = list() + commands = 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 + ) + + if self.state in self.ACTION_STATES: + result["before"] = existing_l3_interfaces_facts + if result["changed"]: + result["after"] = changed_l3_interfaces_facts + + elif self.state == "gathered": + result["gathered"] = changed_l3_interfaces_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_l3_interfaces_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_l3_interfaces_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + 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: + interface["name"] = normalize_interface(interface["name"]) + for each in have: + if each["name"] == interface["name"]: + break + else: + 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 = [] + not_in_have = set() + in_have = set() + + for each in have: + for interface in want: + interface["name"] = normalize_interface(interface["name"]) + if each["name"] == interface["name"]: + in_have.add(interface["name"]) + break + if interface["name"] != each["name"]: + not_in_have.add(interface["name"]) + 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)) + # Add the want interface that's not already configured in have interface + for each in not_in_have - in_have: + for every in want: + interface = "interface {0}".format(every["name"]) + if each and interface not in commands: + commands.extend(self._set_config(every, {}, 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: + interface["name"] = normalize_interface(interface["name"]) + if self.state == "rendered": + commands.extend(self._set_config(interface, dict(), module)) + else: + for each in have: + if each["name"] == interface["name"]: + break + else: + 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: + interface["name"] = normalize_interface(interface["name"]) + for each in have: + if ( + each["name"] == interface["name"] + or 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 = dict(each) + for every in have: + every_have = dict(every) + + if each_want.get("address") == every_have.get("address"): + if len(each_want.keys()) == len(every_have.keys()) and ( + each_want.get("secondary") + == every_have.get("secondary") + ): + diff = False + break + if not each_want.get("secondary") and not every_have.get( + "secondary" + ): + diff = False + break + + diff = True + else: + diff = True + 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 + + # Get the diff b/w want and have + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + + # To handle L3 IPV4 configuration + want_ipv4 = dict(want_dict).get("ipv4") + have_ipv4 = dict(have_dict).get("ipv4") + if want_ipv4: + if have_ipv4: + diff_ipv4 = set(want_ipv4) - set(dict(have_dict).get("ipv4")) + if diff_ipv4: + diff_ipv4 = ( + diff_ipv4 + if self.verify_diff_again(want_ipv4, have_ipv4) + else () + ) + else: + diff_ipv4 = set(want_ipv4) + for each in diff_ipv4: + ipv4_dict = dict(each) + if ipv4_dict.get("address") != "dhcp": + cmd = "ipv4 address {0}".format(ipv4_dict["address"]) + if ipv4_dict.get("secondary"): + cmd += " secondary" + add_command_to_config_list(interface, cmd, commands) + + # To handle L3 IPV6 configuration + want_ipv6 = dict(want_dict).get("ipv6") + have_ipv6 = dict(have_dict).get("ipv6") + if want_ipv6: + if have_ipv6: + diff_ipv6 = set(want_ipv6) - set(have_ipv6) + else: + diff_ipv6 = set(want_ipv6) + for each in diff_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, "ipv4 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/iosxr/plugins/module_utils/network/iosxr/config/lacp/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lacp/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lacp/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lacp/lacp.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lacp/lacp.py new file mode 100644 index 00000000..1c5d53f2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lacp/lacp.py @@ -0,0 +1,213 @@ +# +# -*- 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 iosxr_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.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_diff, +) +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_empties, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + flatten_dict, +) + + +class Lacp(ConfigBase): + """ + The iosxr_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 {} + return lacp_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + warnings = list() + commands = list() + + if self.state in self.ACTION_STATES: + existing_lacp_facts = self.get_lacp_facts() + else: + existing_lacp_facts = {} + + 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) + + 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.get("config") + if not want: + want = {} + 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._state_deleted(want, have)) + + commands.extend(self._state_merged(want, have)) + + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + updates = dict_diff(have, want) + if self.state == "rendered": + updates = want + if updates: + for key, value in iteritems( + flatten_dict(remove_empties(updates["system"])) + ): + commands.append( + "lacp system {0} {1}".format( + key.replace("address", "mac"), value + ) + ) + + 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 = [] + + for x in [ + k + for k in have.get("system", {}) + if k not in remove_empties(want.get("system", {})) + ]: + commands.append("no lacp system {0}".format(x)) + + return commands diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lacp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lacp_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lacp_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lacp_interfaces/lacp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 00000000..83c1ee9d --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,312 @@ +# +# -*- 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 iosxr_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.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_diff, + remove_empties, +) +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + search_obj_in_list, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + dict_delete, + pad_commands, + flatten_dict, +) + + +class Lacp_interfaces(ConfigBase): + """ + The iosxr_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 + ) + + 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 + """ + want = self._module.params["config"] + 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 = [] + 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.extend(Lacp_interfaces._state_overridden(want, have)) + + elif state == "deleted": + if not want: + for intf in have: + commands.extend( + Lacp_interfaces._state_deleted( + {"name": intf["name"]}, intf + ) + ) + else: + for item in want: + obj_in_have = search_obj_in_list(item["name"], have) + commands.extend( + Lacp_interfaces._state_deleted(item, obj_in_have) + ) + + else: + for item in want: + name = item["name"] + obj_in_have = search_obj_in_list(name, have) + + if state in ("merged", "rendered"): + commands.extend( + Lacp_interfaces._state_merged(item, obj_in_have) + ) + + elif state == "replaced": + commands.extend( + Lacp_interfaces._state_replaced(item, obj_in_have) + ) + + return commands + + @staticmethod + def _state_replaced(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 = [] + replaced_commands = [] + merged_commands = [] + + if have: + replaced_commands = Lacp_interfaces._state_deleted(want, have) + + merged_commands = Lacp_interfaces._state_merged(want, have) + + if merged_commands and replaced_commands: + del merged_commands[0] + + commands.extend(replaced_commands) + commands.extend(merged_commands) + + return commands + + @staticmethod + def _state_overridden(want, have): + """ The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + for intf in have: + intf_in_want = search_obj_in_list(intf["name"], want) + if not intf_in_want: + commands.extend( + Lacp_interfaces._state_deleted( + {"name": intf["name"]}, intf + ) + ) + + for intf in want: + intf_in_have = search_obj_in_list(intf["name"], have) + commands.extend( + Lacp_interfaces._state_replaced(intf, intf_in_have) + ) + + return commands + + @staticmethod + def _state_merged(want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + if not have: + have = {"name": want["name"]} + + for key, value in iteritems( + flatten_dict(remove_empties(dict_diff(have, want))) + ): + commands.append(Lacp_interfaces._compute_commands(key, value)) + + if commands: + pad_commands(commands, want["name"]) + + return commands + + @staticmethod + def _state_deleted(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 = [] + + for key, value in iteritems( + flatten_dict(dict_delete(have, remove_empties(want))) + ): + commands.append( + Lacp_interfaces._compute_commands(key, value, remove=True) + ) + + if commands: + pad_commands(commands, have["name"]) + + return commands + + @staticmethod + def _compute_commands(key, value, remove=False): + if key == "churn_logging": + cmd = "lacp churn logging {0}".format(value) + + elif key == "collector_max_delay": + cmd = "lacp collector-max-delay {0}".format(value) + + elif key == "period": + cmd = "lacp period {0}".format(value) + + elif key == "switchover_suppress_flaps": + cmd = "lacp switchover suppress-flaps {0}".format(value) + + elif key == "mac": + cmd = "lacp system mac {0}".format(value) + + elif key == "priority": + cmd = "lacp system priority {0}".format(value) + + if remove: + cmd = "no " + cmd + + return cmd diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lag_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lag_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lag_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lag_interfaces/lag_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lag_interfaces/lag_interfaces.py new file mode 100644 index 00000000..a877341b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lag_interfaces/lag_interfaces.py @@ -0,0 +1,439 @@ +# +# -*- 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 iosxr_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 + +from copy import deepcopy +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + dict_diff, + remove_empties, + search_obj_in_list, + param_list_to_dict, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + diff_list_of_dicts, + pad_commands, + flatten_dict, + dict_delete, + normalize_interface, +) + + +class Lag_interfaces(ConfigBase): + """ + The iosxr_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} + warnings = list() + commands = list() + + if self.state in self.ACTION_STATES: + existing_lag_interfaces_facts = self.get_lag_interfaces_facts() + else: + existing_lag_interfaces_facts = {} + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_lag_interfaces_facts)) + + if commands and self.state in self.ACTION_STATES: + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True + + if self.state in self.ACTION_STATES: + result["commands"] = commands + + if self.state in self.ACTION_STATES or self.state == "gathered": + changed_lag_interfaces_facts = self.get_lag_interfaces_facts() + + elif self.state == "rendered": + result["rendered"] = commands + + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed" + ) + result["parsed"] = self.get_lag_interfaces_facts( + data=running_config + ) + + if self.state in self.ACTION_STATES: + result["before"] = existing_lag_interfaces_facts + if result["changed"]: + result["after"] = changed_lag_interfaces_facts + + elif self.state == "gathered": + result["gathered"] = changed_lag_interfaces_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_lag_interfaces_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + if want: + for item in want: + item["name"] = normalize_interface(item["name"]) + if "members" in want and want["members"]: + for item in want["members"]: + item.update( + { + "member": normalize_interface(item["member"]), + "mode": item["mode"], + } + ) + 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 + """ + state = self._module.params["state"] + commands = [] + + if ( + self.state in ("merged", "replaced", "overridden", "rendered") + and not want + ): + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format( + state + ) + ) + + if state == "overridden": + commands.extend(self._state_overridden(want, have)) + + elif state == "deleted": + commands.extend(self._state_deleted(want, have)) + + else: + # Instead of passing entire want and have + # list of dictionaries to the respective + # _state_* methods we are passing the want + # and have dictionaries per interface + for item in want: + name = item["name"] + obj_in_have = search_obj_in_list(name, have) + + if state in ("merged", "rendered"): + commands.extend(self._state_merged(item, obj_in_have)) + + elif state == "replaced": + commands.extend(self._state_replaced(item, obj_in_have)) + + return commands + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + if have: + commands.extend(self._render_bundle_del_commands(want, have)) + commands.extend(self._render_bundle_updates(want, have)) + + if commands or have == {}: + pad_commands(commands, want["name"]) + + if have: + commands.extend(self._render_interface_del_commands(want, have)) + commands.extend(self._render_interface_updates(want, have)) + + return commands + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + for have_intf in have: + intf_in_want = search_obj_in_list(have_intf["name"], want) + if not intf_in_want: + commands.extend(self._purge_attribs(have_intf)) + + for intf in want: + intf_in_have = search_obj_in_list(intf["name"], have) + commands.extend(self._state_replaced(intf, intf_in_have)) + + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + commands.extend(self._render_bundle_updates(want, have)) + + if commands or have == {}: + pad_commands(commands, want["name"]) + + commands.extend(self._render_interface_updates(want, have)) + + return commands + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + + if not want: + for item in have: + commands.extend(self._purge_attribs(intf=item)) + else: + for item in want: + name = item["name"] + obj_in_have = search_obj_in_list(name, have) + if not obj_in_have: + self._module.fail_json( + msg=("interface {0} does not exist".format(name)) + ) + commands.extend(self._purge_attribs(intf=obj_in_have)) + + return commands + + def _render_bundle_updates(self, want, have): + """ The command generator for updates to bundles + :rtype: A list + :returns: the commands necessary to update bundles + """ + commands = [] + if not have: + have = {"name": want["name"]} + + want_copy = deepcopy(want) + have_copy = deepcopy(have) + + want_copy.pop("members", []) + have_copy.pop("members", []) + + bundle_updates = dict_diff(have_copy, want_copy) + + if bundle_updates: + for key, value in iteritems( + flatten_dict(remove_empties(bundle_updates)) + ): + commands.append(self._compute_commands(key=key, value=value)) + + return commands + + def _render_interface_updates(self, want, have): + """ The command generator for updates to member + interfaces + :rtype: A list + :returns: the commands necessary to update member + interfaces + """ + commands = [] + + if not have: + have = {"name": want["name"]} + + member_diff = diff_list_of_dicts( + want["members"], have.get("members", []) + ) + + for diff in member_diff: + diff_cmd = [] + bundle_cmd = "bundle id {0}".format( + want["name"].split("Bundle-Ether")[1] + ) + if diff.get("mode"): + bundle_cmd += " mode {0}".format(diff.get("mode")) + diff_cmd.append(bundle_cmd) + pad_commands(diff_cmd, diff["member"]) + commands.extend(diff_cmd) + + return commands + + def _render_bundle_del_commands(self, want, have): + """ The command generator for delete commands + w.r.t bundles + :rtype: A list + :returns: the commands necessary to update member + interfaces + """ + commands = [] + if not want: + want = {"name": have["name"]} + + want_copy = deepcopy(want) + have_copy = deepcopy(have) + want_copy.pop("members", []) + have_copy.pop("members", []) + + to_delete = dict_delete(have_copy, remove_empties(want_copy)) + if to_delete: + for key, value in iteritems( + flatten_dict(remove_empties(to_delete)) + ): + commands.append( + self._compute_commands(key=key, value=value, remove=True) + ) + + return commands + + def _render_interface_del_commands(self, want, have): + """ The command generator for delete commands + w.r.t member interfaces + :rtype: A list + :returns: the commands necessary to update member + interfaces + """ + commands = [] + if not want: + want = {} + have_members = have.get("members") + + if have_members: + have_members = param_list_to_dict( + deepcopy(have_members), unique_key="member" + ) + want_members = param_list_to_dict( + deepcopy(want).get("members", []), unique_key="member" + ) + + for key in have_members: + if key not in want_members: + member_cmd = ["no bundle id"] + pad_commands(member_cmd, key) + commands.extend(member_cmd) + + return commands + + def _purge_attribs(self, intf): + """ The command generator for purging attributes + :rtype: A list + :returns: the commands necessary to purge attributes + """ + commands = [] + have_copy = deepcopy(intf) + members = have_copy.pop("members", []) + + to_delete = dict_delete( + have_copy, remove_empties({"name": have_copy["name"]}) + ) + if to_delete: + for key, value in iteritems( + flatten_dict(remove_empties(to_delete)) + ): + commands.append( + self._compute_commands(key=key, value=value, remove=True) + ) + + if commands: + pad_commands(commands, intf["name"]) + + if members: + members = param_list_to_dict( + deepcopy(members), unique_key="member" + ) + for key in members: + member_cmd = ["no bundle id"] + pad_commands(member_cmd, key) + commands.extend(member_cmd) + + return commands + + def _compute_commands(self, key, value, remove=False): + """ The method generates LAG commands based on the + key, value passed. When remove is set to True, + the command is negated. + :rtype: str + :returns: a command based on the `key`, `value` pair + passed and the value of `remove` + """ + if key == "mode": + cmd = "lacp mode {0}".format(value) + + elif key == "load_balancing_hash": + cmd = "bundle load-balancing hash {0}".format(value) + + elif key == "max_active": + cmd = "bundle maximum-active links {0}".format(value) + + elif key == "min_active": + cmd = "bundle minimum-active links {0}".format(value) + + if remove: + cmd = "no {0}".format(cmd) + + return cmd diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lldp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lldp_global/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lldp_global/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lldp_global/lldp_global.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lldp_global/lldp_global.py new file mode 100644 index 00000000..af15f791 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lldp_global/lldp_global.py @@ -0,0 +1,221 @@ +# +# -*- 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 iosxr_lldp_global class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + +__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, + dict_diff, + remove_empties, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) +from ansible.module_utils.six import iteritems +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + flatten_dict, + dict_delete, +) + + +class Lldp_global(ConfigBase): + """ + The iosxr_lldp class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["lldp_global"] + + 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_facts = facts["ansible_network_resources"].get("lldp_global") + if not lldp_facts: + return {} + return lldp_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + warnings = list() + commands = list() + if self.state in self.ACTION_STATES: + existing_lldp_global_facts = self.get_lldp_global_facts() + else: + existing_lldp_global_facts = {} + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_lldp_global_facts)) + + if commands and self.state in self.ACTION_STATES: + + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True + + if self.state in self.ACTION_STATES: + result["commands"] = commands + + if self.state in self.ACTION_STATES or self.state == "gathered": + changed_lldp_global_facts = self.get_lldp_global_facts() + + elif self.state == "rendered": + result["rendered"] = commands + + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed" + ) + result["parsed"] = self.get_lldp_global_facts(data=running_config) + + if self.state in self.ACTION_STATES: + result["before"] = existing_lldp_global_facts + if result["changed"]: + result["after"] = changed_lldp_global_facts + elif self.state == "gathered": + result["gathered"] = changed_lldp_global_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_lldp_global_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + if not want and self._module.params["state"] == "deleted": + want = {} + have = existing_lldp_global_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + state = self._module.params["state"] + if 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( + state + ) + ) + + if state == "deleted": + commands = self._state_deleted(want, have) + elif state in ("merged", "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 = [] + + commands.extend(self._state_deleted(want, have)) + + commands.extend(self._state_merged(want, have)) + + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + updates = dict_diff(have, want) + if updates: + for key, value in iteritems(flatten_dict(remove_empties(updates))): + commands.append(self._compute_commands(key, value)) + + 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 = [] + for key, value in iteritems( + flatten_dict(dict_delete(have, remove_empties(want))) + ): + cmd = self._compute_commands(key, value, remove=True) + if cmd: + commands.append(cmd) + + return commands + + def _compute_commands(self, key, value=None, remove=False): + if key in ["holdtime", "reinit", "timer"]: + cmd = "lldp {0} {1}".format(key, value) + if remove: + return "no {0}".format(cmd) + else: + return cmd + + elif key == "subinterfaces": + cmd = "lldp subinterfaces enable" + if value and not remove: + return cmd + elif (not value and not remove) or (value and remove): + return "no {0}".format(cmd) + + else: + cmd = "lldp tlv-select {0} disable".format(key.replace("_", "-")) + if not value and not remove: + return cmd + elif (value and not remove) or (not value and remove): + return "no {0}".format(cmd) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lldp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lldp_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lldp_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lldp_interfaces/lldp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lldp_interfaces/lldp_interfaces.py new file mode 100644 index 00000000..70394f10 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/lldp_interfaces/lldp_interfaces.py @@ -0,0 +1,278 @@ +# +# -*- 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 iosxr_lldp_interfaces class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_list, + search_obj_in_list, + dict_diff, + remove_empties, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) +from ansible.module_utils.six import iteritems +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + dict_delete, + pad_commands, + flatten_dict, +) + + +class Lldp_interfaces(ConfigBase): + """ + The iosxr_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} + warnings = list() + commands = list() + + if self.state in self.ACTION_STATES: + existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts() + else: + existing_lldp_interfaces_facts = [] + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_lldp_interfaces_facts)) + + if commands and self.state in self.ACTION_STATES: + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True + + if self.state in self.ACTION_STATES: + result["commands"] = commands + + if self.state in self.ACTION_STATES or self.state == "gathered": + changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts() + elif self.state == "rendered": + result["rendered"] = commands + + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed" + ) + result["parsed"] = self.get_lldp_interfaces_facts( + data=running_config + ) + + if self.state in self.ACTION_STATES: + result["before"] = existing_lldp_interfaces_facts + if result["changed"]: + result["after"] = changed_lldp_interfaces_facts + elif self.state == "gathered": + result["gathered"] = changed_lldp_interfaces_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_lldp_interfaces_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_lldp_interfaces_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + state = self._module.params["state"] + commands = [] + 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.extend(self._state_overridden(want, have)) + + elif state == "deleted": + if not want: + for intf in have: + commands.extend( + self._state_deleted({"name": intf["name"]}, intf) + ) + else: + for item in want: + obj_in_have = search_obj_in_list(item["name"], have) + commands.extend(self._state_deleted(item, obj_in_have)) + + else: + for item in want: + name = item["name"] + obj_in_have = search_obj_in_list(name, have) + + if state in ("merged", "rendered"): + commands.extend(self._state_merged(item, obj_in_have)) + + elif state == "replaced": + commands.extend(self._state_replaced(item, obj_in_have)) + + 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 = [] + replaced_commands = [] + merged_commands = [] + + if have: + replaced_commands = self._state_deleted(want, have) + + merged_commands = self._state_merged(want, have) + + if merged_commands and replaced_commands: + del merged_commands[0] + + commands.extend(replaced_commands) + commands.extend(merged_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 intf in have: + intf_in_want = search_obj_in_list(intf["name"], want) + if not intf_in_want: + commands.extend( + self._state_deleted({"name": intf["name"]}, intf) + ) + + for intf in want: + intf_in_have = search_obj_in_list(intf["name"], have) + commands.extend(self._state_replaced(intf, intf_in_have)) + + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + if not have: + have = {"name": want["name"]} + + for key, value in iteritems( + flatten_dict(remove_empties(dict_diff(have, want))) + ): + commands.append(self._compute_commands(key, value)) + + if commands: + pad_commands(commands, want["name"]) + + 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 = [] + + for key, value in iteritems( + flatten_dict(dict_delete(have, remove_empties(want))) + ): + commands.append(self._compute_commands(key, value, remove=True)) + + if commands: + pad_commands(commands, have["name"]) + + return commands + + def _compute_commands(self, key, value=None, remove=False): + if key == "mac_address": + cmd = "lldp destination mac-address {0}".format(value) + if remove: + return "no {0}".format(cmd) + else: + return cmd + + else: + cmd = "lldp {0} disable".format(key) + if not value and not remove: + return cmd + elif (value and not remove) or (not value and remove): + return "no {0}".format(cmd) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospf_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospf_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospf_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospf_interfaces/ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 00000000..343124b5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,240 @@ +# +# -*- 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 iosxr_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.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.rm_templates.ospf_interfaces import ( + Ospf_interfacesTemplate, +) + + +class Ospf_interfaces(ResourceModule): + """ + The iosxr_ospf_interfaces config class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["ospf_interfaces"] + + def __init__(self, module): + super(Ospf_interfaces, self).__init__( + empty_fact_val=[], + facts_module=Facts(module), + module=module, + resource="ospf_interfaces", + tmplt=Ospf_interfacesTemplate(), + ) + self.parsers = [ + "authentication.message_digest", + "authentication.null_auth", + "authentication.message_digest.keychain", + "authentication_key", + "bfd.minimum_interval", + "bfd.multiplier", + "bfd.fast_detect.set", + "bfd.fast_detect.disable", + "bfd.fast_detect.strict_mode", + "cost", + "cost_fallback", + "dead_interval", + "demand_circuit", + "flood_reduction", + "hello_interval", + "link_down.set", + "link_down.disable", + "message_digest_key", + "mpls.set_ldp", + "mpls.ldp_sync", + "mpls.ldp_sync_disable", + "mtu_ignore", + "network", + "packet_size", + "passive", + "prefix_suppression.disable", + "prefix_suppression.secondary_address", + "priority", + "retransmit_interval", + "security.ttl_hops", + "security.ttl", + "transmit_delay", + ] + + def execute_module(self): + """ Execute the module + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """ Generate configuration commands to send based on + want, have and desired state. + """ + wantd = {entry["name"]: entry for entry in self.want} + haved = {entry["name"]: entry for entry in self.have} + + # turn all lists of dicts into dicts prior to merge + for entry in wantd, haved: + self._ospf_int_list_to_dict(entry) + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to wantd + if self.state == "deleted": + haved = { + k: v for k, v in iteritems(haved) if k in wantd or not wantd + } + have_int = [] + if wantd == {}: + for k, have in iteritems(haved): + self._remove_ospf_int(have) + + for k, have in iteritems(haved): + if k in wantd: + have_int.append(k) + self._remove_ospf_int(have) + wantd = {} + + # remove superfluous config for overridden and deleted + if self.state == "overridden": + for k, have in iteritems(haved): + if k not in wantd: + self._remove_ospf_int(have) + if self.state != "deleted": + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _remove_ospf_int(self, entry): + int_name = entry.get("name", {}) + int_addr = entry.get("address_family", {}) + for k, addr in iteritems(int_addr): + for key, value in addr["processes"].items(): + rem_entry = { + "name": int_name, + "afi": addr["afi"], + "process": value["process_id"], + "area": value["area"], + } + self.addcmd(rem_entry, "name", True) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Ospf_interfaces network resource. + """ + self._compare_interface(want=want, have=have) + + self._compare_addr_family(want=want, have=have) + + def _compare_interface(self, want, have): + wdict = want.get("address_family", {}) + hdict = have.get("address_family", {}) + wname = want.get("name") + hname = have.get("name") + h_value = {} + + for key, w_value in iteritems(wdict): + if hdict and hdict.get(key): + h_value = hdict[key] + else: + h_value = None + w = { + "name": wname, + "type": w_value["afi"], + "address_family": w_value, + } + if h_value is not None: + h = { + "name": hname, + "type": h_value["afi"], + "address_family": h_value, + } + else: + h = {} + self.compare(parsers="name", want=w, have=h) + + def _compare_addr_family(self, want, have): + wdict = want.get("address_family", {}) + hdict = have.get("address_family", {}) + wname = want.get("name") + hname = have.get("name") + # Fetch the area info as that would be common to all the attributes per interface + for name, entry in iteritems(wdict): + w_process = {} + h_process = {} + for key, value in iteritems(entry): + if key == "processes": + for pname, pentry in iteritems(value): + w_process = pentry + for key, param in iteritems(entry): + w_addr = {"afi": name, key: param, "processes": w_process} + h_addr = {} + if hdict.get(name): + hdict_entry = hdict.get(name) + for item, value in iteritems(hdict_entry): + if item == "processes": + for pname, pentry in iteritems(value): + h_process = pentry + h_addr = { + "afi": name, + key: hdict[name].pop(key, {}), + "processes": h_process, + } + w = {"name": wname, "address_family": w_addr} + h = {"name": hname, "address_family": h_addr} + self.compare(parsers=self.parsers, want=w, have=h) + for name, entry in iteritems(hdict): + for key, param in iteritems(entry): + h_addr = {"afi": name, key: param} + w_addr = {} + w = {"name": wname, "address_family": w_addr} + h = {"name": hname, "address_family": h_addr} + self.compare(parsers=self.parsers, want=w, have=h) + + def _ospf_int_list_to_dict(self, entry): + for name, family in iteritems(entry): + if "address_family" in family: + family["address_family"] = { + entry["afi"]: entry + for entry in family.get("address_family", []) + } + self._ospf_int_list_to_dict(family["address_family"]) + for name, ospf_processes in iteritems(entry): + if "processes" in ospf_processes: + ospf_processes["processes"] = { + entry["process_id"]: entry + for entry in ospf_processes.get("processes", []) + } + self._ospf_int_list_to_dict(ospf_processes["processes"]) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospfv2/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospfv2/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospfv2/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospfv2/ospfv2.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospfv2/ospfv2.py new file mode 100644 index 00000000..d593c7ac --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospfv2/ospfv2.py @@ -0,0 +1,262 @@ +# +# -*- 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 iosxr_ospfv2 class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from copy import deepcopy +from ansible.module_utils.six import iteritems +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) + +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.rm_templates.ospfv2 import ( + Ospfv2Template, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + + +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", []): + virtual_link = { + entry["id"]: entry + for entry in area.get("virtual_link", []) + } + if bool(virtual_link): + area["virtual_link"] = virtual_link + ranges = { + entry["address"]: entry + for entry in area.get("ranges", []) + } + if bool(ranges): + area["ranges"] = ranges + + 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 == "deleted": + haved_del = deepcopy(haved) + want_process = {} + for k, t_want in iteritems(haved_del): + want_process["process_id"] = t_want.get("process_id") + if not (len(t_want) == 2 and not t_want.get("areas")): + self._compare(want=want_process, have=haved_del.get(k, {})) + if self.state == "overridden": + haved_del = deepcopy(haved) + want = {} + for k, t_want in iteritems(haved_del): + if k not in wantd: + want["process_id"] = t_want.get("process_id") + if not (len(t_want) == 2 and not t_want.get("areas")): + self._compare(want=want, have=haved_del.get(k, {})) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + parsers = [ + "bfd", + "cost", + "weight", + "passive", + "priority", + "protocol", + "auto_cost", + "bandwidth", + "flood_reduction", + "default_metric", + "default_weight", + "router_id", + "demand_circuit", + "packet_size", + "transmit_delay", + "summary_in", + "external_out", + "dead_interval", + "hello_interval", + "authentication", + "adjacency_stagger", + "retransmit_interval", + "mtu_ignore", + "bfd.fast_detect", + "capability", + "capability.opaque", + "admin_distance", + "ospf_distance", + "address_family_unicast", + "loopback_stub_network", + "authentication.message_digest", + "default_information_originate", + "link_down_fast_detect", + "nsr", + "database_filter", + "log_adjacency", + "distribute_bgp_ls", + "distribute_link_state", + "max_lsa", + "max_metric", + "mpls_ldp", + "mpls_traffic_eng", + "microloop_avoidance", + "prefix_suppression", + "protocol_shutdown", + "timers.lsa", + "timers.graceful_shutdown", + "throttle.lsa_all", + "throttle.spf", + "throttle.fast_reroute", + "timers.pacing_flood", + ] + + 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.authentication_key", + "area.authentication.message_digest", + "area.mpls_traffic_eng", + "area.mpls_ldp", + "area.bfd", + "area.bfd.fast_detect", + "area.nssa", + "area.nssa.default_information_originate", + "area.nssa.translate", + "area.default_cost", + "area.stub", + "area.ranges", + "area.cost", + "area.dead_interval", + "area.hello_interval", + "area.transmit_delay", + "area.mtu_ignore", + "area.packet_size", + "area.priority", + "area.weight", + "area.external_out", + "area.summary_in", + "area.demand_circuit", + "area.passive", + ] + self.compare(parsers=parsers, want=want, have=have) + self._areas_compare_virtual_link(want, have) + + def _areas_compare_virtual_link(self, want, have): + wvlinks = want.get("virtual_link", {}) + hvlinks = have.get("virtual_link", {}) + for name, entry in iteritems(wvlinks): + self._area_compare_virtual_link( + want=entry, have=hvlinks.pop(name, {}) + ) + for name, entry in iteritems(hvlinks): + self._area_compare_virtual_link(want={}, have=entry) + + def _area_compare_virtual_link(self, want, have): + parsers = [ + "virtual_link.authentication", + "virtual_link.authentication_key", + "virtual_link.authentication.message_digest", + "virtual_link.hello_interval", + "virtual_link.dead_interval", + "virtual_link.retransmit_interval", + ] + self.compare(parsers=parsers, want=want, have=have) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospfv3/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospfv3/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospfv3/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospfv3/ospfv3.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospfv3/ospfv3.py new file mode 100644 index 00000000..f68b1292 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/ospfv3/ospfv3.py @@ -0,0 +1,255 @@ +# +# -*- 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 + +from copy import deepcopy +from ansible.module_utils.six import iteritems +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) + +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.rm_templates.ospfv3 import ( + Ospfv3Template, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + + +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", []): + virtual_link = { + entry["id"]: entry + for entry in area.get("virtual_link", []) + } + if bool(virtual_link): + area["virtual_link"] = virtual_link + ranges = { + entry["address"]: entry + for entry in area.get("ranges", []) + } + if bool(ranges): + area["ranges"] = ranges + + 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 == "deleted": + haved_del = deepcopy(haved) + want_process = {} + for k, t_want in iteritems(haved_del): + want_process["process_id"] = t_want.get("process_id") + if not (len(t_want) == 2 and not t_want.get("areas")): + self._compare(want=want_process, have=haved_del.get(k, {})) + if self.state == "overridden": + haved_del = deepcopy(haved) + want = {} + for k, t_want in iteritems(haved_del): + if k not in wantd: + want["process_id"] = t_want.get("process_id") + if not (len(t_want) == 2 and not t_want.get("areas")): + self._compare(want=want, have=haved_del.get(k, {})) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + parsers = [ + "bfd.minimum_interval", + "bfd.multiplier", + "cost", + "weight", + "passive", + "priority", + "protocol", + "auto_cost", + "bandwidth", + "flood_reduction", + "default_metric", + "default_weight", + "router_id", + "demand_circuit", + "packet_size", + "transmit_delay", + "dead_interval", + "hello_interval", + "authentication", + "retransmit_interval", + "mtu_ignore", + "bfd.fast_detect", + "capability", + "capability.opaque", + "admin_distance", + "ospf_distance", + "address_family_unicast", + "loopback_stub_network", + "authentication.ipsec", + "default_information_originate", + "link_down_fast_detect", + "nsr", + "database_filter", + "log_adjacency", + "distribute_bgp_ls", + "distribute_link_state", + "max_lsa", + "max_metric", + "mpls_ldp", + "mpls_traffic_eng", + "microloop_avoidance", + "prefix_suppression", + "protocol_shutdown", + "timers.lsa", + "timers.graceful_shutdown", + "throttle.lsa_all", + "throttle.spf", + "throttle.fast_reroute", + "timers.pacing_flood", + ] + + 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.authentication.ipsec", + "area.mpls_traffic_eng", + "area.mpls_ldp", + "area.bfd.minimum_interval", + "area.bfd.multiplier", + "area.bfd.fast_detect", + "area.nssa", + "area.nssa.default_information_originate", + "area.nssa.translate", + "area.default_cost", + "area.stub", + "area.ranges", + "area.cost", + "area.dead_interval", + "area.hello_interval", + "area.transmit_delay", + "area.mtu_ignore", + "area.packet_size", + "area.priority", + "area.weight", + "area.external_out", + "area.summary_in", + "area.demand_circuit", + "area.passive", + ] + self.compare(parsers=parsers, want=want, have=have) + self._areas_compare_virtual_link(want, have) + + def _areas_compare_virtual_link(self, want, have): + wvlinks = want.get("virtual_link", {}) + hvlinks = have.get("virtual_link", {}) + for name, entry in iteritems(wvlinks): + self._area_compare_virtual_link( + want=entry, have=hvlinks.pop(name, {}) + ) + for name, entry in iteritems(hvlinks): + self._area_compare_virtual_link(want={}, have=entry) + + def _area_compare_virtual_link(self, want, have): + parsers = [ + "virtual_link.authentication", + "virtual_link.authentication_key", + "virtual_link.authentication.message_digest", + "virtual_link.hello_interval", + "virtual_link.dead_interval", + "virtual_link.retransmit_interval", + ] + self.compare(parsers=parsers, want=want, have=have) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/static_routes/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/static_routes/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/static_routes/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/static_routes/static_routes.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/static_routes/static_routes.py new file mode 100644 index 00000000..246966e4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/config/static_routes/static_routes.py @@ -0,0 +1,549 @@ +# +# -*- 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 iosxr_static_routes class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from 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.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + search_obj_in_list, + remove_empties, + dict_diff, + dict_merge, +) + + +class Static_routes(ConfigBase): + """ + The iosxr_static_routes class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["static_routes"] + + def __init__(self, module): + super(Static_routes, self).__init__(module) + + def get_static_routes_facts(self, data=None): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, self.gather_network_resources, data=data + ) + static_routes_facts = facts["ansible_network_resources"].get( + "static_routes" + ) + if not static_routes_facts: + return [] + return static_routes_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + warnings = list() + commands = list() + + if self.state in self.ACTION_STATES: + existing_static_routes_facts = self.get_static_routes_facts() + else: + existing_static_routes_facts = [] + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_static_routes_facts)) + + if commands and self.state in self.ACTION_STATES: + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True + + if self.state in self.ACTION_STATES: + result["commands"] = commands + + if self.state in self.ACTION_STATES or self.state == "gathered": + changed_static_routes_facts = self.get_static_routes_facts() + + elif self.state == "rendered": + result["rendered"] = commands + + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed" + ) + result["parsed"] = self.get_static_routes_facts( + data=running_config + ) + + 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"] + commands = [] + + 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.extend(self._state_overridden(want, have)) + + elif state == "deleted": + if not want: + if len(have) >= 1: + return "no router static" + + else: + for w_item in want: + obj_in_have = self._find_vrf(w_item, have) + if obj_in_have: + commands.extend( + self._state_deleted( + remove_empties(w_item), obj_in_have + ) + ) + + else: + for w_item in want: + obj_in_have = self._find_vrf(w_item, have) + if state == "merged" or self.state == "rendered": + commands.extend( + self._state_merged(remove_empties(w_item), obj_in_have) + ) + + elif state == "replaced": + commands.extend( + self._state_replaced( + remove_empties(w_item), obj_in_have + ) + ) + + if commands: + commands.insert(0, "router static") + + 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 want_afi in want.get("address_families", []): + have_afi = ( + self.find_af_context( + want_afi, have.get("address_families", []) + ) + or {} + ) + update_commands = [] + for want_route in want_afi.get("routes", []): + have_route = ( + search_obj_in_list( + want_route["dest"], + have_afi.get("routes", []), + key="dest", + ) + or {} + ) + + rotated_have_next_hops = self.rotate_next_hops( + have_route.get("next_hops", {}) + ) + rotated_want_next_hops = self.rotate_next_hops( + want_route.get("next_hops", {}) + ) + + for key in rotated_have_next_hops.keys(): + if key not in rotated_want_next_hops: + cmd = "no {0}".format(want_route["dest"]) + for item in key: + if "." in item or ":" in item or "/" in item: + cmd += " {0}".format(item) + else: + cmd += " vrf {0}".format(item) + update_commands.append(cmd) + + for key, value in iteritems(rotated_want_next_hops): + if key in rotated_have_next_hops: + existing = True + have_exit_point_attribs = rotated_have_next_hops[key] + + else: + existing = False + have_exit_point_attribs = {} + + updates = dict_diff(have_exit_point_attribs, value) + + if updates or not existing: + update_commands.append( + self._compute_commands( + dest=want_route["dest"], + next_hop=key, + updates=updates, + ) + ) + + if update_commands: + update_commands.insert( + 0, + "address-family {0} {1}".format( + want_afi["afi"], want_afi["safi"] + ), + ) + commands.extend(update_commands) + + if "vrf" in want and update_commands: + commands.insert(0, "vrf {0}".format(want["vrf"])) + + 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 = [] + + # Iterate through all the entries, i.e., VRFs and Global entry in have + # and fully remove the ones that are not present in want and then call + # replaced + + for h_item in have: + w_item = self._find_vrf(h_item, want) + + # Delete all the top-level keys (VRFs/Global Route Entry) that are + # not specified in want. + if not w_item: + if "vrf" in h_item: + commands.append("no vrf {0}".format(h_item["vrf"])) + else: + for have_afi in h_item.get("address_families", []): + commands.append( + "no address-family {0} {1}".format( + have_afi["afi"], have_afi["safi"] + ) + ) + + # For VRFs/Global Entry present in want, we also need to delete extraneous routes + # from them. We cannot reuse `_state_replaced` for this purpose since its scope is + # limited to replacing a single `dest`. + else: + del_cmds = [] + for have_afi in h_item.get("address_families", []): + want_afi = ( + self.find_af_context( + have_afi, w_item.get("address_families", []) + ) + or {} + ) + update_commands = [] + for h_route in have_afi.get("routes", []): + w_route = ( + search_obj_in_list( + h_route["dest"], + want_afi.get("routes", []), + key="dest", + ) + or {} + ) + if not w_route: + update_commands.append( + "no {0}".format(h_route["dest"]) + ) + + if update_commands: + update_commands.insert( + 0, + "address-family {0} {1}".format( + want_afi["afi"], want_afi["safi"] + ), + ) + del_cmds.extend(update_commands) + + if "vrf" in want and update_commands: + del_cmds.insert(0, "vrf {0}".format(want["vrf"])) + + commands.extend(del_cmds) + + # We finally call `_state_replaced` to replace exiting `dest` entries + # or add new ones as specified in want. + for w_item in want: + h_item = self._find_vrf(w_item, have) + commands.extend( + self._state_replaced(remove_empties(w_item), h_item) + ) + + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + for want_afi in want.get("address_families", []): + have_afi = ( + self.find_af_context( + want_afi, have.get("address_families", []) + ) + or {} + ) + + update_commands = [] + for want_route in want_afi.get("routes", []): + have_route = ( + search_obj_in_list( + want_route["dest"], + have_afi.get("routes", []), + key="dest", + ) + or {} + ) + + # convert the next_hops list of dictionaries to dictionary of + # dictionaries with (`dest_vrf`, `forward_router_address`, `interface`) tuple + # being the key for each dictionary. + # a combination of these 3 attributes uniquely identifies a route entry. + # in case `dest_vrf` is not specified, `forward_router_address` and `interface` + # become the unique identifier + rotated_have_next_hops = self.rotate_next_hops( + have_route.get("next_hops", {}) + ) + rotated_want_next_hops = self.rotate_next_hops( + want_route.get("next_hops", {}) + ) + + # for every dict in the want next_hops dictionaries, if the key + # is present in `rotated_have_next_hops`, we set `existing` to True, + # which means the the given want exit point exists and we run dict_diff + # on `value` which is basically all the other attributes of the exit point + # if the key is not present, it means that this is a new exit point + for key, value in iteritems(rotated_want_next_hops): + if key in rotated_have_next_hops: + existing = True + have_exit_point_attribs = rotated_have_next_hops[key] + + else: + existing = False + have_exit_point_attribs = {} + + updates = dict_diff(have_exit_point_attribs, value) + if updates or not existing: + update_commands.append( + self._compute_commands( + dest=want_route["dest"], + next_hop=key, + # dict_merge() is necessary to make sure that we + # don't end up overridding the entry and also to + # allow incremental updates + updates=dict_merge( + rotated_have_next_hops.get(key, {}), + updates, + ), + ) + ) + + if update_commands: + update_commands.insert( + 0, + "address-family {0} {1}".format( + want_afi["afi"], want_afi["safi"] + ), + ) + commands.extend(update_commands) + + if "vrf" in want and update_commands: + commands.insert(0, "vrf {0}".format(want["vrf"])) + + 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 "address_families" not in want: + return ["no vrf {0}".format(want["vrf"])] + + else: + for want_afi in want.get("address_families", []): + have_afi = ( + self.find_af_context( + want_afi, have.get("address_families", []) + ) + or {} + ) + if have_afi: + commands.append( + "no address-family {0} {1}".format( + have_afi["afi"], have_afi["safi"] + ) + ) + if "vrf" in want and commands: + commands.insert(0, "vrf {0}".format(want["vrf"])) + + return commands + + def _find_vrf(self, item, entries): + """ This method iterates through the items + in `entries` and returns the object that + matches `item`. + + :rtype: A dict + :returns: the obj in `entries` that matches `item` + """ + obj = {} + afi = item.get("vrf") + + if afi: + obj = search_obj_in_list(afi, entries, key="vrf") or {} + else: + for x in entries: + if "vrf" not in remove_empties(x): + obj = x + break + return obj + + def find_af_context(self, want_af_context, have_address_families): + """ This method iterates through the have AFs + and returns the one that matches the want AF + + :rtype: A dict + :returns: the corresponding AF in have AFs + that matches the want AF + """ + for have_af in have_address_families: + if ( + have_af["afi"] == want_af_context["afi"] + and have_af["safi"] == want_af_context["safi"] + ): + return have_af + + def rotate_next_hops(self, next_hops): + """ This method iterates through the list of + next hops for a given destination network + and converts it to a dictionary of dictionaries. + Each dictionary has a primary key indicated by the + tuple of `dest_vrf`, `forward_router_address` and + `interface` and the value of this key is a dictionary + that contains all the other attributes of the next hop. + + :rtype: A dict + :returns: A next_hops list in a dictionary of dictionaries format + """ + next_hops_dict = {} + + for entry in next_hops: + entry = entry.copy() + key_list = [] + + for x in ["dest_vrf", "forward_router_address", "interface"]: + if entry.get(x): + key_list.append(entry.pop(x)) + + key = tuple(key_list) + next_hops_dict[key] = entry + + return next_hops_dict + + def _compute_commands(self, dest, next_hop, updates=None): + """ This method computes a static route entry command + from the specified `dest`, `next_hop` and `updates` + + :rtype: A str + :returns: A platform specific static routes command + """ + if not updates: + updates = {} + + command = dest + + for x in next_hop: + if "." in x or ":" in x or "/" in x: + command += " {0}".format(x) + else: + command += " vrf {0}".format(x) + + for key in sorted(updates): + if key == "admin_distance": + command += " {0}".format(updates[key]) + else: + command += " {0} {1}".format(key, updates[key]) + + return command diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/acl_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/acl_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/acl_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/acl_interfaces/acl_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/acl_interfaces/acl_interfaces.py new file mode 100644 index 00000000..e9c476a3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/acl_interfaces/acl_interfaces.py @@ -0,0 +1,78 @@ +# +# -*- 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 iosxr 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 + +from copy import deepcopy +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.rm_templates.acl_interfaces import ( + Acl_interfacesTemplate, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.acl_interfaces.acl_interfaces import ( + Acl_interfacesArgs, +) + + +class Acl_interfacesFacts(object): + """ The iosxr 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 populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for acl_interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + + if not data: + data = connection.get_config(flags="interface") + + config_parser = Acl_interfacesTemplate(lines=data.splitlines()) + entry = sorted( + list(config_parser.parse().values()), + key=lambda k, sk="name": k[sk], + ) + if entry: + for item in entry: + item["access_groups"] = sorted( + list(item["access_groups"].values()), + key=lambda k, sk="afi": k[sk], + ) + + ansible_facts["ansible_network_resources"].pop("acl_interfaces", None) + facts = {"acl_interfaces": []} + params = utils.validate_config(self.argument_spec, {"config": entry}) + for cfg in params["config"]: + facts["acl_interfaces"].append(utils.remove_empties(cfg)) + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/acls/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/acls/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/acls/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/acls/acls.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/acls/acls.py new file mode 100644 index 00000000..32375b7f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/acls/acls.py @@ -0,0 +1,457 @@ +# +# -*- 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 iosxr 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 collections import deque +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.acls.acls import ( + AclsArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + isipaddress, +) + +PROTOCOL_OPTIONS = { + "tcp": ("ack", "fin", "psh", "rst", "syn", "urg", "established"), + "igmp": ( + "dvmrp", + "host_query", + "host_report", + "mtrace", + "mtrace_response", + "pim", + "trace", + "v2_leave", + "v2_report", + "v3_report", + ), + "icmp": ( + "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", + ), + "icmpv6": ( + "address_unreachable", + "administratively_prohibited", + "beyond_scope_of_source_address", + "destination_unreachable", + "echo", + "echo_reply", + "erroneous_header_field", + "group_membership_query", + "group_membership_report", + "group_membership_termination", + "host_unreachable", + "nd_na", + "nd_ns", + "neighbor_redirect", + "no_route_to_destination", + "node_information_request_is_refused", + "node_information_successful_reply", + "packet_too_big", + "parameter_problem", + "port_unreachable", + "query_subject_is_IPv4address", + "query_subject_is_IPv6address", + "query_subject_is_domainname", + "reassembly_timeout", + "redirect", + "router_advertisement", + "router_renumbering", + "router_solicitation", + "rr_command", + "rr_result", + "rr_seqnum_reset", + "time_exceeded", + "ttl_exceeded", + "unknown_query_type", + "unreachable", + "unrecognized_next_header", + "unrecognized_option", + "whoareyou_reply", + "whoareyou_request", + ), +} + + +class AclsFacts(object): + """ The iosxr 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_device_data(self, connection): + return connection.get("show access-lists afi-all") + + 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_device_data(connection) + + objs = [] + + acl_lines = data.splitlines() + + # We iterate through the data and create a list of ACLs + # where each ACL is a dictionary in the following format: + # {'afi': 'ipv4', 'name': 'acl_1', 'aces': ['10 permit 172.16.0.0 0.0.255.255', '20 deny 192.168.34.0 0.0.0.255']} + if acl_lines: + acl, acls = {}, [] + for line in acl_lines: + if line.startswith("ip"): + if acl: + acls.append(acl) + acl = {"aces": []} + acl["afi"], acl["name"] = line.split()[0], line.split()[2] + else: + acl["aces"].append(line.strip()) + acls.append(acl) + + # Here we group the ACLs based on AFI + # { + # 'ipv6': [{'aces': ['10 permit ipv6 2000::/12 any'], 'name': 'acl_2'}], + # 'ipv4': [{'aces': ['10 permit 172.16.0.0 0.0.255.255', '20 deny 192.168.34.0 0.0.0.255'], 'name': 'acl_1'}, + # {'aces': ['20 deny 10.0.0.0/8 log'], 'name': 'acl_3'}] + # } + + grouped_acls = {"ipv4": [], "ipv6": []} + for acl in acls: + acl_copy = deepcopy(acl) + del acl_copy["afi"] + grouped_acls[acl["afi"]].append(acl_copy) + + # Now that we have the ACLs in a fairly structured format, + # we pass it on to render_config to convert it to model spec + for key, value in iteritems(grouped_acls): + obj = self.render_config(self.generated_spec, value) + if obj: + obj["afi"] = key + objs.append(obj) + + ansible_facts["ansible_network_resources"].pop("acls", None) + facts = {} + + 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 + + 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["acls"] = [] + + for item in conf: + acl = {"name": item["name"]} + aces = item.get("aces", []) + if aces: + acl["aces"] = [] + for ace in aces: + acl["aces"].append(self._render_ace(ace)) + config["acls"].append(acl) + + return utils.remove_empties(config) + + def _render_ace(self, ace): + """ + Parses an Access Control Entry (ACE) and converts it + into model spec + + :param ace: An ACE in device specific format + :rtype: dictionary + :returns: The ACE in structured format + """ + + def __parse_src_dest(rendered_ace, ace_queue, direction): + """ + Parses the ACE queue and populates address, wildcard_bits, + host or any keys in the source/destination dictionary of + ace dictionary, i.e., `rendered_ace`. + + :param rendered_ace: The dictionary containing the ACE in structured format + :param ace_queue: The ACE queue + :param direction: Specifies whether to populate `source` or `destination` + dictionary + """ + element = ace_queue.popleft() + if element == "host": + rendered_ace[direction] = {"host": ace_queue.popleft()} + + elif element == "any": + rendered_ace[direction] = {"any": True} + + elif "/" in element: + rendered_ace[direction] = {"prefix": element} + + elif isipaddress(element): + rendered_ace[direction] = { + "address": element, + "wildcard_bits": ace_queue.popleft(), + } + + def __parse_port_protocol(rendered_ace, ace_queue, direction): + """ + Parses the ACE queue and populates `port_protocol` dictionary in the + ACE dictionary, i.e., `rendered_ace`. + + :param rendered_ace: The dictionary containing the ACE in structured format + :param ace_queue: The ACE queue + :param direction: Specifies whether to populate `source` or `destination` + dictionary + """ + if len(ace_queue) > 0 and ace_queue[0] in ( + "eq", + "gt", + "lt", + "neq", + "range", + ): + element = ace_queue.popleft() + port_protocol = {} + + if element == "range": + port_protocol["range"] = { + "start": ace_queue.popleft(), + "end": ace_queue.popleft(), + } + else: + port_protocol[element] = ace_queue.popleft() + + rendered_ace[direction]["port_protocol"] = port_protocol + + def __parse_protocol_options(rendered_ace, ace_queue, protocol): + """ + Parses the ACE queue and populates protocol specific options + of the required dictionary and updates the ACE dictionary, i.e., + `rendered_ace`. + + :param rendered_ace: The dictionary containing the ACE in structured format + :param ace_queue: The ACE queue + :param protocol: Specifies the protocol that will be populated under + `protocol_options` dictionary + """ + if len(ace_queue) > 0: + protocol_options = {protocol: {}} + + for match_bit in PROTOCOL_OPTIONS.get(protocol, ()): + if match_bit.replace("_", "-") in ace_queue: + protocol_options[protocol][match_bit] = True + ace_queue.remove(match_bit.replace("_", "-")) + + rendered_ace["protocol_options"] = protocol_options + + def __parse_match_options(rendered_ace, ace_queue): + """ + Parses the ACE queue and populates remaining options in the ACE dictionary, + i.e., `rendered_ace` + + :param rendered_ace: The dictionary containing the ACE in structured format + :param ace_queue: The ACE queue + """ + if len(ace_queue) > 0: + # We deepcopy the actual queue and iterate through the + # copied queue. However, we pop off the elements from + # the actual queue. Then, in every pass we update the copied + # queue with the current state of the original queue. + # This is done because a queue cannot be mutated during iteration. + copy_ace_queue = deepcopy(ace_queue) + + for element in copy_ace_queue: + if element == "precedence": + ace_queue.popleft() + rendered_ace["precedence"] = ace_queue.popleft() + + elif element == "dscp": + ace_queue.popleft() + dscp = {} + operation = ace_queue.popleft() + + if operation in ("eq", "gt", "neq", "lt", "range"): + if operation == "range": + dscp["range"] = { + "start": ace_queue.popleft(), + "end": ace_queue.popleft(), + } + else: + dscp[operation] = ace_queue.popleft() + else: + # `dscp` can be followed by either the dscp value itself or + # the same thing can be represented using "dscp eq <dscp_value>". + # In both cases, it would show up as {'dscp': {'eq': "dscp_value"}}. + dscp["eq"] = operation + + rendered_ace["dscp"] = dscp + + elif element in ("packet-length", "ttl"): + ace_queue.popleft() + element_dict = {} + operation = ace_queue.popleft() + + if operation == "range": + element_dict["range"] = { + "start": ace_queue.popleft(), + "end": ace_queue.popleft(), + } + else: + element_dict[operation] = ace_queue.popleft() + + rendered_ace[element.replace("-", "_")] = element_dict + + elif element in ( + "log", + "log-input", + "fragments", + "icmp-off", + "capture", + "destopts", + "authen", + "routing", + "hop-by-hop", + ): + rendered_ace[element.replace("-", "_")] = True + ace_queue.remove(element) + + copy_ace_queue = deepcopy(ace_queue) + + rendered_ace = {} + split_ace = ace.split() + + # Create a queue with each word in the ace + # We parse each element and pop it off the queue + ace_queue = deque(split_ace) + + # An ACE will always have a sequence number, even if + # it is not explicitly provided while configuring + sequence = int(ace_queue.popleft()) + rendered_ace["sequence"] = sequence + operation = ace_queue.popleft() + + if operation == "remark": + rendered_ace["remark"] = " ".join(split_ace[2:]) + + else: + rendered_ace["grant"] = operation + + # If the entry is a non-remark entry, the third element + # will always be the protocol specified. By default, it's + # the AFI. + rendered_ace["protocol"] = ace_queue.popleft() + + # Populate source dictionary + __parse_src_dest(rendered_ace, ace_queue, direction="source") + # Populate port_protocol key in source dictionary + __parse_port_protocol(rendered_ace, ace_queue, direction="source") + # Populate destination dictionary + __parse_src_dest(rendered_ace, ace_queue, direction="destination") + # Populate port_protocol key in destination dictionary + __parse_port_protocol( + rendered_ace, ace_queue, direction="destination" + ) + # Populate protocol_options dictionary + __parse_protocol_options( + rendered_ace, ace_queue, protocol=rendered_ace["protocol"] + ) + # Populate remaining match options' dictionaries + __parse_match_options(rendered_ace, ace_queue) + + # At this stage the queue should be empty + # If the queue is not empty, it means that + # we haven't been able to parse the entire ACE + # In this case, we add the whole unprocessed ACE + # to a key called `line` and send it back + if len(ace_queue) > 0: + rendered_ace = { + "sequence": sequence, + "line": " ".join(split_ace[1:]), + } + + return utils.remove_empties(rendered_ace) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/facts.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/facts.py new file mode 100644 index 00000000..1441ef6d --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/facts.py @@ -0,0 +1,122 @@ +# +# -*- 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 iosxr +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.iosxr.plugins.module_utils.network.iosxr.facts.legacy.base import ( + Default, + Hardware, + Interfaces, + Config, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.lacp.lacp import ( + LacpFacts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.lacp_interfaces.lacp_interfaces import ( + Lacp_interfacesFacts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.lldp_global.lldp_global import ( + Lldp_globalFacts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.lldp_interfaces.lldp_interfaces import ( + Lldp_interfacesFacts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.interfaces.interfaces import ( + InterfacesFacts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.lag_interfaces.lag_interfaces import ( + Lag_interfacesFacts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.l2_interfaces.l2_interfaces import ( + L2_InterfacesFacts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.l3_interfaces.l3_interfaces import ( + L3_InterfacesFacts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.acl_interfaces.acl_interfaces import ( + Acl_interfacesFacts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.acls.acls import ( + AclsFacts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.static_routes.static_routes import ( + Static_routesFacts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.ospfv2.ospfv2 import ( + Ospfv2Facts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.ospfv3.ospfv3 import ( + Ospfv3Facts, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.ospf_interfaces.ospf_interfaces import ( + Ospf_interfacesFacts, +) + + +FACT_LEGACY_SUBSETS = dict( + default=Default, hardware=Hardware, interfaces=Interfaces, config=Config +) +FACT_RESOURCE_SUBSETS = dict( + lacp=LacpFacts, + lacp_interfaces=Lacp_interfacesFacts, + lldp_global=Lldp_globalFacts, + lldp_interfaces=Lldp_interfacesFacts, + interfaces=InterfacesFacts, + l2_interfaces=L2_InterfacesFacts, + lag_interfaces=Lag_interfacesFacts, + l3_interfaces=L3_InterfacesFacts, + acl_interfaces=Acl_interfacesFacts, + acls=AclsFacts, + static_routes=Static_routesFacts, + ospfv2=Ospfv2Facts, + ospfv3=Ospfv3Facts, + ospf_interfaces=Ospf_interfacesFacts, +) + + +class Facts(FactsBase): + """ The fact class for iosxr + """ + + 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 iosxr + + :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/iosxr/plugins/module_utils/network/iosxr/facts/interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/interfaces/interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/interfaces/interfaces.py new file mode 100644 index 00000000..bc4450fc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/interfaces/interfaces.py @@ -0,0 +1,110 @@ +# +# -*- 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 iosxr 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.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + get_interface_type, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.interfaces.interfaces import ( + InterfacesArgs, +) + + +class InterfacesFacts(object): + """ The iosxr 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 populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for interfaces + :param module: the module instance + :param connection: the device connection + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + objs = [] + if not data: + data = connection.get("show running-config 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["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 match.group(1).lower() == "preconfigure": + match = re.search(r"^(\S+) (.*)", conf) + if match: + intf = match.group(2) + + if get_interface_type(intf) == "unknown": + return {} + # populate the facts from the configuration + config["name"] = intf + config["description"] = utils.parse_conf_arg(conf, "description") + if utils.parse_conf_arg(conf, "speed"): + config["speed"] = int(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/iosxr/plugins/module_utils/network/iosxr/facts/l2_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/l2_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/l2_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/l2_interfaces/l2_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/l2_interfaces/l2_interfaces.py new file mode 100644 index 00000000..82220d89 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/l2_interfaces/l2_interfaces.py @@ -0,0 +1,134 @@ +# +# -*- 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 iosxr l2_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.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + get_interface_type, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.l2_interfaces.l2_interfaces import ( + L2_InterfacesArgs, +) + + +class L2_InterfacesFacts(object): + """ The iosxr 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 populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for l2_interfaces + :param module: the module instance + :param connection: the device connection + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + objs = [] + if not data: + data = connection.get("show running-config 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["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 match.group(1).lower() == "preconfigure": + match = re.search(r"^(\S+) (.*)", conf) + if match: + intf = match.group(2) + + if get_interface_type(intf) == "unknown": + return {} + + if intf.lower().startswith("gi"): + config["name"] = intf + + # populate the facts from the configuration + native_vlan = re.search(r"dot1q native vlan (\d+)", conf) + if native_vlan: + config["native_vlan"] = int(native_vlan.group(1)) + + dot1q = utils.parse_conf_arg(conf, "encapsulation dot1q") + config["q_vlan"] = [] + if dot1q: + config["q_vlan"].append(int(dot1q.split(" ")[0])) + if len(dot1q.split(" ")) > 1: + config["q_vlan"].append(int(dot1q.split(" ")[2])) + + if utils.parse_conf_cmd_arg(conf, "l2transport", True): + config["l2transport"] = True + if utils.parse_conf_arg(conf, "propagate"): + config["propagate"] = True + config["l2protocol"] = [] + + cdp = utils.parse_conf_arg(conf, "l2protocol cdp") + pvst = utils.parse_conf_arg(conf, "l2protocol pvst") + stp = utils.parse_conf_arg(conf, "l2protocol stp") + vtp = utils.parse_conf_arg(conf, "l2protocol vtp") + if cdp: + config["l2protocol"].append({"cdp": cdp}) + if pvst: + config["l2protocol"].append({"pvst": pvst}) + if stp: + config["l2protocol"].append({"stp": stp}) + if vtp: + config["l2protocol"].append({"vtp": vtp}) + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/l3_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/l3_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/l3_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/l3_interfaces/l3_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/l3_interfaces/l3_interfaces.py new file mode 100644 index 00000000..5bc589d3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/l3_interfaces/l3_interfaces.py @@ -0,0 +1,126 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The iosxr_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.iosxr.plugins.module_utils.network.iosxr.utils.utils import ( + get_interface_type, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.l3_interfaces.l3_interfaces import ( + L3_InterfacesArgs, +) + + +class L3_InterfacesFacts(object): + """ The iosxr_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 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 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 match.group(1).lower() == "preconfigure": + match = re.search(r"^(\S+) (.*)", conf) + if match: + intf = match.group(2) + + if get_interface_type(intf) == "unknown": + return {} + + # populate the facts from the configuration + config["name"] = intf + + # Get the configured IPV4 details + ipv4 = [] + ipv4_all = re.findall(r"ipv4 address (\S+.*)", conf) + for each in ipv4_all: + each_ipv4 = dict() + if "secondary" in each: + each_ipv4["address"] = each.split(" secondary")[0] + each_ipv4["secondary"] = True + else: + each_ipv4["address"] = each + 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() + each_ipv6["address"] = each + ipv6.append(each_ipv6) + config["ipv6"] = ipv6 + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lacp/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lacp/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lacp/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lacp/lacp.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lacp/lacp.py new file mode 100644 index 00000000..e052f116 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lacp/lacp.py @@ -0,0 +1,89 @@ +# +# -*- 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 iosxr 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.iosxr.plugins.module_utils.network.iosxr.argspec.lacp.lacp import ( + LacpArgs, +) + + +class LacpFacts(object): + """ The iosxr 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 not data: + data = connection.get_config(flags="lacp") + + 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) + + system_priority = utils.parse_conf_arg(conf, "priority") + config["system"]["priority"] = ( + int(system_priority) if system_priority else system_priority + ) + config["system"]["mac"]["address"] = utils.parse_conf_arg(conf, "mac") + + return config diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lacp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lacp_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lacp_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lacp_interfaces/lacp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 00000000..8cb5fcda --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lacp_interfaces/lacp_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 iosxr 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.iosxr.plugins.module_utils.network.iosxr.argspec.lacp_interfaces.lacp_interfaces import ( + Lacp_interfacesArgs, +) +from ansible.module_utils.six import iteritems + + +class Lacp_interfacesFacts(object): + """ The iosxr 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 not data: + data = connection.get_config(flags="interface") + interfaces = ("\n" + data).split("\ninterface ") + + objs = [] + for interface in interfaces: + obj = self.render_config(self.generated_spec, interface) + if obj: + objs.append(obj) + + ansible_facts["ansible_network_resources"].pop("lacp_interfaces", None) + 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"(GigabitEthernet|Bundle-Ether|TenGigE|FortyGigE|HundredGigE)(\S+)", + conf, + re.M, + ) + if match: + config["name"] = match.group(1) + match.group(2) + + temp = { + "churn_logging": "lacp churn logging", + "switchover_suppress_flaps": "lacp switchover suppress-flaps", + "collector_max_delay": "lacp collector-max-delay", + "period": "lacp period", + } + + for key, value in iteritems(temp): + config[key] = utils.parse_conf_arg(conf, value) + + for key in config["system"].keys(): + config["system"][key] = utils.parse_conf_arg( + conf, "lacp system {0}".format(key) + ) + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lag_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lag_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lag_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lag_interfaces/lag_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lag_interfaces/lag_interfaces.py new file mode 100644 index 00000000..2b911398 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lag_interfaces/lag_interfaces.py @@ -0,0 +1,141 @@ +# +# -*- 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 iosxr 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.iosxr.plugins.module_utils.network.iosxr.argspec.lag_interfaces.lag_interfaces import ( + Lag_interfacesArgs, +) + + +class Lag_interfacesFacts(object): + """ The iosxr 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 lag_interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + + if not data: + data = connection.get_config(flags="interface") + interfaces = ("\n" + data).split("\ninterface ") + + objs = [] + + for interface in interfaces: + if interface.startswith("Bundle-Ether"): + obj = self.render_config( + self.generated_spec, interface, interfaces + ) + if obj: + objs.append(obj) + + ansible_facts["ansible_network_resources"].pop("lag_interfaces", None) + facts = {} + + 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, data): + """ + 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"(Bundle-Ether)(\d+)", conf, re.M) + if match: + config["name"] = match.group(1) + match.group(2) + config["load_balancing_hash"] = utils.parse_conf_arg( + conf, "bundle load-balancing hash" + ) + config["mode"] = utils.parse_conf_arg(conf, "lacp mode") + config["links"]["max_active"] = utils.parse_conf_arg( + conf, "bundle maximum-active links" + ) + config["links"]["min_active"] = utils.parse_conf_arg( + conf, "bundle minimum-active links" + ) + config["members"] = self.parse_members(match.group(2), data) + + return utils.remove_empties(config) + + def parse_members(self, bundle_id, interfaces): + """ + Renders a list of member interfaces for every bundle + present in running-config. + + :param bundle_id: The Bundle-Ether ID fetched from running-config + :param interfaces: Data of all interfaces present in running-config + :rtype: list + :returns: A list of member interfaces + """ + + def _parse_interface(name): + if name.startswith("preconfigure"): + return name.split()[1] + else: + return name.split()[0] + + members = [] + for interface in interfaces: + if not interface.startswith("Bu"): + match = re.search( + r"bundle id (\d+) mode (\S+)", interface, re.M + ) + if match: + if bundle_id == match.group(1): + members.append( + { + "member": _parse_interface(interface), + "mode": match.group(2), + } + ) + + return members diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/legacy/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/legacy/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/legacy/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/legacy/base.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/legacy/base.py new file mode 100644 index 00000000..463190cd --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/legacy/base.py @@ -0,0 +1,261 @@ +# -*- 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 iosxr 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.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + run_commands, + get_capabilities, +) +from ansible.module_utils.six import iteritems +from ansible.module_utils.six.moves import zip + + +class FactsBase(object): + + COMMANDS = frozenset() + + def __init__(self, module): + self.module = module + self.facts = dict() + self.warnings = list() + self.responses = None + + def populate(self): + self.responses = run_commands( + self.module, list(self.COMMANDS), check_rc=False + ) + + +class Default(FactsBase): + def populate(self): + self.facts.update(self.platform_facts()) + + 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 /all", "show memory summary"] + + def populate(self): + super(Hardware, self).populate() + data = self.responses[0] + self.facts["filesystems"] = self.parse_filesystems(data) + + data = self.responses[1] + match = re.search(r"Physical Memory: (\d+)M total \((\d+)", data) + if match: + self.facts["memtotal_mb"] = match.group(1) + self.facts["memfree_mb"] = match.group(2) + + def parse_filesystems(self, data): + return re.findall(r"^Directory of (\S+)", data, re.M) + + +class Config(FactsBase): + + COMMANDS = ["show running-config"] + + def populate(self): + super(Config, self).populate() + self.facts["config"] = self.responses[0] + + +class Interfaces(FactsBase): + + COMMANDS = [ + "show interfaces", + "show ipv6 interface", + "show lldp", + "show lldp neighbors detail", + ] + + def populate(self): + super(Interfaces, self).populate() + self.facts["all_ipv4_addresses"] = list() + self.facts["all_ipv6_addresses"] = list() + + interfaces = self.parse_interfaces(self.responses[0]) + self.facts["interfaces"] = self.populate_interfaces(interfaces) + + data = self.responses[1] + if len(data) > 0: + data = self.parse_interfaces(data) + self.populate_ipv6_interfaces(data) + + if "LLDP is not enabled" not in self.responses[2]: + neighbors = self.responses[3] + self.facts["neighbors"] = self.parse_neighbors(neighbors) + + 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) + + ipv4 = self.parse_ipv4(value) + intf["ipv4"] = self.parse_ipv4(value) + if ipv4: + self.add_ip_address(ipv4["address"], "ipv4") + + intf["mtu"] = self.parse_mtu(value) + intf["bandwidth"] = self.parse_bandwidth(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_ipv6_interfaces(self, data): + for key, value in iteritems(data): + if key in ["No", "RPF"] or key.startswith("IP"): + continue + 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() + nbors = neighbors.split( + "------------------------------------------------" + ) + for entry in nbors[1:]: + if entry == "": + continue + intf = self.parse_lldp_intf(entry) + if intf not in facts: + facts[intf] = list() + fact = dict() + fact["host"] = self.parse_lldp_host(entry) + fact["remote_description"] = self.parse_lldp_remote_desc(entry) + fact["port"] = self.parse_lldp_port(entry) + 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"address is (\S+)", data) + if match: + return match.group(1) + + def parse_ipv4(self, data): + match = re.search(r"Internet address is (\S+)/(\d+)", data) + if match: + addr = match.group(1) + masklen = int(match.group(2)) + return dict(address=addr, masklen=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+)(?: D|-d)uplex", 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+?$", 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 Interface: (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_lldp_remote_desc(self, data): + match = re.search(r"Port Description: (.+)$", 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) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lldp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lldp_global/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lldp_global/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lldp_global/lldp_global.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lldp_global/lldp_global.py new file mode 100644 index 00000000..13d7f0cf --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lldp_global/lldp_global.py @@ -0,0 +1,107 @@ +# +# -*- 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 iosxr lldp 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.iosxr.plugins.module_utils.network.iosxr.argspec.lldp_global.lldp_global import ( + Lldp_globalArgs, +) + + +class Lldp_globalFacts(object): + """ The iosxr lldp 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 populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for lldp + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + data = connection.get_config(flags="lldp") + + obj = {} + if data: + lldp_obj = self.render_config(self.generated_spec, data) + if lldp_obj: + obj = lldp_obj + + ansible_facts["ansible_network_resources"].pop("lldp_global", None) + facts = {} + + params = utils.validate_config(self.argument_spec, {"config": obj}) + 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) + + for key in spec.keys(): + if key == "subinterfaces": + config[key] = True if "subinterfaces enable" in conf else None + + elif key == "tlv_select": + for item in [ + "system_name", + "port_description", + "management_address", + "system_description", + "system_capabilities", + ]: + config[key][item] = ( + False + if ("{0} disable".format(item.replace("_", "-"))) + in conf + else None + ) + + else: + value = utils.parse_conf_arg(conf, key) + config[key] = int(value) if value else value + + return config diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lldp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lldp_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lldp_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lldp_interfaces/lldp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lldp_interfaces/lldp_interfaces.py new file mode 100644 index 00000000..57fbc425 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/lldp_interfaces/lldp_interfaces.py @@ -0,0 +1,109 @@ +# +# -*- 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 iosxr 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.iosxr.plugins.module_utils.network.iosxr.argspec.lldp_interfaces.lldp_interfaces import ( + Lldp_interfacesArgs, +) + + +class Lldp_interfacesFacts(object): + """ The iosxr lldp_interfaces fact class + """ + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Lldp_interfacesArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for lldp_interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + + if not data: + data = connection.get_config(flags="interface") + interfaces = ("\n" + data).split("\ninterface ") + + objs = [] + for interface in interfaces: + obj = self.render_config(self.generated_spec, interface) + if obj: + objs.append(obj) + + ansible_facts["ansible_network_resources"].pop("lldp_interfaces", None) + 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"(GigabitEthernet|Bundle-Ether|TenGigE|FortyGigE|HundredGigE)(\S+)", + conf, + re.M, + ) + if match: + config["name"] = match.group(1) + match.group(2) + + for key in ["receive", "transmit"]: + config[key] = ( + False if ("{0} disable".format(key)) in conf else None + ) + + for x in ["ieee-nearest-bridge", "ieee-nearest-non-tmpr-bridge"]: + if x in conf: + config["destination"]["mac_address"] = x + + return utils.remove_empties(config) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospf_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospf_interfaces/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospf_interfaces/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospf_interfaces/ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 00000000..c21eaf0d --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,137 @@ +# -*- 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 iosxr 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 +import re +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.rm_templates.ospf_interfaces import ( + Ospf_interfacesTemplate, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.ospf_interfaces.ospf_interfaces import ( + Ospf_interfacesArgs, +) + + +class Ospf_interfacesFacts(object): + """ The iosxr 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(self, connection, flag): + cmd = "show running-config router " + flag + return connection.get(cmd) + + 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(connection, flag="ospf") + data += "\n" + self.get_ospf_interfaces(connection, flag="ospfv3") + end_flag, end_mark, count, v_read = 0, 0, 0, False + areas, config_commands = [], [] + area_str, process, curr_process = "", "", "" + data = data.splitlines() + + for line in data: + if ( + line.startswith("router") + and curr_process != "" + and curr_process != line + ): + end_mark, count, end_flag, area_str = 0, 0, 0, "" + if end_mark == 0 and count == 0 and line.startswith("router ospf"): + curr_process = line + process = re.sub("\n", "", line) + count += 1 + config_commands.append(process) + else: + if line.startswith(" area") or line.startswith(" vrf"): + area_str = process + re.sub("\n", "", line) + config_commands.append(area_str.replace(" ", " ")) + end_flag += 1 + elif line.startswith(" interface"): + ospf_int = area_str + re.sub("\n", "", line) + # default output format has more spaces with default identation + # reset the spaces with replace + config_commands.append(ospf_int.replace(" ", " ")) + v_read = True + elif v_read: + if "!" not in line: + command = ospf_int.replace(" ", " ") + re.sub( + "\n", "", line + ) + config_commands.append(command.replace(" ", " ")) + else: + v_read = False + elif end_flag > 0 and "!" not in line: + command = area_str + re.sub("\n", "", line) + config_commands.append(command.replace(" ", " ")) + elif "!" in line: + end_flag = 0 + end_mark += 1 + if end_mark == 3: + end_mark, count = 0, 0 + area_str = "" + else: + command = process + line + command.replace(" ", " ") + config_commands.append(re.sub("\n", "", command)) + areas.append(re.sub("\n", "", command)) + data = config_commands + + ospf_interfaces_parser = Ospf_interfacesTemplate(lines=data) + objs = list(ospf_interfaces_parser.parse().values()) + if objs: + for item in objs: + item["address_family"] = list(item["address_family"].values()) + for af in item["address_family"]: + if af.get("processes"): + af["processes"] = list(af["processes"].values()) + + ansible_facts["ansible_network_resources"].pop("ospf_interfaces", None) + + params = utils.remove_empties( + utils.validate_config(self.argument_spec, {"config": objs}) + ) + + facts["ospf_interfaces"] = params.get("config", []) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospfv2/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospfv2/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospfv2/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospfv2/ospfv2.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospfv2/ospfv2.py new file mode 100644 index 00000000..1da5ea8e --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospfv2/ospfv2.py @@ -0,0 +1,157 @@ +# +# -*- 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 iosxr snmp 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.cisco.iosxr.plugins.module_utils.network.iosxr.rm_templates.ospfv2 import ( + Ospfv2Template, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.ospfv2.ospfv2 import ( + Ospfv2Args, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import ( + NetworkTemplate, +) + + +class Ospfv2Facts(object): + """ The iosxr snmp 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("show running-config router ospf") + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + + if not data: + data = self.get_ospfv2_data(connection) + end_flag, end_mark, count, v_read = 0, 0, 0, False + areas, config_commands = [], [] + area_str, process, curr_process = "", "", "" + data = data.splitlines() + for line in data: + if ( + line.startswith("router ospf") + and curr_process != "" + and curr_process != line + ): + end_mark, count, end_flag, area_str = 0, 0, 0, "" + if end_mark == 0 and count == 0 and line.startswith("router ospf"): + curr_process = line + process = re.sub("\n", "", line) + count += 1 + config_commands.append(process) + else: + if line.startswith(" area") or line.startswith(" vrf"): + area_str = process + re.sub("\n", "", line) + config_commands.append(area_str.replace(" ", " ")) + end_flag += 1 + elif line.startswith(" virtual-link"): + virtual_str = area_str + re.sub("\n", "", line) + config_commands.append(virtual_str.replace(" ", " ")) + v_read = True + elif v_read: + if "!" not in line: + command = virtual_str.replace(" ", " ") + re.sub( + "\n", "", line + ) + config_commands.append(command.replace(" ", " ")) + else: + v_read = False + elif end_flag > 0 and "!" not in line: + command = area_str + re.sub("\n", "", line) + config_commands.append(command.replace(" ", " ")) + elif "!" in line: + end_flag = 0 + end_mark += 1 + if end_mark == 3: + end_mark, count = 0, 0 + area_str = "" + else: + command = process + line + command.replace(" ", " ") + config_commands.append(re.sub("\n", "", command)) + areas.append(re.sub("\n", "", command)) + data = config_commands + ipv4 = {"processes": []} + rmmod = NetworkTemplate(lines=data, 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 "ranges" in area: + area["ranges"] = sorted( + area["ranges"], key=lambda k, s="ranges": k[s] + ) + if "virtual_link" in area: + area["virtual_link"] = list( + area["virtual_link"].values() + ) + area["virtual_link"] = sorted( + area["virtual_link"], key=lambda k, sk="id": k[sk] + ) + 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/iosxr/plugins/module_utils/network/iosxr/facts/ospfv3/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospfv3/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospfv3/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospfv3/ospfv3.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospfv3/ospfv3.py new file mode 100644 index 00000000..8e54e3f4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/ospfv3/ospfv3.py @@ -0,0 +1,155 @@ +# -*- 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 + +from copy import deepcopy +import re + +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.rm_templates.ospfv3 import ( + Ospfv3Template, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.ospfv3.ospfv3 import ( + Ospfv3Args, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import ( + NetworkTemplate, +) + + +class Ospfv3Facts(object): + """ The iosxr snmp fact class + """ + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Ospfv3Args.argument_spec + + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_ospfv3_data(self, connection): + return connection.get("show running-config router ospfv3") + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + + if not data: + data = self.get_ospfv3_data(connection) + end_flag, end_mark, count, v_read = 0, 0, 0, False + areas, config_commands = [], [] + area_str, process, curr_process = "", "", "" + data = data.splitlines() + for line in data: + if ( + line.startswith("router ospfv3") + and curr_process != "" + and curr_process != line + ): + end_mark, count, end_flag, area_str = 0, 0, 0, "" + if ( + end_mark == 0 + and count == 0 + and line.startswith("router ospfv3") + ): + curr_process = line + process = re.sub("\n", "", line) + count += 1 + config_commands.append(process) + else: + if line.startswith(" area") or line.startswith(" vrf"): + area_str = process + re.sub("\n", "", line) + config_commands.append(area_str.replace(" ", " ")) + end_flag += 1 + elif line.startswith(" virtual-link"): + virtual_str = area_str + re.sub("\n", "", line) + config_commands.append(virtual_str.replace(" ", " ")) + v_read = True + elif v_read: + if "!" not in line: + command = virtual_str.replace(" ", " ") + re.sub( + "\n", "", line + ) + config_commands.append(command.replace(" ", " ")) + else: + v_read = False + elif end_flag > 0 and "!" not in line: + command = area_str + re.sub("\n", "", line) + config_commands.append(command.replace(" ", " ")) + elif "!" in line: + end_flag = 0 + end_mark += 1 + if end_mark == 3: + end_mark, count = 0, 0 + area_str = "" + else: + command = process + line + command.replace(" ", " ") + config_commands.append(re.sub("\n", "", command)) + areas.append(re.sub("\n", "", command)) + data = config_commands + ipv4 = {"processes": []} + rmmod = NetworkTemplate(lines=data, tmplt=Ospfv3Template()) + 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 "ranges" in area: + area["ranges"] = sorted( + area["ranges"], key=lambda k, s="ranges": k[s] + ) + if "virtual_link" in area: + area["virtual_link"] = list( + area["virtual_link"].values() + ) + area["virtual_link"] = sorted( + area["virtual_link"], key=lambda k, sk="id": k[sk] + ) + 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/iosxr/plugins/module_utils/network/iosxr/facts/static_routes/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/static_routes/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/static_routes/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/static_routes/static_routes.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/static_routes/static_routes.py new file mode 100644 index 00000000..7df0e8bc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/facts/static_routes/static_routes.py @@ -0,0 +1,191 @@ +# +# -*- 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 iosxr 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 + + +import re +from copy import deepcopy +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.static_routes.static_routes import ( + Static_routesArgs, +) + + +class Static_routesFacts(object): + """ The iosxr static_routes fact class + """ + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Static_routesArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_device_data(self, connection): + return connection.get_config(flags="router static") + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for static_routes + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + data = self.get_device_data(connection) + + objs = [] + + if "No such configuration" not in data: + for entry in re.compile(r"(\s) vrf").split(data): + obj = self.render_config(self.generated_spec, entry) + if obj: + objs.append(obj) + + ansible_facts["ansible_network_resources"].pop("static_routes", None) + facts = {} + + facts["static_routes"] = [] + params = utils.validate_config(self.argument_spec, {"config": objs}) + for cfg in params["config"]: + facts["static_routes"].append(utils.remove_empties(cfg)) + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def render_config(self, 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) + entry_list = conf.split(" address-family") + config["address_families"] = [] + + if "router static" not in entry_list[0]: + config["vrf"] = entry_list[0].replace("!", "").strip() + + for item in entry_list[1:]: + routes = [] + address_family = {"routes": []} + address_family["afi"], address_family["safi"] = self.parse_af(item) + + destinations = re.findall(r"((?:\S+)/(?:\d+)) (?:.*)", item, re.M) + for dest in set(destinations): + route = {"next_hops": []} + route["dest"] = dest + + regex = r"%s .+$" % dest + cfg = re.findall(regex, item, re.M) + + for route_entry in cfg: + exit_point = {} + exit_point["forward_router_address"] = self.parse_faddr( + route_entry + ) + exit_point["interface"] = self.parse_intf(route_entry) + exit_point["admin_distance"] = self.parse_admin_distance( + route_entry + ) + + for x in [ + "tag", + "tunnel-id", + "metric", + "description", + "track", + "vrflabel", + "dest_vrf", + ]: + exit_point[x.replace("-", "_")] = self.parse_attrib( + route_entry, x.replace("dest_vrf", "vrf") + ) + + route["next_hops"].append(exit_point) + + routes.append(route) + address_family["routes"] = sorted( + routes, key=lambda i: i["dest"] + ) + config["address_families"].append(address_family) + + return utils.remove_empties(config) + + def parse_af(self, item): + match = re.search(r"(?:\s*)(\w+)(?:\s*)(\w+)", item, re.M) + if match: + return match.group(1), match.group(2) + + def parse_faddr(self, item): + for x in item.split(" "): + if (":" in x or "." in x) and "/" not in x: + return x + + def parse_intf(self, item): + match = re.search(r" ((\w+)((?:\d)/(?:\d)/(?:\d)/(?:\d+)))", item) + if match: + return match.group(1) + + def parse_attrib(self, item, attrib): + match = re.search(r" %s (\S+)" % attrib, item) + if match: + val = match.group(1).strip("'") + if attrib in ["tunnel-id", "vrflabel", "tag", "metric"]: + val = int(val) + return val + + def parse_admin_distance(self, item): + split_item = item.split(" ") + for item in [ + "vrf", + "metric", + "tunnel-id", + "vrflabel", + "track", + "tag", + "description", + ]: + try: + del split_item[split_item.index(item) + 1] + del split_item[split_item.index(item)] + except ValueError: + continue + try: + return [ + i + for i in split_item + if "." not in i + and ":" not in i + and ord(i[0]) > 48 + and ord(i[0]) < 57 + ][0] + except IndexError: + return None diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/iosxr.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/iosxr.py new file mode 100644 index 00000000..771aec74 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/iosxr.py @@ -0,0 +1,671 @@ +# 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. +# +# Copyright (c) 2015 Peter Sprygada, <psprygada@ansible.com> +# Copyright (c) 2017 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 +import re +from difflib import Differ + +from ansible.module_utils._text import to_text, to_bytes +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 +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + NetconfConnection, +) + +try: + from ncclient.xml_ import to_xml + + HAS_NCCLIENT = True +except ImportError: + HAS_NCCLIENT = False + +try: + from lxml import etree + + HAS_XML = True +except ImportError: + HAS_XML = False + +_EDIT_OPS = frozenset(["merge", "create", "replace", "delete"]) + +BASE_1_0 = "{urn:ietf:params:xml:ns:netconf:base:1.0}" + +NS_DICT = { + "BASE_NSMAP": {"xc": "urn:ietf:params:xml:ns:netconf:base:1.0"}, + "BANNERS_NSMAP": { + None: "http://cisco.com/ns/yang/Cisco-IOS-XR-infra-infra-cfg" + }, + "INTERFACES_NSMAP": {None: "http://openconfig.net/yang/interfaces"}, + "INSTALL_NSMAP": { + None: "http://cisco.com/ns/yang/Cisco-IOS-XR-installmgr-admin-oper" + }, + "HOST-NAMES_NSMAP": { + None: "http://cisco.com/ns/yang/Cisco-IOS-XR-shellutil-cfg" + }, + "M:TYPE_NSMAP": {"idx": "urn:ietf:params:xml:ns:yang:iana-if-type"}, + "ETHERNET_NSMAP": {None: "http://openconfig.net/yang/interfaces/ethernet"}, + "CETHERNET_NSMAP": { + None: "http://cisco.com/ns/yang/Cisco-IOS-XR-drivers-media-eth-cfg" + }, + "INTERFACE-CONFIGURATIONS_NSMAP": { + None: "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg" + }, + "INFRA-STATISTICS_NSMAP": { + None: "http://cisco.com/ns/yang/Cisco-IOS-XR-infra-statsd-oper" + }, + "INTERFACE-PROPERTIES_NSMAP": { + None: "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-oper" + }, + "IP-DOMAIN_NSMAP": { + None: "http://cisco.com/ns/yang/Cisco-IOS-XR-ip-domain-cfg" + }, + "SYSLOG_NSMAP": { + None: "http://cisco.com/ns/yang/Cisco-IOS-XR-infra-syslog-cfg" + }, + "AAA_NSMAP": {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-aaa-lib-cfg"}, + "AAA_LOCALD_NSMAP": { + None: "http://cisco.com/ns/yang/Cisco-IOS-XR-aaa-locald-cfg" + }, +} + +iosxr_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" + ), + "timeout": dict(type="int"), + "transport": dict(type="str", default="cli", choices=["cli", "netconf"]), +} + +iosxr_argument_spec = { + "provider": dict( + type="dict", + options=iosxr_provider_spec, + removed_at_date="2022-06-01", + removed_from_collection="cisco.iosxr", + ) +} + +command_spec = { + "command": dict(), + "prompt": dict(default=None), + "answer": dict(default=None), +} + +CONFIG_MISPLACED_CHILDREN = [re.compile(r"^end-\s*(.+)$")] + +# Objects defined in Route-policy Language guide of IOS_XR. +# Reconfiguring these objects replace existing configurations. +# Hence these objects should be played direcly from candidate +# configurations +CONFIG_BLOCKS_FORCED_IN_DIFF = [ + {"start": re.compile(r"^route-policy"), "end": re.compile(r"end-policy$")}, + {"start": re.compile(r"^prefix-set"), "end": re.compile(r"end-set$")}, + {"start": re.compile(r"^as-path-set"), "end": re.compile(r"end-set$")}, + {"start": re.compile(r"^community-set"), "end": re.compile(r"end-set$")}, + {"start": re.compile(r"^rd-set"), "end": re.compile(r"end-set$")}, + { + "start": re.compile(r"^extcommunity-set"), + "end": re.compile(r"end-set$"), + }, +] + + +def get_provider_argspec(): + return iosxr_provider_spec + + +def get_connection(module): + if hasattr(module, "connection"): + return module.connection + + capabilities = get_capabilities(module) + network_api = capabilities.get("network_api") + if network_api == "cliconf": + module.connection = Connection(module._socket_path) + elif network_api == "netconf": + module.connection = NetconfConnection(module._socket_path) + else: + module.fail_json( + msg="Invalid connection type {0!s}".format(network_api) + ) + + return module.connection + + +def get_capabilities(module): + if hasattr(module, "capabilities"): + return module.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.capabilities = json.loads(capabilities) + + return module.capabilities + + +def build_xml_subtree(container_ele, xmap, param=None, opcode=None): + sub_root = container_ele + meta_subtree = list() + + for key, meta in xmap.items(): + candidates = meta.get("xpath", "").split("/") + if container_ele.tag == candidates[-2]: + parent = container_ele + elif sub_root.tag == candidates[-2]: + parent = sub_root + else: + parent = sub_root.find( + ".//" + + meta.get("xpath", "") + .split(sub_root.tag + "/", 1)[1] + .rsplit("/", 1)[0] + ) + + if ( + opcode in ("delete", "merge") + and meta.get("operation", "unknown") == "edit" + ) or meta.get("operation", None) is None: + + if meta.get("tag", False) is True: + if parent.tag == container_ele.tag: + if meta.get("ns", False) is True: + child = etree.Element( + candidates[-1], + nsmap=NS_DICT[key.upper() + "_NSMAP"], + ) + else: + child = etree.Element(candidates[-1]) + meta_subtree.append(child) + sub_root = child + else: + if meta.get("ns", False) is True: + child = etree.SubElement( + parent, + candidates[-1], + nsmap=NS_DICT[key.upper() + "_NSMAP"], + ) + else: + child = etree.SubElement(parent, candidates[-1]) + + if meta.get("attrib", None) is not None and opcode in ( + "delete", + "merge", + ): + child.set(BASE_1_0 + meta.get("attrib"), opcode) + + continue + + text = None + param_key = key.split(":") + if param_key[0] == "a": + if ( + param is not None + and param.get(param_key[1], None) is not None + ): + text = param.get(param_key[1]) + elif param_key[0] == "m": + if meta.get("value", None) is not None: + text = meta.get("value") + + if text: + if meta.get("ns", False) is True: + child = etree.SubElement( + parent, + candidates[-1], + nsmap=NS_DICT[key.upper() + "_NSMAP"], + ) + else: + child = etree.SubElement(parent, candidates[-1]) + child.text = text + + if meta.get("attrib", None) is not None and opcode in ( + "delete", + "merge", + ): + child.set(BASE_1_0 + meta.get("attrib"), opcode) + + if len(meta_subtree) > 1: + for item in meta_subtree: + container_ele.append(item) + + if sub_root == container_ele: + return None + else: + return sub_root + + +def build_xml(container, xmap=None, params=None, opcode=None): + """ + Builds netconf xml rpc document from meta-data + + Args: + container: the YANG container within the namespace + xmap: meta-data map to build xml tree + params: Input params that feed xml tree values + opcode: operation to be performed (merge, delete etc.) + + Example: + Module inputs: + banner_params = [{'banner':'motd', 'text':'Ansible banner example', 'state':'present'}] + + Meta-data definition: + bannermap = collections.OrderedDict() + bannermap.update([ + ('banner', {'xpath' : 'banners/banner', 'tag' : True, 'attrib' : "operation"}), + ('a:banner', {'xpath' : 'banner/banner-name'}), + ('a:text', {'xpath' : 'banner/banner-text', 'operation' : 'edit'}) + ]) + + Fields: + key: exact match to the key in arg_spec for a parameter + (prefixes --> a: value fetched from arg_spec, m: value fetched from meta-data) + xpath: xpath of the element (based on YANG model) + tag: True if no text on the element + attrib: attribute to be embedded in the element (e.g. xc:operation="merge") + operation: if edit --> includes the element in edit_config() query else ignores for get() queries + value: if key is prefixed with "m:", value is required in meta-data + + Output: + <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <banners xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-infra-infra-cfg"> + <banner xc:operation="merge"> + <banner-name>motd</banner-name> + <banner-text>Ansible banner example</banner-text> + </banner> + </banners> + </config> + :returns: xml rpc document as a string + """ + if opcode == "filter": + root = etree.Element("filter", type="subtree") + elif opcode in ("delete", "merge"): + root = etree.Element("config", nsmap=NS_DICT["BASE_NSMAP"]) + + container_ele = etree.SubElement( + root, container, nsmap=NS_DICT[container.upper() + "_NSMAP"] + ) + + if xmap is not None: + if params is None: + build_xml_subtree(container_ele, xmap, opcode=opcode) + else: + subtree_list = list() + for param in to_list(params): + subtree_ele = build_xml_subtree( + container_ele, xmap, param=param, opcode=opcode + ) + if subtree_ele is not None: + subtree_list.append(subtree_ele) + + for item in subtree_list: + container_ele.append(item) + + return etree.tostring(root, encoding="unicode") + + +def etree_find(root, node): + try: + root = etree.fromstring(to_bytes(root)) + except (ValueError, etree.XMLSyntaxError): + pass + + return root.find(".//%s" % node.strip()) + + +def etree_findall(root, node): + try: + root = etree.fromstring(to_bytes(root)) + except (ValueError, etree.XMLSyntaxError): + pass + + return root.findall(".//%s" % node.strip()) + + +def is_cliconf(module): + capabilities = get_capabilities(module) + return capabilities.get("network_api") == "cliconf" + + +def is_netconf(module): + capabilities = get_capabilities(module) + network_api = capabilities.get("network_api") + if network_api == "netconf": + if not HAS_NCCLIENT: + module.fail_json(msg="ncclient is not installed") + if not HAS_XML: + module.fail_json(msg="lxml is not installed") + return True + + return False + + +def get_config_diff(module, running=None, candidate=None): + conn = get_connection(module) + + if is_cliconf(module): + try: + response = conn.get("show commit changes diff") + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + return response + elif is_netconf(module): + if running and candidate: + # ignore rpc-reply root node and diff from data element onwards + running_data_ele = etree.fromstring( + to_bytes(running.strip()) + ).getchildren()[0] + candidate_data_ele = etree.fromstring( + to_bytes(candidate.strip()) + ).getchildren()[0] + + running_data = to_text(etree.tostring(running_data_ele)).strip() + candidate_data = to_text( + etree.tostring(candidate_data_ele) + ).strip() + if running_data != candidate_data: + d = Differ() + diff = list( + d.compare( + running_data.splitlines(), candidate_data.splitlines() + ) + ) + return "\n".join(diff).strip() + + return None + + +def discard_config(module): + conn = get_connection(module) + try: + if is_netconf(module): + conn.discard_changes(remove_ns=True) + else: + conn.discard_changes() + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + + +def commit_config( + module, + comment=None, + confirmed=False, + confirm_timeout=None, + persist=False, + check=False, + label=None, +): + conn = get_connection(module) + reply = None + try: + if is_netconf(module): + if check: + reply = conn.validate(remove_ns=True) + else: + reply = conn.commit( + confirmed=confirmed, + timeout=confirm_timeout, + persist=persist, + remove_ns=True, + ) + elif is_cliconf(module): + if check: + module.fail_json( + msg="Validate configuration is not supported with network_cli connection type" + ) + else: + reply = conn.commit(comment=comment, label=label) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + + return reply + + +def get_oper(module, filter=None): + conn = get_connection(module) + + if filter is not None: + try: + if is_netconf(module): + response = conn.get(filter=filter, remove_ns=True) + else: + response = conn.get(filter) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + else: + return None + + return to_bytes( + etree.tostring(response), errors="surrogate_then_replace" + ).strip() + + +def get_config(module, config_filter=None, source="running"): + conn = get_connection(module) + + # Note: Does not cache config in favour of latest config on every get operation. + try: + if is_netconf(module): + out = to_xml( + conn.get_config( + source=source, filter=config_filter, remove_ns=True + ) + ) + elif is_cliconf(module): + out = conn.get_config(source=source, flags=config_filter) + cfg = out.strip() + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + return cfg + + +def check_existing_commit_labels(conn, label): + out = conn.get( + command="show configuration history detail | include %s" % label + ) + label_exist = re.search(label, out, re.M) + if label_exist: + return True + else: + return False + + +def load_config( + module, + command_filter, + commit=False, + replace=False, + comment=None, + admin=False, + exclusive=False, + running=None, + nc_get_filter=None, + label=None, +): + + conn = get_connection(module) + + diff = None + if is_netconf(module): + # FIXME: check for platform behaviour and restore this + # conn.lock(target = 'candidate') + # conn.discard_changes() + + try: + for filter in to_list(command_filter): + conn.edit_config(config=filter, remove_ns=True) + + candidate = get_config( + module, source="candidate", config_filter=nc_get_filter + ) + diff = get_config_diff(module, running, candidate) + + if commit and diff: + commit_config(module) + else: + discard_config(module) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + finally: + # conn.unlock(target = 'candidate') + pass + + elif is_cliconf(module): + try: + if label: + old_label = check_existing_commit_labels(conn, label) + if old_label: + module.fail_json( + msg="commit label {%s} is already used for" + " an earlier commit, please choose a different label" + " and rerun task" % label + ) + + response = conn.edit_config( + candidate=command_filter, + commit=commit, + admin=admin, + exclusive=exclusive, + replace=replace, + comment=comment, + label=label, + ) + if module._diff: + diff = response.get("diff") + + # Overwrite the default diff by the IOS XR commit diff. + # See plugins/cliconf/iosxr.py for this key set: show_commit_config_diff + diff = response.get("show_commit_config_diff") + + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + + return diff + + +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 copy_file(module, src, dst, proto="scp"): + conn = get_connection(module) + try: + conn.copy_file(source=src, destination=dst, proto=proto) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + + +def get_file(module, src, dst, proto="scp"): + conn = get_connection(module) + try: + conn.get_file(source=src, destination=dst, proto=proto) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + + +# A list of commands like {end-set, end-policy, ...} are part of configuration +# block like { prefix-set, as-path-set , ... } but they are not indented properly +# to be included with their parent. sanitize_config will add indentation to +# end-* commands so they are included with their parents +def sanitize_config(config, force_diff_prefix=None): + conf_lines = config.split("\n") + for regex in CONFIG_MISPLACED_CHILDREN: + for index, line in enumerate(conf_lines): + m = regex.search(line) + if m and m.group(0): + if force_diff_prefix: + conf_lines[index] = " " + m.group(0) + force_diff_prefix + else: + conf_lines[index] = " " + m.group(0) + conf = ("\n").join(conf_lines) + return conf + + +def mask_config_blocks_from_diff(config, candidate, force_diff_prefix): + conf_lines = config.split("\n") + candidate_lines = candidate.split("\n") + + for regex in CONFIG_BLOCKS_FORCED_IN_DIFF: + block_index_start_end = [] + for index, line in enumerate(candidate_lines): + startre = regex["start"].search(line) + if startre and startre.group(0): + start_index = index + else: + endre = regex["end"].search(line) + if endre and endre.group(0): + end_index = index + new_block = True + for prev_start, prev_end in block_index_start_end: + if start_index == prev_start: + # This might be end-set of another regex + # otherwise we would be having new start + new_block = False + break + if new_block and start_index: + block_index_start_end.append((start_index, end_index)) + + for start, end in block_index_start_end: + diff = False + if candidate_lines[start] in conf_lines: + run_conf_start_index = conf_lines.index(candidate_lines[start]) + else: + diff = False + continue + for i in range(start, end + 1): + if conf_lines[run_conf_start_index] == candidate_lines[i]: + run_conf_start_index = run_conf_start_index + 1 + else: + diff = True + break + if diff: + run_conf_start_index = conf_lines.index(candidate_lines[start]) + for i in range(start, end + 1): + conf_lines[run_conf_start_index] = ( + conf_lines[run_conf_start_index] + force_diff_prefix + ) + run_conf_start_index = run_conf_start_index + 1 + + conf = ("\n").join(conf_lines) + return conf diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/bgp/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/bgp/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/bgp/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/bgp/address_family.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/bgp/address_family.py new file mode 100644 index 00000000..fd9c9bf2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/bgp/address_family.py @@ -0,0 +1,129 @@ +# +# (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.iosxr.plugins.module_utils.network.iosxr.providers.providers import ( + CliProvider, +) + + +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 %s" % (item["afi"], 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") + + safe_list.append(context) + + 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_networks(self, item, config=None): + commands = list() + safe_list = list() + + for entry in item["networks"]: + network = entry["prefix"] + if entry["masklen"]: + network = "%s/%s" % (entry["prefix"], entry["masklen"]) + safe_list.append(network) + + cmd = "network %s" % network + + if entry["route_map"]: + cmd += " route-policy %s" % entry["route_map"] + + if not config or cmd not in config: + commands.append(cmd) + + if config and self.params["operation"] == "replace": + matches = re.findall(r"network (\S+)", 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", + "eigrp", + "isis", + "ospfv3", + ): + cmd += " %s" % entry["id"] + option += " %s" % entry["id"] + + if entry["metric"]: + cmd += " metric %s" % entry["metric"] + + if entry["route_map"]: + cmd += " route-policy %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 diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/bgp/neighbors.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/bgp/neighbors.py new file mode 100644 index 00000000..1ea870f7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/bgp/neighbors.py @@ -0,0 +1,135 @@ +# +# (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 +import socket + +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.iosxr.plugins.module_utils.network.iosxr.providers.providers import ( + CliProvider, +) + + +class Neighbors(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.neighbors"): + context_commands = list() + + neighbor = item["neighbor"] + + try: + socket.inet_aton(neighbor) + context = "neighbor %s" % neighbor + except socket.error: + context = "neighbor-group %s" % neighbor + + 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") + + safe_list.append(context) + + 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_remote_as(self, item, config=None): + cmd = "remote-as %s" % item["remote_as"] + if not config or cmd not in config: + return cmd + + def _render_description(self, item, config=None): + cmd = "description %s" % item["description"] + if not config or cmd not in config: + return cmd + + def _render_enabled(self, item, config=None): + cmd = "shutdown" + if item["enabled"] is True: + cmd = "no %s" % cmd + if not config or cmd not in config: + return cmd + + def _render_update_source(self, item, config=None): + cmd = "update-source %s" % item["update_source"].replace(" ", "") + if not config or cmd not in config: + return cmd + + def _render_password(self, item, config=None): + cmd = "password %s" % item["password"] + if not config or cmd not in config: + return cmd + + def _render_ebgp_multihop(self, item, config=None): + cmd = "ebgp-multihop %s" % item["ebgp_multihop"] + if not config or cmd not in config: + return cmd + + def _render_tcp_mss(self, item, config=None): + cmd = "tcp mss %s" % item["tcp_mss"] + if not config or cmd not in config: + return cmd + + def _render_advertisement_interval(self, item, config=None): + cmd = "advertisement-interval %s" % item["advertisement_interval"] + if not config or cmd not in config: + return cmd + + def _render_neighbor_group(self, item, config=None): + cmd = "use neighbor-group %s" % item["neighbor_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"] + + if keepalive and holdtime: + cmd = "timers %s %s" % (keepalive, holdtime) + if min_neighbor_holdtime: + cmd += " %s" % min_neighbor_holdtime + if not config or cmd not in config: + return cmd + else: + raise ValueError( + "required both options for timers: keepalive and holdtime" + ) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/bgp/process.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/bgp/process.py new file mode 100644 index 00000000..8b0aec3a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/cli/config/bgp/process.py @@ -0,0 +1,126 @@ +# +# (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.iosxr.plugins.module_utils.network.iosxr.providers.providers import ( + register_provider, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.providers.providers import ( + CliProvider, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.providers.cli.config.bgp.neighbors import ( + Neighbors, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.providers.cli.config.bgp.address_family import ( + AddressFamily, +) + +REDISTRIBUTE_PROTOCOLS = frozenset( + [ + "ospf", + "ospfv3", + "eigrp", + "isis", + "static", + "connected", + "lisp", + "mobile", + "rip", + "subscriber", + ] +) + + +@register_provider("iosxr", "iosxr_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: + if operation == "replace": + if existing_as and int(existing_as) != self.get_value( + "config.bgp_as" + ): + # The negate command has to be committed before new BGP AS is used. + self.connection.edit_config( + "no router bgp %s" % existing_as + ) + config = None + + elif operation == "override": + if existing_as: + # The negate command has to be committed before new BGP AS is used. + self.connection.edit_config( + "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 "%s detail" % cmd + elif log_neighbor_changes is False: + if config and cmd in config: + return "%s disable" % cmd + + 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) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/module.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/module.py new file mode 100644 index 00000000..4786b80c --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/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.iosxr.plugins.module_utils.network.iosxr.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/iosxr/plugins/module_utils/network/iosxr/providers/providers.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/providers/providers.py new file mode 100644 index 00000000..485eb383 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/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/iosxr/plugins/module_utils/network/iosxr/rm_templates/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/acl_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/acl_interfaces.py new file mode 100644 index 00000000..7f07a123 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/acl_interfaces.py @@ -0,0 +1,61 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import ( + NetworkTemplate, +) + + +class Acl_interfacesTemplate(NetworkTemplate): + def __init__(self, lines=None): + super(Acl_interfacesTemplate, self).__init__(lines=lines, tmplt=self) + + # fmt: off + PARSERS = [ + { + 'name': 'interface', + 'getval': re.compile(r''' + ^interface + \s(preconfigure)*\s* + (?P<name>\S+)$''', re.VERBOSE), + 'setval': 'interface {{ name }}', + 'result': { + '{{ name }}': { + 'name': '{{ name }}', + 'access_groups': {}, + }, + }, + 'shared': True + }, + { + "name": "access_groups", + "getval": re.compile( + r""" + \s+(?P<afi>ipv4|ipv6) + \saccess-group\s(?P<acl_name>\S+) + \s(?P<direction>\S+)$ + """, + re.VERBOSE, + ), + "setval": "{{ afi }} access-group {{ name }} {{ 'egress' if direction == 'out' else 'ingress' }}", + "result": { + "{{ name }}": { + "access_groups": { + "{{ afi }}": { + "afi": "{{ afi }}", + "acls": [ + { + "name": "{{ acl_name }}", + "direction": "{{ 'in' if direction == 'ingress' else 'out' }}", + }, + ], + } + } + } + } + }, + ] + # fmt: on diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/ospf_interfaces.py new file mode 100644 index 00000000..3cd153d7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/ospf_interfaces.py @@ -0,0 +1,1370 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +import re +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import ( + NetworkTemplate, +) + + +def get_ospf_type(afi): + return "ospf" if afi == "ipv4" else "ospfv3" + + +def get_interface_type(name): + return ( + "GigabitEthernet" if name.startswith("GigabitEthernet") else "Loopback" + ) + + +def _compute_command(cfg): + ospf_type = get_ospf_type(cfg["address_family"]["afi"]) + type = get_interface_type(cfg["name"]) + area = cfg["address_family"]["processes"]["area"] + pid = cfg["address_family"]["processes"]["process_id"] + cmd = "router {0} {1} area {2} interface {3} {4}".format( + ospf_type, pid, area["area_id"], type, cfg["name"].split(type)[1] + ) + return cmd + + +def _tmplt_ospf_int_delete(config_data): + ospf_type = get_ospf_type(config_data["afi"]) + type = get_interface_type(config_data["name"]) + area = config_data["area"] + command = "router {0} {1} area {2} interface {3} {4}".format( + ospf_type, + config_data["process"], + area["area_id"], + type, + config_data["name"].split(type)[1], + ) + return command + + +def _tmplt_ospf_config(config_data): + command = _compute_command(config_data) + return command + + +def _tmplt_ospf_authentication_md_config(config_data): + command = _compute_command(config_data) + auth = config_data["address_family"]["authentication"] + if auth["message_digest"].get("keychain"): + command += ( + " authentication message-digest keychain " + + auth["message_digest"]["keychain"] + ) + return command + + +def _tmplt_ospf_authentication_md_set(config_data): + command = _compute_command(config_data) + auth = config_data["address_family"]["authentication"] + if auth.get("message_digest") and auth["message_digest"].get("keychain"): + command += " authentication message-digest" + elif auth.get("null_auth"): + command += " authentication null" + return command + + +def _tmplt_ospf_authentication_key(config_data): + command = _compute_command(config_data) + auth = config_data["address_family"]["authentication_key"] + if auth.get("password"): + command += " authentication-key " + auth["password"] + elif auth.get("encrypted"): + command += " authentication-key encrypted " + auth["encrypted"] + elif auth.get("clear"): + command += " authentication-key clear " + auth["clear"] + return command + + +def _tmplt_ospf_int_bfd_min_int(config_data): + command = _compute_command(config_data) + bfd = config_data["address_family"]["bfd"] + if bfd.get("minimum_interval"): + command += " bfd minimum-interval " + str(bfd["minimum_interval"]) + return command + + +def _tmplt_ospf_int_bfd_mult(config_data): + command = _compute_command(config_data) + bfd = config_data["address_family"]["bfd"] + if bfd.get("multiplier"): + command += " bfd multiplier " + str(bfd["multiplier"]) + return command + + +def _tmplt_ospf_int_bfd_fd(config_data): + command = _compute_command(config_data) + bfd = config_data["address_family"]["bfd"] + if bfd.get("fast_detect") and bfd["fast_detect"].get("set"): + command += " bfd fast-detect" + elif bfd.get("fast_detect") and bfd["fast_detect"].get("disable"): + command += " bfd fast-detect disable" + elif bfd.get("fast_detect") and bfd["fast_detect"].get("strict_mode"): + command += " bfd fast-detect strict-mode" + return command + + +def _tmplt_ospf_cost_config(config_data): + command = _compute_command(config_data) + command += " cost " + str(config_data["address_family"]["cost"]) + return command + + +def _tmplt_ospf_cost_fallback_config(config_data): + command = _compute_command(config_data) + fallback = config_data["address_family"]["cost_fallback"] + command += ( + " cost-fallback " + + str(fallback["cost"]) + + " threshold " + + str(fallback["threshold"]) + ) + return command + + +def _tmplt_ospf_dead_int_config(config_data): + command = _compute_command(config_data) + command += " dead-interval " + str( + config_data["address_family"]["dead_interval"] + ) + return command + + +def _tmplt_ospf_demand_config(config_data): + command = _compute_command(config_data) + if config_data["address_family"]["demand_circuit"]: + command += " demand-circuit enable" + else: + command += " demand-circuit disable" + return command + + +class Ospf_interfacesTemplate(NetworkTemplate): + def __init__(self, lines=None): + super(Ospf_interfacesTemplate, self).__init__(lines=lines, tmplt=self) + + # fmt: off + PARSERS = [ + { + "name": "name", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + $''', + re.VERBOSE), + "remval": _tmplt_ospf_int_delete, + "setval": _tmplt_ospf_config, + "compval": "name", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + }, + }, + }, + "shared": True, + }, + { + "name": "authentication.message_digest", + "getval": re.compile( + r""" + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sauthentication(?P<authentication>) + \s(?P<opt>message-digest) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_authentication_md_set, + "compval": "address_family.authentication.message_digest", + "result": { + "{{ name }}": { + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "authentication": { + "set": "{{ True if authentication is defined and opt is undefined }}", + "message_digest": { + "set": "{{ True if opt == 'message-digest' else None }}", + }, + } + }, + } + } + } + }, + { + "name": "authentication.message_digest.keychain", + "getval": re.compile( + r""" + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sauthentication(?P<authentication>) + \s(?P<message_digest>message-digest) + \skeychain\s(?P<keychain>\S+)$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_authentication_md_config, + "compval": "address_family.authentication.message_digest.keychain", + "result": { + "{{ name }}": { + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "authentication": { + "message_digest": { + "keychain": "{{ keychain }}", + }, + } + }, + } + } + } + }, + { + "name": "authentication.null_auth", + "getval": re.compile( + r""" + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sauthentication(?P<authentication>) + \s(?P<opt>null) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_authentication_md_set, + "compval": "address_family.authentication.null_auth", + "result": { + "{{ name }}": { + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "authentication": { + "set": "{{ True if authentication is defined and opt is undefined }}", + "null_auth": "{{ True if opt == 'null' else None }}", + } + }, + } + } + } + }, + { + "name": "authentication_key", + "getval": re.compile( + r""" + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sauthentication-key + (\sencrypted\s(?P<encrypted>\S+))? + (\s(?P<key>\S+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_authentication_key, + "compval": "address_family.authentication_key", + "result": { + "{{ name }}": { + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "authentication_key": { + "encrypted": "{{ encrypted }}", + } + }, + } + } + } + }, + { + "name": "bfd.minimum_interval", + "getval": re.compile( + r""" + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sbfd(?P<bfd>) + \sminimum-interval\s(?P<minimum_interval>\d+) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_bfd_min_int, + "compval": "address_family.bfd.minimum_interval", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + + } + }, + "bfd": { + "minimum_interval": "{{ minimum_interval|int }}", + }, + }, + }, + }, + } + }, + { + "name": "bfd.multiplier", + "getval": re.compile( + r""" + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sbfd(?P<bfd>) + \smultiplier\s(?P<multiplier>\d+) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_bfd_mult, + "compval": "address_family.bfd.multiplier", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + + } + }, + "bfd": { + "multiplier": "{{ multiplier|int }}", + }, + }, + }, + }, + } + }, + { + "name": "bfd.fast_detect.set", + "getval": re.compile( + r""" + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sbfd(?P<bfd>) + \sfast-detect(?P<fast_detect>) + (\s(?P<opt>(disable|strict-mode)))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_bfd_fd, + "compval": "address_family.bfd.fast_detect.set", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + + } + }, + "bfd": { + "fast_detect": { + "set": "{{ True if opt != 'disable' and opt != 'strict-mode' and fast_detect is defined else None }}", + }, + }, + }, + }, + }, + } + }, + { + "name": "bfd.fast_detect.disable", + "getval": re.compile( + r""" + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sbfd(?P<bfd>) + \sfast-detect(?P<fast_detect>) + (\s(?P<opt>(disable|strict-mode)))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_bfd_fd, + "compval": "address_family.bfd.fast_detect.disable", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + + } + }, + "bfd": { + "fast_detect": { + "disable": "{{ True if opt == 'disable' else None }}" + }, + }, + }, + }, + }, + }, + }, + { + "name": "bfd.fast_detect.strict_mode", + "getval": re.compile( + r""" + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sbfd(?P<bfd>) + \sfast-detect(?P<fast_detect>) + \s(?P<opt>strict-mode) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_int_bfd_fd, + "compval": "address_family.bfd.fast_detect.strict_mode", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "bfd": { + "fast_detect": { + "strict_mode": "{{ True if opt == 'strict-mode' else None }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "cost", + "getval": re.compile( + r""" + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \scost\s(?P<cost>\S+)$""", + re.VERBOSE), + "setval": _tmplt_ospf_cost_config, + "compval": "address_family.cost", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "cost": "{{ cost }}" + }, + }, + }, + } + }, + { + "name": "cost_fallback", + "getval": re.compile( + r""" + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \scost-fallback\s(?P<cost>\S+) + \sthreshold\s(?P<threshold>\S+) + $""", + re.VERBOSE), + "setval": _tmplt_ospf_cost_fallback_config, + "compval": "address_family.cost_fallback", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "cost_fallback": { + "cost": "{{ cost }}", + "threshold": "{{ threshold }}" + } + }, + }, + }, + } + }, + { + "name": "dead_interval", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sdead-interval\s(?P<dead_interval>\S+) + $''', + re.VERBOSE), + "setval": _tmplt_ospf_dead_int_config, + "compval": "address_family.dead_interval", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "dead_interval": "{{ dead_interval }}" + }, + }, + }, + } + }, + { + "name": "demand_circuit", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sdemand-circuit\s(?P<demand_circuit>\S+) + $''', + re.VERBOSE), + "setval": _tmplt_ospf_demand_config, + "compval": "address_family.demand_circuit", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "demand_circuit": "{{ True if demand_circuit == 'enable' else False if demand_circuit == 'disable' else None }}" + }, + }, + }, + } + }, + { + "name": "flood_reduction", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sflood-reduction\s(?P<flood_reduction>\S+) + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "flood-reduction {{ 'enable' if flood_reduction == True else 'disable' }}", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "flood_reduction": "{{ True if flood_reduction == 'enable' else False if flood_reduction == 'disable' else None }}" + }, + }, + }, + } + }, + { + "name": "hello_interval", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \shello-interval\s(?P<dead_interval>\S+) + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "hello-interval {{ hello_interval }}", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "hello_interval": "{{ hello_interval }}" + }, + }, + }, + } + }, + { + "name": "link_down.set", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \s(?P<link_down>link-down) + (\s(?P<disable>disable))? + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "link-down", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "link_down": { + "set": "{{ True if link_down is defined and disable is undefined else None}}" + } + }, + }, + }, + } + }, + { + "name": "link_down.disable", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \s(?P<link_down>link-down) + \s(?P<disable>disable) + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "link-down disable", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "link_down": { + "disable": "{{ True if disable is defined else None }}" + } + }, + }, + }, + } + }, + { + "name": "message_digest_key", + "getval": re.compile( + r""" + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \smessage-digest-key + \s(?P<id>\d+) + \smd5 + \s(?P<encryption>\d) + \s(?P<key>\S+)$""", + re.VERBOSE, + ), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface " + "{{ type }} {{ name }} #message-digest-key {{ message_digest_key.id }} " + "md5 encrypted {{ message_digest_key.encrypted}}", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "message_digest_key": { + "id": "{{ id }}", + "encrypted": "{{ encryption }}", + } + }, + } + } + } + }, + { + "name": "mpls.set_ldp", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \s(?P<mpls>mpls) + \s(?P<ldp>set_ldp) + (\s(?P<sync>sync))? + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "mpls ldp", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "mpls": { + "set_ldp": "{{ True if set_ldp is defined and sync is undefined else None}}" + } + }, + }, + }, + } + }, + { + "name": "mpls.ldp_sync", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \s(?P<mpls>mpls) + \s(?P<ldp>ldp) + \s(?P<sync>sync) + (\s(?P<disable>disable))? + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "mpls ldp sync", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "mpls": { + "ldp_sync": "{{ True if sync is defined and disable is undefined else None}}" + } + }, + }, + }, + } + }, + { + "name": "mpls.ldp_sync_disable", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \s(?P<mpls>mpls) + \s(?P<ldp>ldp) + \s(?P<sync>sync) + \s(?P<disable>disable) + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "mpls ldp sync disable", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "mpls": { + "ldp_sync": "{{ False if disable is defined }}" + } + }, + }, + }, + } + }, + { + "name": "mtu_ignore", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \smtu-ignore\s(?P<mtu_ignore>\S+) + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "mtu_ignore {{ 'enable' if mtu_ignore == 'True' else 'disable' if mtu_ignore == 'False' }}", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "mtu_ignore": "{{ True if mtu_ignore == 'enable' else False if mtu_ignore == 'disable' else None }}" + }, + }, + }, + } + }, + { + "name": "network", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \snetwork\s(?P<network>\S+) + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "network {{ network }}", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "network": "{{ network }}" + }, + }, + }, + } + }, + { + "name": "packet_size", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \spacket-size\s(?P<packet_size>\S+) + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "packet-size {{ packet_size }}", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "packet_size": "{{ packet_size }}" + }, + }, + }, + } + }, + { + "name": "passive", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \spassive\s(?P<passive>\S+) + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "passive {{ 'enable' if passive == 'True' else 'disable' if passive == 'False' }}", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "passive": "{{ True if passive == 'enable' else False if passive == 'disable' else None }}" + }, + }, + }, + } + }, + { + "name": "prefix_suppression.disable", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sprefix-suppression\s(?P<prefix_suppression>\S+) + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "prefix-suppression {{ prefix_suppression }}", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "prefix_suppression": { + "disable": "{{ True if prefix_suppression == 'disable' else None }}" + } + }, + }, + }, + } + }, + { + "name": "prefix_suppression.secondary_address", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \s(?P<prefix_suppression>prefix-suppression) + \s(?P<secondary_address>secondary-address) + (\s(?P<disable>disable))? + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "prefix-suppression secondary-address {{ disable if secondary_address is False }}", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "prefix_suppression": { + "secondary_address": "{{ True if secondary_address is defined and " + "disable is undefined else False if disable is defined else None }}" + }, + }, + }, + }, + } + }, + { + "name": "priority", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \spriority\s(?P<priority>\d+) + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "priority {{ priority }}", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "priority": "{{ priority|int }}" + }, + }, + }, + } + }, + { + "name": "retransmit_interval", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \sretransmit-interval\s(?P<retransmit_interval>\d+) + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "retransmit-interval {{ retransmit_interval }}", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "retransmit_interval": "{{ retransmit_interval|int }}" + }, + }, + }, + } + }, + { + "name": "security.ttl_hops", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \s(?P<security>security) + \s(?P<ttl>ttl) + \shops\s(?P<hops>\d+) + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "security ttl hops {{ hops }}", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "security_ttl": { + "hops": "{{ hops|int }}" + }, + }, + }, + }, + } + }, + { + "name": "security.ttl", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \s(?P<security>security) + \s(?P<ttl>ttl) + (\s(?P<hops>hops))? + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "security ttl", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "security_ttl": { + "set": "{{ True if ttl is defined and hops is undefined }}" + }, + }, + }, + }, + } + }, + { + "name": "transmit_delay", + "getval": re.compile(r''' + ^router + \s(?P<ospf_type>ospf|ospfv3) + \s(?P<process_id>\S+) + \sarea\s(?P<area_id>\S+) + \sinterface\s(?P<name>\S+) + \stransmit-delay\s(?P<transmit_delay>\d+) + $''', + re.VERBOSE), + "setval": "router {{ ospf_type }} {{ process_id }} area {{ area_id }} interface {{ type }} {{ name }} " + "transmit-delay {{ transmit_delay }}", + "result": { + "{{ name }}": { + "name": "{{ name }}", + "type": "{{ 'gigabitethernet' if 'GigabitEthernet' in name else 'loopback' if 'Loopback' in name }}", + "address_family": { + "{{ ospf_type }}": { + "afi": "{{ 'ipv4' if ospf_type == 'ospf' else 'ipv6' }}", + "processes": { + "{{ process_id }}": { + "process_id": "{{ process_id }}", + "area": { + "area_id": "{{ area_id }}", + }, + } + }, + "transmit_delay": "{{ transmit_delay|int }}" + }, + }, + }, + } + }, + ] + # fmt: on diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/ospfv2.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/ospfv2.py new file mode 100644 index 00000000..83527d03 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/ospfv2.py @@ -0,0 +1,2876 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +import re +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import ( + NetworkTemplate, +) +from ansible.module_utils.six import iteritems + + +def _tmplt_ospf_default_information(config_data): + if "default_information_originate" in config_data: + command = "default-information originate" + if "always" in config_data["default_information_originate"]: + command += " always" + if "metric" in config_data["default_information_originate"]: + command += " metric {metric}".format( + **config_data["default_information_originate"] + ) + if "metric_type" in config_data["default_information_originate"]: + command += " metric-type {metric_type}".format( + **config_data["default_information_originate"] + ) + if "route_policy" in config_data["default_information_originate"]: + command += " route-policy {route_policy}".format( + **config_data["default_information_originate"] + ) + return command + + +def _tmplt_ospf_auto_cost(config_data): + if "auto_cost" in config_data: + command = "auto-cost" + if "disable" in config_data["auto_cost"]: + command += " disable" + if "reference_bandwidth" in config_data["auto_cost"]: + command += " reference-bandwidth {reference_bandwidth}".format( + **config_data["auto_cost"] + ) + return command + + +def _tmplt_ospf_bfd(config_data): + if "bfd" in config_data: + command = "bfd" + if "minimum_interval" in config_data["bfd"]: + command += " minimum-interval {minimum_interval}".format( + **config_data["bfd"] + ) + + if "multiplier" in config_data["bfd"]: + command += " multiplier {multiplier}".format(**config_data["bfd"]) + + return command + + +def _tmplt_ospf_security(config_data): + if "security_ttl" in config_data: + command = "security_ttl" + if "set" in config_data["security_ttl"]: + command += " ttl" + elif config_data["security_ttl"].get("hops"): + command += " ttl hops {0}".format( + config_data["security_ttl"].get("hops") + ) + return command + + +def _tmplt_ospf_log_adjacency(config_data): + if "log_adjacency_changes" in config_data: + command = "log adjacency" + if "set" in config_data["log_adjacency_changes"]: + command += " changes" + elif config_data["log_adjacency_changes"].get("disable"): + command += " disable" + elif config_data["log_adjacency_changes"].get("details"): + command += " details" + return command + + +def _tmplt_ospf_log_max_lsa(config_data): + if "max_lsa" in config_data: + command = "max-lsa" + if "threshold" in config_data["max_lsa"]: + command += " {0}".format(config_data["max_lsa"].get("threshold")) + if "warning_only" in config_data["max_lsa"]: + command += " warning-only {0}".format( + config_data["max_lsa"].get("warning_only") + ) + if "ignore_time" in config_data["max_lsa"]: + command += " ignore-time {0}".format( + config_data["max_lsa"].get("ignore_time") + ) + if "ignore_count" in config_data["max_lsa"]: + command += " ignore-count {0}".format( + config_data["max_lsa"].get("ignore_count") + ) + if "reset_time" in config_data["max_lsa"]: + command += " reset-time {0}".format( + config_data["max_lsa"].get("reset_time") + ) + 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_distance_admin(config_data): + if "admin_distance" in config_data: + command = "distance" + if config_data["admin_distance"].get("value"): + command += " {0}".format( + config_data["admin_distance"].get("value") + ) + if config_data["admin_distance"].get("source"): + command += " {0}".format( + config_data["admin_distance"].get("source") + ) + if config_data["admin_distance"].get("wildcard"): + command += " {0}".format( + config_data["admin_distance"].get("wildcard") + ) + if config_data["admin_distance"].get("access_list"): + command += " {0}".format( + config_data["admin_distance"].get("access_list") + ) + return command + + +def _tmplt_ospf_distance_ospf(config_data): + if "ospf_distance" in config_data: + command = "distance ospf" + if config_data["ospf_distance"].get("external"): + command += " external {0}".format( + config_data["ospf_distance"].get("external") + ) + if config_data["ospf_distance"].get("inter_area"): + command += " inter-area {0}".format( + config_data["ospf_distance"].get("inter_area") + ) + if config_data["ospf_distance"].get("intra_area"): + command += " intra-area {0}".format( + config_data["ospf_distance"].get("intra_area") + ) + return command + + +def _tmplt_ospf_nsr(config_data): + if "nsr" in config_data: + command = "nsr" + if "set" in config_data["nsr"]: + command += " nsr" + elif config_data["nsr"].get("disable"): + command += " nsr {0}".format("disable") + return command + + +def _tmplt_ospf_protocol(config_data): + if "protocol_shutdown" in config_data: + command = "protocol" + if "set" in config_data["protocol_shutdown"]: + command += " shutdown" + elif config_data["shutdown"].get("host_mode"): + command += " shutdown host-mode" + elif config_data["shutdown"].get("on_reload"): + command += " shutdown on-reload" + return command + + +def _tmplt_microloop_avoidance(config_data): + if "microloop_avoidance" in config_data: + command = "microloop avoidance" + if "protected" in config_data["microloop_avoidance"]: + command += " protected" + if "segment_routing" in config_data["microloop_avoidance"]: + command += " segment_routing" + if "rib_update_delay" in config_data["microloop_avoidance"]: + command += " rin-update-delay {0}".config_data[ + "microloop_avoidance" + ].get("rib_update_delay") + return command + + +def _tmplt_ospf_bfd_fast_detect(config_data): + if "bfd" in config_data: + command = "bfd" + if "fast_detect" in config_data["bfd"]: + fast_detect = config_data["bfd"].get("fast_detect") + command += " fast-detect" + if "strict_mode" in fast_detect: + command += " strict-mode" + return command + + +def _tmplt_ospf_mpls_traffic_eng(config_data): + if "traffic_eng" in config_data: + command = "mpls traffic-eng" + if "igp_intact" in config_data["traffic_eng"]: + command += " igp-intact" + if "ldp_sync_update" in config_data["traffic_eng"]: + command += " ldp_sync_update" + if "multicast_intact" in config_data["traffic_eng"]: + command += " multicast_intact" + if "auto_route_exclude" in config_data["traffic_eng"]: + policy = config_data["traffic_eng"].get("autoroute_exclude") + command += " autoroute-exlude route-policy {0}".format( + policy.get("route_policy") + ) + return command + + +def _tmplt_ospf_authentication_md(config_data): + command = [] + if "authentication" in config_data: + if config_data["authentication"].get("message_digest"): + command = "authentication message-digest" + md = config_data["authentication"].get("message_digest") + if md.get("keychain"): + command += " keychain " + md.get("keychain") + return command + + +def _tmplt_ospf_authentication(config_data): + command = [] + if "authentication" in config_data: + if config_data["authentication"].get("keychain"): + command = "authentication keychain " + config_data[ + "authentication" + ].get("keychain") + elif config_data["authentication"].get("no_auth"): + command = "authentication null" + return command + + +def _tmplt_ospf_adjacency_stagger(config_data): + if "adjacency_stagger" in config_data: + command = "adjacency stagger".format(**config_data) + if config_data["adjacency_stagger"].get( + "min_adjacency" + ) and config_data["adjacency_stagger"].get("min_adjacency"): + command += " {0} {1}".format( + config_data["adjacency_stagger"].get("min_adjacency"), + config_data["adjacency_stagger"].get("max_adjacency"), + ) + elif config_data["adjacency_stagger"].get("disable"): + command += " disable" + return command + + +def _tmplt_ospf_adjacency_distribute_bgp_state(config_data): + if "distribute_link_list" in config_data: + command = "distribute link-state" + if config_data["distribute_link_list"].get("instance_id"): + command += " instance-id {0}".format( + config_data["distribute_link_list"].get("instance_id") + ) + elif config_data["distribute_link_list"].get("throttle"): + command += " throttle {0}".format( + config_data["distribute_link_list"].get("throttle") + ) + return command + elif "distribute_bgp_ls" in config_data: + command = "distribute bgp-ls" + if config_data["distribute_bgp_ls"].get("instance_id"): + command += " instance-id {0}".format( + config_data["distribute_bgp_ls"].get("instance_id") + ) + elif config_data["distribute_bgp_ls"].get("throttle"): + command += " throttle {0}".format( + config_data["distribute_bgp_ls"].get("throttle") + ) + return command + + +def _tmplt_ospf_capability_opaque(config_data): + if "capability" in config_data: + if "opaque" in config_data["capability"]: + command = "capability opaque" + opaque = config_data["capability"].get("opaque") + if "disable" in opaque: + command += "capability opaque disable" + return command + + +def _tmplt_ospf_authentication_key(config_data): + if "authentication_key" in config_data: + command = "authentication-key".format(**config_data) + if config_data["authentication_key"].get("password"): + command += " {0}".format( + config_data["authentication_key"].get("password") + ) + 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("keychain"): + command += " keychain " + config_data["authentication"].get( + "keychain" + ) + elif config_data["authentication"].get("no_auth"): + command += " null" + return command + + +def _tmplt_ospf_area_authentication_md(config_data): + if "authentication" in config_data: + command = "area {area_id} authentication".format(**config_data) + if "message_digest" in config_data["authentication"]: + command = "authentication message-digest" + md = config_data["authentication"].get("message_digest") + if md.get("keychain"): + command += " keychain " + md.get("keychain") + return command + + +def _tmplt_ospf_area_authentication_key(config_data): + if "authentication_key" in config_data: + command = "area {area_id} authentication-key".format(**config_data) + if config_data["authentication_key"].get("password"): + command += " {0}".format( + config_data["authentication_key"].get("password") + ) + return command + + +def _tmplt_ospf_area_mpls_ldp(config_data): + commands = [] + if "mpls" in config_data: + command = "area {area_id} mpls".format(**config_data) + if config_data["mpls"].get("ldp"): + ldp = config_data["mpls"].get("ldp") + if "auto_config" in ldp: + command += " auto-config" + commands.append(command) + if "sync" in ldp: + command += " sync" + commands.append(command) + if "sync_igp_shortcuts" in ldp: + command += " sync-igp-shortcuts" + commands.append(command) + return commands + + +def _tmplt_ospf_area_bfd(config_data): + if "bfd" in config_data: + command = "area {area_id} bfd".format(**config_data) + if "minimum_interval" in config_data["bfd"]: + command += " minimum-interval {minimum_interval}".format( + **config_data["bfd"] + ) + + if "multiplier" in config_data["bfd"]: + command += " multiplier {multiplier}".format(**config_data["bfd"]) + + return command + + +def _tmplt_ospf_area_bfd_fast_detect(config_data): + if "bfd" in config_data: + command = "area {area_id} bfd".format(**config_data) + if "fast_detect" in config_data["bfd"]: + fast_detect = config_data["bfd"].get("fast_detect") + command += " fast-detect" + if "strict_mode" in fast_detect: + command += " strict-mode" + return command + + +def _tmplt_ospf_mpls_ldp(config_data): + commands = [] + if "mpls" in config_data: + command = "mpls".format(**config_data) + if config_data["mpls"].get("ldp"): + ldp = config_data["mpls"].get("ldp") + if "auto_config" in ldp: + command += " auto-config" + commands.append(command) + if "sync" in ldp: + command += " sync" + commands.append(command) + if "sync_igp_shortcuts" in ldp: + command += " sync-igp-shortcuts" + commands.append(command) + return commands + + +def _tmplt_ospf_area_nssa(config_data): + if "nssa" in config_data: + command = "area {area_id} nssa".format(**config_data) + 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_def_info_origin(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" + def_info_origin = config_data["nssa"].get( + "default_information_originate" + ) + if "metric" in def_info_origin: + command += " metric {metric}".format( + **config_data["nssa"]["default_information_originate"] + ) + if "metric_type" in def_info_origin: + command += " metric-type {metric_type}".format( + **config_data["nssa"]["default_information_originate"] + ) + return command + + +def _tmplt_ospf_area_nssa_translate(config_data): + if "nssa" in config_data: + command = "area {area_id} nssa".format(**config_data) + if config_data["nssa"].get("translate"): + command += " translate" + translate = config_data["nssa"].get("translate") + if "type7" in translate: + command += " type7" + if translate["type7"].get("always"): + command += " always" + return command + + +def _tmplt_ospf_area_vlink_authentication(config_data): + if "authentication" in config_data: + command = "area {area_id} virtual-link {id} authentication".format( + **config_data + ) + if config_data["authentication"].get("keychain"): + command += " keychain " + config_data["authentication"].get( + "keychain" + ) + elif config_data["authentication"].get("no_auth"): + command += " null" + return command + + +def _tmplt_ospf_area_vlink_authentication_md(config_data): + if "authentication" in config_data: + command = "area {area_id} virtual-link {id} authentication".format( + **config_data + ) + if config_data["authentication"].get("message_digest"): + command = "authentication message-digest" + md = config_data["authentication"].get("message_digest") + if md.get("keychain"): + command += " keychain " + md.get("keychain") + return command + + +def _tmplt_ospf_area_vlink_authentication_key(config_data): + if "authentication_key" in config_data: + command = "area {area_id} virtual-link {id} authentication-key".format( + **config_data + ) + if config_data["authentication_key"].get("password"): + command += " {0}".format( + config_data["authentication_key"].get("password") + ) + return command + + +def _tmplt_ospf_area_stub(config_data): + if "stub" in config_data: + command = "area {area_id} stub".format(**config_data) + if config_data["stub"].get("no_summary"): + command += " no-summary" + 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}".format(**v) + if "advertise" in v: + temp_cmd += " advertise" + elif "not_advertise" in v: + temp_cmd += " not-advertise" + cmd += temp_cmd + commands.append(cmd) + return commands + + +def _tmplt_prefix_suppression(config_data): + if "prefix_suppression" in config_data: + if "set" in config_data["prefix_suppression"]: + command = "prefix-suppression" + if "secondary_address" in config_data["prefix_suppression"]: + command = "prefix-suppression secondary-address" + return command + + +def _tmplt_protocol_shutdown(config_data): + if "protocol_shutdown" in config_data: + if "set" in config_data["protocol_shutdown"]: + command = "protocol-shutdown" + if "host_mode" in config_data["protocol_shutdown"]: + command = "protocol-shutdown host-mode" + if "on_reload" in config_data["protocol_shutdown"]: + command = "protocol-shutdown on-reload" + return command + + +def _tmplt_timers_lsa(config_data): + if "timers" in config_data: + command = "timers lsa" + if "group_pacing" in config_data["timers"]["lsa"]: + command += " group-pacing {group_pacing}".format( + **config_data["timers"]["lsa"] + ) + if "min_arrival" in config_data["timers"]["lsa"]: + command += " min-arrival {min_arrival}".format( + **config_data["timers"]["lsa"] + ) + if "refresh" in config_data["timers"]["lsa"]: + command += " refresh {refresh}".format( + **config_data["timers"]["lsa"] + ) + return command + + +def _tmplt_timers_graceful_shutdown(config_data): + if "timers" in config_data: + command = "timers graceful-shutdown" + if "initial_delay" in config_data["timers"]["graceful-shutdown"]: + command += " initial delay {initial_delay}".format( + **config_data["timers"]["graceful-shutdown"] + ) + if "retain_routes" in config_data["timers"]["graceful-shutdown"]: + command += " retain routes {retain_routes}".format( + **config_data["timers"]["graceful-shutdown"] + ) + return command + + +class Ospfv2Template(NetworkTemplate): + def __init__(self, lines=None): + super(Ospfv2Template, self).__init__(lines=lines, tmplt=self) + + # fmt: off + PARSERS = [ + { + "name": "pid", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + $""", + re.VERBOSE, + ), + "setval": "router ospf {{ process_id }}", + "result": { + "processes": {"{{ pid }}": {"process_id": "{{ pid }}"}} + }, + "shared": True, + }, + { + "name": "cost", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \scost(?P<cost>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "cost {{ cost }}", + "result": { + "processes": { + "{{ pid }}": { + "cost": "{{ cost|int }}", + } + } + }, + }, + { + "name": "default_metric", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sdefault-metric(?P<default_metric>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "default-metric {{ default_metric }}", + "result": { + "processes": { + "{{ pid }}": { + "default_metric": "{{ default_metric|int }}", + } + } + }, + }, + { + "name": "packet_size", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \spacket-size(?P<packet_size>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "packet-size {{ packet_size }}", + "result": { + "processes": { + "{{ pid }}": { + "packet_size": "{{ packet_size|int }}", + } + } + }, + }, + { + "name": "dead_interval", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sdead-interval(?P<dead_interval>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "dead-interval {{ dead_interval }}", + "result": { + "processes": { + "{{ pid }}": { + "dead_interval": "{{ dead_interval|int }}", + } + } + }, + }, + { + "name": "hello_interval", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \shello-interval(?P<hello_interval>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "hello-interval {{ hello_interval }}", + "result": { + "processes": { + "{{ pid }}": { + "hello_interval": "{{ hello_interval|int }}", + } + } + }, + }, + { + "name": "priority", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \spriority(?P<priority>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "priority {{ priority }}", + "result": { + "processes": { + "{{ pid }}": { + "priority": "{{ priority|int }}", + } + } + }, + }, + { + "name": "weight", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sweight(?P<weight>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "weight {{ weight }}", + "result": { + "processes": { + "{{ pid }}": { + "weight": "{{ weight|int }}", + } + } + }, + }, + { + "name": "retransmit_interval", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sretransmit-interval(?P<retransmit_interval>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "retransmit-interval {{ retransmit_interval }}", + "result": { + "processes": { + "{{ pid }}": { + "retransmit_interval": "{{ retransmit_interval|int }}", + } + } + }, + }, + { + "name": "transmit_delay", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \stransmit-delay(?P<transmit_delay>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "transmit-delay {{ transmit_delay }}", + "result": { + "processes": { + "{{ pid }}": { + "transmit_delay": "{{ transmit_delay|int }}", + } + } + }, + }, + { + "name": "passive", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \spassive\s(?P<passive>\S+) + $""", + re.VERBOSE, + ), + + "setval": "passive {{ passive }}", + "result": { + "processes": { + "{{ pid }}": { + "passive": "{{ passive }}", + } + } + }, + }, + { + "name": "process.database_filter", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sdatabase-filter + \sall + \sout\s(?P<database_filter>\s\S+) + $""", + re.VERBOSE, + ), + + "setval": "process.database_filter", + "result": { + "processes": { + "{{ pid }}": { + "database_filter": "{{ database_filter }}", + } + } + }, + }, + { + "name": "demand_circuit", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sdemand-circuit\s(?P<demand_circuit>\S+) + $""", + re.VERBOSE, + ), + + "setval": "demand-circuit {{ demand_circuit }}", + "result": { + "processes": { + "{{ pid }}": { + "demand_circuit": "{{ demand_circuit }}", + } + } + }, + }, + { + "name": "external_out", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sexternal-out\s(?P<external_out>\S+) + $""", + re.VERBOSE, + ), + + "setval": "external-out {{ external_out }}", + "result": { + "processes": { + "{{ pid }}": { + "external_out": "{{ external_out }}", + } + } + }, + }, + { + "name": "router_id", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \srouter-id\s(?P<router_id>\S+) + $""", + re.VERBOSE, + ), + + "setval": "router-id {{ router_id }}", + "result": { + "processes": { + "{{ pid }}": { + "router_id": "{{ router_id }}", + } + } + }, + }, + { + "name": "summary_in", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \ssummary-in\s(?P<summary_in>\S+) + $""", + re.VERBOSE, + ), + + "setval": "summary-in {{ summary_in }}", + "result": { + "processes": { + "{{ pid }}": { + "summary_in": "{{ summary_in }}", + } + } + }, + }, + + { + "name": "mtu_ignore", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \smtu-ignore\s(?P<mtu_ignore>\S+) + $""", + re.VERBOSE, + ), + + "setval": "mtu-ignore {{ mtu_ignore }}", + "result": { + "processes": { + "{{ pid }}": { + "mtu_ignore": "{{ mtu_ignore }}", + } + } + }, + }, + { + "name": "flood_reduction", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sflood-reduction\s(?P<flood_reduction>\S+) + $""", + re.VERBOSE, + ), + + "setval": "flood-reduction {{ flood_reduction }}", + "result": { + "processes": { + "{{ pid }}": { + "flood_reduction": "{{ flood_reduction }}", + } + } + }, + }, + { + "name": "loopback_stub_network", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sloopback(?P<loopback>) + \sstub-network\s(?P<stub_network>\S+) + $""", + re.VERBOSE, + ), + + "setval": "loopback stub-network {{ stub_network }}", + "result": { + "processes": { + "{{ pid }}": { + "loopback_stub_network": "{{ loopback_stub_network }}", + } + } + }, + }, + { + "name": "address_family_unicast", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \saddress-family(?P<address_family>) + \sipv4(?P<ipv4>) + \sunicast(?P<unicast>) + $""", + re.VERBOSE, + ), + + "setval": "address_family_unicast", + "result": { + "processes": { + "{{ pid }}": { + "address_family_unicast": "{{ True if unicast is defined }}", + } + } + }, + }, + { + "name": "default_weight", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sapply-weight(?P<apply_weight>) + \sdefault-weight(?P<default_weight>\s\d+) + $""", + re.VERBOSE, + ), + "setval": "apply-weight default-weight {{ default_weight }}", + "result": { + "processes": { + "{{ pid }}": { + "apply_weight": { + "default_weight": "{{ default_weight|int }}", + } + } + } + }, + }, + { + "name": "bandwidth", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sapply-weight(?P<apply_weight>) + \sbandwidth(?P<bandwidth>\s\d+)? + $""", + re.VERBOSE, + ), + "setval": "apply-weight bandwidth {{ bandwidth }}", + "result": { + "processes": { + "{{ pid }}": { + "apply_weight": { + "bandwidth": "{{ bandwidth|int }}", + } + } + } + }, + }, + { + "name": "adjacency_stagger", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sadjacency(?P<adjacency>) + \sstagger(?P<stagger>) + (\s(?P<min_adjacency>\d+))? + (\s(?P<max_adjacency>\d+))? + (\sdisable(?P<disable>\S+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_adjacency_stagger, + "result": { + "processes": { + "{{ pid }}": { + "adjacency_stagger": { + "min_adjacency": "{{ min_adjacency|int }}", + "max_adjacency": "{{ max_adjacency }}", + "disable": "{{ True if disable is defined }}", + }, + } + } + }, + }, + { + "name": "authentication", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sauthentication(?P<auth>) + (\skeychain\s(?P<keychain>\S+)*)? + (\snull(?P<no_auth>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_authentication, + "result": { + "processes": { + "{{ pid }}": { + "authentication": { + "no_auth": "{{ True if no_auth is defined }}", + "keychain": "{{ keychain }}", + }, + } + } + }, + }, + { + "name": "authentication.message_digest", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sauthentication(?P<auth>) + \smessage-digest(?P<md>) + \skeychain\s(?P<md_key>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_authentication_md, + "result": { + "processes": { + "{{ pid }}": { + "authentication": { + "message_digest": { + "keychain": "{{ md_key }}", + "set": "{{ True if md is defined and md_key is undefined }}", + } + }, + } + } + }, + }, + { + "name": "default_information_originate", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sdefault-information(?P<default_information>) + (\soriginate(?P<originate>))? + (\salways(?P<always>))? + (\smetric\s(?P<metric>\d+))? + (\smetric-type\s(?P<metric_type>\d+))? + (\sroute_policy\s(?P<route_policy>)\S+)? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_default_information, + "result": { + "processes": { + "{{ pid }}": { + "default_information_originate": { + "always": "{{ True if always is defined }}", + "metric": "{{ metric|int }}", + "metric_type": "{{ metric_type|int }}", + "route_policy": "{{ route_policy }}", + "set": "{{ True if default_information is defined and always is undefined and metric " + "is undefined and metric_type is undefined and route_policy is undefined }}" + }, + } + } + }, + }, + { + "name": "auto_cost", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sauto-cost(?P<auto_cost>) + (\sreference-bandwidth\s(?P<reference_bandwidth>\d+))? + (\sdisable(?P<disable>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_auto_cost, + "result": { + "processes": { + "{{ pid }}": { + "auto_cost": { + "disable": "{{ True if disable is defined }}", + "reference_bandwidth": "{{ reference_bandwidth|int }}", + }, + } + } + }, + }, + { + "name": "bfd", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sbfd(?P<bfd>) + (\sminimum-interval\s(?P<minimum_interval>\d+))? + (\smultiplier\s(?P<multiplier>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_bfd, + "result": { + "processes": { + "{{ pid }}": { + "bfd": { + "minimum_interval": "{{ minimum_interval|int }}", + "multiplier": "{{ multiplier|int }}", + }, + } + } + }, + }, + { + "name": "bfd.fast_detect", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sbfd(?P<bfd>) + \sfast-detect(?P<fast_detect>) + (\s(?P<disable>disable))? + (\s(?P<strict_mode>strict-mode))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_bfd_fast_detect, + "result": { + "processes": { + "{{ pid }}": { + "bfd": { + "fast_detect": { + "set": "{{ True if disable is undefined and strict_mode is undefined }}", + "strict_mode": "{{ True if strict_mode is defined }}", + }, + }, + } + }, + }, + }, + { + "name": "security", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \ssecurity(?P<security>) + \sttl(?P<ttl>)? + (\shops\s(?P<hops>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_security, + "result": { + "processes": { + "{{ pid }}": { + "security_ttl": { + "set": "{{ True if ttl is defined and hops is undefined }}", + "hops": "{{ hops }}", + }, + } + } + }, + }, + { + "name": "nsr", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \snsr(?P<nsr>) + \sdisable(?P<disable>)? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_nsr, + "result": { + "processes": { + "{{ pid }}": { + "nsr": { + "set": "{{ True if nsr is defined and disable is undefined }}", + "disable": "{{ True if disable is defined }}", + }, + } + } + }, + }, + { + "name": "protocol", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sprotocol(?P<protocol>) + \s(shutdown(?P<shutdown>)) + (\shost-mode(?P<host_mode>))? + (\son-reload\s(?P<on_reload>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_protocol, + "result": { + "processes": { + "{{ pid }}": { + "protocol_shutdown": { + "set": "{{ True if shutdown is defined and host-mode is undefined and on_reload is undefined }}", + "host_mode": "{{ True if host_mode is defined }}", + "on_reload": "{{ True if on_reload is defined }}", + }, + } + } + }, + }, + { + "name": "capability", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \scapability(?P<capability>) + (\stype7\s(?P<type7>\S+))? + $""", + re.VERBOSE, + ), + "setval": "capability type7 {{ type7 }}", + "result": { + "processes": { + "{{ pid }}": { + "capability": { + "type7": "{{ type7 }}" + }, + } + } + }, + }, + { + "name": "capability.opaque", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \scapability(?P<capability>)? + \sopaque(?P<opaque>) + (\sdisable(?P<disable>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_capability_opaque, + "result": { + "processes": { + "{{ pid }}": { + "capability": { + "opaque": { + "disable": "{{ True if disable is defined }}", + "set": "{{ True if opaque is defined and disable is undefined }}", + }, + }, + } + } + }, + }, + { + "name": "admin_distance", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sdistance\s(?P<value>d+) + \s(?P<source>\S+) + \s(?P<wildcard>\S+) + (\s(?P<access_list>\S+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_admin, + "result": { + "processes": { + "{{ pid }}": { + "distance": { + "admin_distance": { + "value": "{{ value|int }}", + "source": "{{ source }}", + "wildcard": "{{ wildcard }}", + "access_list": "{{ access_list }}", + } + }, + } + } + }, + }, + + { + "name": "ospf_distance", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sdistance(?P<value>) + \sospf(?P<ospf>) + (\sexternal\s(?P<external>\d+))? + (\sinter-area\s(?P<inter_area>\d+))? + (\sintra-area\s(?P<intra_area>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_ospf, + "result": { + "processes": { + "{{ pid }}": { + "distance": { + "ospf_distance": { + "external": "{{ external|int }}", + "inter_area": "{{ inter_area|int }}", + "intra_area": "{{ intra_area|int }}", + } + }, + } + } + }, + }, + { + "name": "authentication_key", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sauthentication-key(?P<auth_key>) + (\s(?P<password>\S+))? + (\sclear\s(?P<clear>)\S+)? + (\sencrypted(?P<encrypted>\S+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_authentication_key, + "result": { + "processes": { + "{{ pid }}": { + "authentication_key": { + "clear": "{{ clear }}", + "encrypted": "{{ encrypted}}", + "password": "{{ password if clear is undefined and encrypted is undefined }}", + }, + } + } + }, + }, + { + "name": "area.default_cost", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sdefault-cost\s(?P<default_cost>\d+) + $""", + 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.dead_interval", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sdead-interval\s(?P<dead_interval>\d+) + $""", + re.VERBOSE, + ), + + "setval": "area {{ area_id }} dead-interval {{ dead_interval }}", + "compval": "dead_interval", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "dead_interval": "{{ dead_interval|int }}", + } + } + } + } + }, + }, + { + "name": "area.hello_interval", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \shello-interval\s(?P<hello_interval>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} hello-interval {{ hello_interval }}", + "compval": "hello_interval", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "hello_interval": "{{ hello_interval|int }}", + } + } + } + } + }, + }, + { + "name": "area.transmit_delay", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \stransmit-delay\s(?P<transmit_delay>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} transmit-delay {{ transmit_delay }}", + "compval": "transmit_delay", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "transmit_delay": "{{ transmit_delay|int }}", + } + } + } + } + }, + }, + { + "name": "area.cost", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \scost\s(?P<cost>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} cost {{ cost }}", + "compval": "cost", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "cost": "{{ cost|int }}", + } + } + } + } + }, + }, + { + "name": "area.priority", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \spriority\s(?P<priority>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} priority {{ priority }}", + "compval": "priority", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "priority": "{{ priority|int }}", + } + } + } + } + }, + }, + { + "name": "area.weight", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sweight\s(?P<weight>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} weight {{ weight }}", + "compval": "weight", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "weight": "{{ weight|int }}", + } + } + } + } + }, + }, + { + "name": "area.packet_size", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \spacket-size\s(?P<packet_size>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} packet-size {{ packet_size }}", + "compval": "packet_size", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "packet_size": "{{ packet_size|int }}", + } + } + } + } + }, + }, + { + "name": "area.summary_in", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \ssummary-in\s(?P<summary_in>\S+) + $""", + re.VERBOSE, + ), + + "setval": "area {{ area_id }} summary-in {{ summary_in }}", + "compval": "summary_in", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "summary_in": "{{ summary_in }}", + } + } + } + } + }, + }, + { + "name": "area.demand_circuit", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sdemand-circuit\s(?P<demand_circuit>\S+) + $""", + re.VERBOSE, + ), + + "setval": "area {{ area_id }} demand-circuit {{ demand_circuit }}", + "compval": "demand_circuit", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "demand_circuit": "{{ demand_circuit }}", + } + } + } + } + }, + }, + { + "name": "area.passive", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \spassive\s(?P<passive>\S+) + $""", + re.VERBOSE, + ), + + "setval": "area {{ area_id }} passive {{ passive }}", + "compval": "passive", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "passive": "{{ passive }}", + } + } + } + } + }, + }, + { + "name": "area.external_out", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sexternal-out\s(?P<external_out>\S+) + $""", + re.VERBOSE, + ), + + "setval": "area {{ area_id }} external-out {{ external_out }}", + "compval": "passive", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "external_out": "{{ external_out }}", + } + } + } + } + }, + }, + { + "name": "area.mtu_ignore", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \smtu-ignore\s(?P<mtu_ignore>\S+) + $""", + re.VERBOSE, + ), + + "setval": "area {{ area_id }} mtu-ignore {{ mtu_ignore }}", + "compval": "mtu_ignore", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "mtu_ignore": "{{ mtu_ignore }}", + } + } + } + } + }, + }, + { + "name": "area.authentication", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sauthentication(?P<auth>) + (\skeychain\s(?P<keychain>\S+))? + (\snull(?P<no_auth>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_authentication, + "compval": "authentication", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "authentication": { + "no_auth": "{{ True if no_auth is defined }}", + "keychain": "{{ keychain }}", + }, + } + } + } + } + }, + }, + { + "name": "area.authentication_key", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sauthentication-key(?P<auth_key>) + (\s(?P<password>\S+))? + (\sclear\s(?P<clear>)\S+)? + (\sencrypted(?P<encrypted>\S+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_authentication_key, + "compval": "authentication_key", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "authentication_key": { + "clear": "{{ clear }}", + "encrypted": "{{ encrypted}}", + "password": "{{ password if clear is undefined and encrypted is undefined }}", + }, + } + } + } + } + }, + }, + { + "name": "area.authentication.message_digest", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sauthentication(?P<auth>) + \smessage-digest(?P<md>) + \skeychain(?P<md_key>\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_authentication_md, + "compval": "authentication.message_digest", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "authentication": { + "message_digest": { + "keychain": "{{ md_key }}", + } + }, + } + } + } + } + }, + }, + { + "name": "area.mpls_traffic_eng", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \smpls(?P<mpls>) + \straffic-end(?P<traffic_eng>) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} mpls traffic-eng", + "compval": "mpls_traffic_eng", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "mpls": { + "traffic_eng": "{{ True if traffic_eng is defined }}", + }, + } + } + } + } + }, + }, + { + "name": "area.mpls_ldp", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \smpls(?P<mpls>) + (\sauto-config(?P<auto_config>))? + (\ssync(?P<sync>))? + (\ssync-igp-shortcuts(?P<syn_igp_shortcuts>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_mpls_ldp, + "compval": "mpls_ldp", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "mpls": { + "ldp": { + "auto_config": "{{ True if auto_config is defined }}", + "sync": "{{ True if sync is defined }}", + "sync_igp_shortcuts": "{{ True if sync_igp_shortcuts is defined }}", + + } + }, + } + } + } + } + }, + }, + { + "name": "area.bfd", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sbfd(?P<bfd>) + (\sminimum-interval\s(?P<minimum_interval>\d+))? + (\smultiplier\s(?P<multiplier>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_bfd, + "compval": "bfd", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "bfd": { + "minimum_interval": "{{ minimum_interval|int }}", + "multiplier": "{{ multiplier|int }}", + }, + } + } + } + } + }, + }, + { + "name": "area.bfd.fast_detect", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sbfd(?P<bfd>) + \sarea(?P<area_id>) + \sfast-detect(?P<fast_detect>) + (\s(?P<disable>disable))? + (\s(?P<strict_mode>strict-mode))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_bfd_fast_detect, + "compval": "bfd.fast_detect", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "bfd": { + "fast_detect": { + "set": "{{ True if disable is undefined and strict_mode is undefined }}", + "strict_mode": "{{ True if strict_mode is defined }}", + }, + }, + } + } + } + }, + }, + }, + { + "name": "area.stub", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sstub(?P<nssa>) + (\sno-summary(?P<no_sum>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_stub, + "compval": "stub", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "area_id": "{{ area_id }}", + "stub": { + "set": "{{ True if stub is defined and no_summary is undefined }}", + "no_summary": "{{ True if no_summary is defined }}", + } + } + } + } + }, + }, + { + "name": "area.nssa", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \snssa(?P<nssa>) + (\sno-redistribution(?P<no_redis>))? + (\sno-summary(?P<no_sum>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa, + "compval": "nssa", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "area_id": "{{ area_id }}", + "nssa": { + "set": "{{ True if nssa is defined and no_summary is undefined and no_redis is undefined }}", + "no_summary": "{{ True if no_summary is defined }}", + "no_redistribution": "{{ True if no_redis is defined }}" + } + } + } + } + }, + }, + { + "name": "area.nssa.default_information_originate", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \snssa(?P<nssa>) + (\sno-redistribution(?P<no_redis>))? + (\sdefault-information-originate(?P<def_info_origin>))? + (\smetric\s(?P<metric>\d+))? + (\smetric-type\s(?P<metric_type>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa_def_info_origin, + "compval": "nssa.default_information_originate", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "default_information_originate": { + "metric": "{{ metric|int }}", + "metric_type": "{{ metric_type|int }}", + }, + } + } + } + } + }, + }, + }, + { + "name": "area.ranges", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \srange(?P<range>) + \s(?P<address>\S+) + (\sadvertise(?P<advertise>)) + (\snot-advertise(?P<not_advertise>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_ranges, + "compval": "ranges", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "ranges": [ + { + "address": "{{ address }}", + "advertise": "{{ True if advertise is defined }}", + "not_advertise": "{{ True if not_advertise is defined }}", + } + ], + } + } + } + } + }, + }, + { + "name": "area.nssa.translate", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \snssa(?P<nssa>) + \stranslate(?P<translate>) + \stype7(?P<type7>) + \salways\s(?P<always>) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa_translate, + "compval": "nssa.translate", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "area_id": "{{ area_id }}", + "nssa": { + "translate": { + "type7": { + "always": "{{ True if always is defined }}" + } + }, + } + } + } + } + }, + }, + { + "name": "virtual_link.hello_interval", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \svirtual-link\s(?P<id>\S+) + \shello-interval\s(?P<hello_interval>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} virtual-link {{ id }} hello-interval {{ hello_interval }}", + "compval": "hello_interval", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "virtual_link": { + "{{ id }}": + { + "id": "{{ id }}", + "hello_interval": "{{ hello_interval|int }}" + } + + } + } + } + } + } + }, + }, + { + "name": "virtual_link.dead_interval", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \svirtual-link\s(?P<id>\S+) + \sdead-interval\s(?P<dead_interval>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} virtual-link {{ id }} dead-interval {{ dead_interval }}", + "compval": "dead_interval", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "virtual_link": { + "{{ id }}": { + "id": "{{ id }}", + "dead_interval": "{{ dead_interval|int }}" + } + + } + } + } + } + } + }, + }, + { + "name": "virtual_link.retransmit_interval", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \svirtual-link\s(?P<id>\S+) + \sretransmit-interval\s(?P<retransmit_interval>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} virtual-link {{ id }} retransmit-interval {{ retransmit_interval }}", + "compval": "retransmit_interval", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "virtual_link": { + "{{ id }}": { + "id": "{{ id }}", + "retransmit_interval": "{{ retransmit_interval|int }}" + } + } + + } + } + } + } + } + }, + { + "name": "virtual_link.authentication", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \savirtual-link\s(?P<id>\S+) + \sauthentication(?P<auth>) + (\skeychain\s(?P<keychain>\S+))? + (\snull(?P<no_auth>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_vlink_authentication, + "compval": "authentication", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "authentication": { + "no_auth": "{{ True if no_auth is defined }}", + "keychain": "{{ keychain }}", + }, + } + } + } + } + }, + }, + { + "name": "virtual_link.authentication_key", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \svirtual-link\s(?P<id>\S+) + \sauthentication-key(?P<auth_key>) + (\s(?P<password>\S+))? + (\sclear\s(?P<clear>)\S+)? + (\sencrypted(?P<encrypted>\S+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_vlink_authentication_key, + "compval": "authentication_key", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "virtual_link": { + "{{ id }}": { + "authentication_key": { + "clear": "{{ clear }}", + "encrypted": "{{ encrypted}}", + "password": "{{ password if clear is undefined and encrypted is undefined }}", + }, + } + } + } + } + } + } + }, + }, + { + "name": "virtual_link.authentication.message_digest", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \svirtual-link\s(?P<id>\S+) + \sauthentication(?P<auth>) + \smessage-digest(?P<md>) + \skeychain(?P<md_key>\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_vlink_authentication_md, + "compval": "authentication.message_digest", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "virtual_link": { + "{{ id }}": { + "authentication": { + "message_digest": { + "keychain": "{{ md_key }}", + } + }, + } + } + } + } + } + } + }, + }, + { + "name": "link_down_fast_detect", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \slink-down + \sfast-detect(?P<fast_detect>) + $""", + re.VERBOSE, + ), + "setval": "link-down fast-detect", + "result": { + "processes": { + "{{ pid }}": { + "link_down_fast_detect": "{{ True if fast_detect is defined }}", + } + } + }, + }, + { + "name": "nsr", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \snsr + \sdisable(?P<disable>) + $""", + re.VERBOSE, + ), + "setval": "nsr disable", + "result": { + "processes": { + "{{ pid }}": { + "disable": "{{ True if disable is defined }}", + } + } + }, + }, + { + "name": "database_filter", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sdatabase-filter + \sall + \sout\s(?P<outing>\S+) + $""", + re.VERBOSE, + ), + + "setval": "database-filter all out {{ outing }}", + "result": { + "processes": { + "{{ pid }}": { + "outing": "{{ outing }}" + } + } + }, + }, + { + "name": "distribute_link_state", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sdistribute(?P<distribute>) + \slink-state(?P<link_state>) + (\sinstance-id(?P<inst_id>\d+))? + (\sthrottle(?P<throttle>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_adjacency_distribute_bgp_state, + "result": { + "processes": { + "{{ pid }}": { + "distribute_link_list": { + "instance_id": "{{ inst_id|int }}", + "throttle": "{{ throttle }}", + }, + } + } + }, + }, + { + "name": "distribute_bgp_ls", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sdistribute(?P<distribute>) + \sbgp-ls(?P<bgp_ls>) + (\sinstance-id(?P<inst_id>\d+))? + (\sthrottle(?P<throttle>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_adjacency_distribute_bgp_state, + "result": { + "processes": { + "{{ pid }}": { + "distribute_bgp_ls": { + "instance_id": "{{ inst_id|int }}", + "throttle": "{{ throttle }}", + }, + } + } + }, + }, + { + "name": "log_adjacency", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \slog(?P<security>) + \sadjacency(?P<adjacency>)? + (\schanges(?P<changes>))? + (\sdisable(?P<disable>))? + (\sdetails(?P<details>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_log_adjacency, + "result": { + "processes": { + "{{ pid }}": { + "log_adjacency_changes": { + "set": "{{ True changes id defined and disable is undefined and detail is undefined }}", + "disable": "{{ True if disable is defined }}", + "details": "{{ True if details is defined }}", + }, + } + } + }, + }, + { + "name": "max_lsa", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + (\smax-lsa\s(?P<threshold>\d+))? + (\swarning-only\s(?P<warning_only>\d+)? + (\signore-time\s(?P<ignore_time>\d+))? + (\signore-count\s(?P<ignore_count>\d+))? + (\sreset-time\s(?P<reset_time>)\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_log_max_lsa, + "result": { + "processes": { + "{{ pid }}": { + "max_lsa": { + "threshold": "{{ threshold|int }}", + "warning_only": "{{ warning_only|int }}", + "ignore_time": "{{ ignore_time|int }}", + "ignore_count": "{{ ignore_count|int }}", + "reset_time": "{{ reset_time|int }}", + }, + } + } + }, + }, + { + "name": "max_metric", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \smax-metric + \s*(?P<router_lsa>) + (\s*external-lsa(?P<external_lsa>))? + (\s(?P<max_metric_value>\d+))? + \s*(?P<include_stub>include-stub)* + \s*(?P<on_startup>on-startup)* + \s*(?P<wait_period>\d+)* + \s*(wait-for\sbgp)* + \s*(?P<bgp_asn>\d+)* + \s*(?P<summary_lsa>summary-lsa)* + \s*(?P<sum_lsa_max_metric_value>\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_metric, + "remval": "max-metric router-lsa", + "result": { + "processes": { + '{{ "pid" }}': { + "max_metric": { + "router_lsa": { + "set": "{{ True if router_lsa is defined and external_lsa is undefined else None }}", + "external_lsa": { + "set": "{{ True if external_lsa is defined and max_metric_value is undefined else None }}", + "max_metric_value": "{{ max_metric_value }}", + }, + "include_stub": "{{ not not include_stub }}", + "on_startup": { + "set": "{{ True if on_startup is defined and (wait_period and bgp_asn) is undefined else None }}", + "wait_period": "{{ wait_period }}", + "wait_for_bgp_asn": "{{ bgp_asn }}", + }, + "summary_lsa": { + "set": "{{ True if summary_lsa is defined and sum_lsa_max_metric_value is undefined else None }}", + "max_metric_value": "{{ sum_lsa_max_metric_value }}", + }, + } + } + } + } + }, + }, + { + "name": "mpls_ldp", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \smpls(?P<mpls>) + (\sauto-config(?P<auto_config>))? + (\ssync(?P<sync>))? + (\ssync-igp-shortcuts(?P<syn_igp_shortcuts>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_mpls_ldp, + "compval": "mpls_ldp", + "result": { + "processes": { + "{{ pid }}": { + "mpls": { + "ldp": { + "auto_config": "{{ True if auto_config is defined }}", + "sync": "{{ True if sync is defined }}", + "sync_igp_shortcuts": "{{ True if sync_igp_shortcuts is defined }}", + } + }, + } + } + }, + }, + { + "name": "microloop_avoidance", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \smicroloop(?P<microloop>) + \savoidance(?P<avoidance>) + (\s(?P<protected>protected))? + (\s(?P<segment_routing>segment-routing))? + (\srib-update-delay\s(?P<rib_update_delay>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_microloop_avoidance, + "compval": "microloop_avoidance", + "result": { + "processes": { + "{{ pid }}": { + "microloop_avoidance": { + "protected": "{{ True if protected is defined }}", + "segment_routing": "{{ True if segment_routing is defined }}", + "rib_update_delay": "{{ rib_update_delay }}", + }, + } + } + }, + }, + { + "name": "mpls_traffic_eng", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \smpls(?P<mpls>) + \straffic-end(?P<traffic_eng>) + (\sautoroute-exclude(?P<autoroute>))? + (\sroute-policy(?P<route_policy>\S+))? + (\s(?P<igp_intact>igp_intact))? + (\s(?P<ldp_sync_update>ldp-sync-update))? + (\s(?P<multicast_intact>multicast-intact))? + (\srouter-id\s(?P<router_id>\S+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_mpls_traffic_eng, + "result": { + "processes": { + "{{ pid }}": { + "mpls": { + "autoroute_exclude": { + "route_policy": "{{ route_policy }}" + }, + "igp_intact": "{{ True if igp_intact is defined }}", + "ldp_sync_update": "{{ True if ldp_sync_update is defined }}", + "multicast_intact": "{{ True if multicast_intact is defined }}", + "router_id": "{{ router_id }}" + }, + } + } + }, + }, + { + "name": "prefix_suppression", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sprefix-suppression(?P<prefix_suppression>) + (\s(?P<secondary_address>secondary-address))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_prefix_suppression, + "result": { + "processes": { + "{{ pid }}": { + "prefix_suppression": { + "set": "{{ True if prefix_suppression is defined and secondary_address is undefined }}", + "secondary_address": "{{ True if secondary_address is defined }}", + }, + } + } + }, + }, + { + "name": "protocol_shutdown", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \sprotocol-shutdown(?P<protocol_shutdown>) + (\s(?P<host_mode>host-mode))? + (\s(?P<on_reload>on-reload))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_protocol_shutdown, + "result": { + "processes": { + "{{ pid }}": { + "protocol_shutdown": { + "set": "{{ True if protocol_shutdown is defined and host_mode is undefined and on_reload is undefined }}", + "host_mode": "{{ True if host_mode is defined }}", + "on_reload": "{{ True if on_reload is defined }}", + }, + } + } + }, + }, + { + "name": "timers.lsa", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \stimers + \slsa + (\sgroup-pacing\s(?P<group_pacing>\d+))? + (\smin-arrival\s(?P<min_arrival>\d+))? + (\srefresh\s(?P<refresh>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_timers_lsa, + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "lsa": { + "group_pacing": "{{ group_pacing|int }}", + "min_arrival": "{{ min_arrival|int }}", + "refresh": "{{ refresh|int }}", + }, + } + } + } + }, + }, + { + "name": "timers.graceful_shutdown", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \stimers + \sgraceful_shutdown + (\sinitial delay\s(?P<initial_delay>\d+))? + (\sretain routes\s(?P<retain_routes>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_timers_graceful_shutdown, + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "graceful_shutdown": { + "initial_delay": "{{ initial_delay|int }}", + "retain_routes": "{{ retain_routes|int }}", + }, + } + } + } + }, + }, + { + "name": "throttle.spf", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \stimers + \sthrottle + \sspf + (\s(?P<change_delay>\d+)) + (\s(?P<second_delay>\d+)) + (\s(?P<max_wait>\d+)) + $""", + re.VERBOSE, + ), + "setval": "timers throttle spf {{ throttle.spf.change_delay }} {{ throttle.spf.second_delay }} {{ throttle.spf.max_wait }}", + "compval": "throttle.lsa_all", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "lsa_all": { + "initial_delay": "{{ initial_delay }}", + "min_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + }, + } + } + } + } + }, + }, + { + "name": "throttle.lsa_all", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \stimers + \sthrottle + \slsa + \sall + (\s(?P<initial_delay>\d+)) + (\s(?P<min_delay>\d+)) + (\s(?P<max_delay>\d+)) + $""", + re.VERBOSE, + ), + "setval": "timers throttle lsa all {{ throttle.lsa_all.initial_delay }} {{ throttle.lsa_all.min_delay }} {{ throttle.lsa_all.max_delay }}", + "compval": "throttle.lsa_all", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "lsa_all": { + "initial_delay": "{{ initial_delay }}", + "min_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + }, + } + } + } + } + }, + }, + { + "name": "throttle.fast_reroute", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \stimers + \sthrottle + \sfast-reroute\s(?P<fast_reroute>\d+) + $""", + re.VERBOSE, + ), + "setval": "timers throttle fast-reroute {{ fast_reroute }}", + "compval": "throttle.fast_reroute", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "fast_reroute": "{{ fast_reroute }}", + } + } + } + } + }, + }, + { + "name": "timers.pacing_flood", + "getval": re.compile( + r""" + ^router + \sospf\s(?P<pid>\S+) + \stimers + \spacing + \sflood\s(?P<pacing_flood>\d+) + $""", + re.VERBOSE, + ), + "setval": "timers pacing flood {{ pacing_flood }}", + "compval": "timers.pacing_flood", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "pacing_flood": "{{ pacing_flood }}", + + } + } + } + }, + }, + ] + # fmt: on diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/ospfv3.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/ospfv3.py new file mode 100644 index 00000000..d458615e --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/rm_templates/ospfv3.py @@ -0,0 +1,2801 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +import re +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import ( + NetworkTemplate, +) +from ansible.module_utils.six import iteritems + + +def _tmplt_ospf_default_information(config_data): + if "default_information_originate" in config_data: + command = "default-information originate" + if "always" in config_data["default_information_originate"]: + command += " always" + if "metric" in config_data["default_information_originate"]: + command += " metric {metric}".format( + **config_data["default_information_originate"] + ) + if "metric_type" in config_data["default_information_originate"]: + command += " metric-type {metric_type}".format( + **config_data["default_information_originate"] + ) + if "route_policy" in config_data["default_information_originate"]: + command += " route-policy {route_policy}".format( + **config_data["default_information_originate"] + ) + return command + + +def _tmplt_ospf_auto_cost(config_data): + if "auto_cost" in config_data: + command = "auto-cost" + if "disable" in config_data["auto_cost"]: + command += " disable" + if "reference_bandwidth" in config_data["auto_cost"]: + command += " reference-bandwidth {reference_bandwidth}".format( + **config_data["auto_cost"] + ) + return command + + +def _tmplt_ospfv3_bfd_minimum_interval(config_data): + if "bfd" in config_data: + if "minimum_interval" in config_data["bfd"]: + command = "bfd minimum-interval {minimum_interval}".format( + **config_data["bfd"] + ) + return command + + +def _tmplt_ospfv3_bfd_multiplier(config_data): + if "bfd" in config_data: + if "multiplier" in config_data["bfd"]: + command = "bfd multiplier {multiplier}".format( + **config_data["bfd"] + ) + return command + + +def _tmplt_ospf_security(config_data): + if "security_ttl" in config_data: + command = "security_ttl" + if "set" in config_data["security_ttl"]: + command += " ttl" + elif config_data["security_ttl"].get("hops"): + command += " ttl hops {0}".format( + config_data["security_ttl"].get("hops") + ) + return command + + +def _tmplt_ospf_log_adjacency(config_data): + if "log_adjacency_changes" in config_data: + command = "log adjacency" + if "set" in config_data["log_adjacency_changes"]: + command += " changes" + elif config_data["log_adjacency_changes"].get("disable"): + command += " disable" + elif config_data["log_adjacency_changes"].get("details"): + command += " details" + return command + + +def _tmplt_ospf_log_max_lsa(config_data): + if "max_lsa" in config_data: + command = "max-lsa" + if "threshold" in config_data["max_lsa"]: + command += " {0}".format(config_data["max_lsa"].get("threshold")) + if "warning_only" in config_data["max_lsa"]: + command += " warning-only {0}".format( + config_data["max_lsa"].get("warning_only") + ) + if "ignore_time" in config_data["max_lsa"]: + command += " ignore-time {0}".format( + config_data["max_lsa"].get("ignore_time") + ) + if "ignore_count" in config_data["max_lsa"]: + command += " ignore-count {0}".format( + config_data["max_lsa"].get("ignore_count") + ) + if "reset_time" in config_data["max_lsa"]: + command += " reset-time {0}".format( + config_data["max_lsa"].get("reset_time") + ) + 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_distance_admin(config_data): + if "admin_distance" in config_data: + command = "distance" + if config_data["admin_distance"].get("value"): + command += " {0}".format( + config_data["admin_distance"].get("value") + ) + if config_data["admin_distance"].get("source"): + command += " {0}".format( + config_data["admin_distance"].get("source") + ) + if config_data["admin_distance"].get("wildcard"): + command += " {0}".format( + config_data["admin_distance"].get("wildcard") + ) + if config_data["admin_distance"].get("access_list"): + command += " {0}".format( + config_data["admin_distance"].get("access_list") + ) + return command + + +def _tmplt_ospf_distance_ospf(config_data): + if "ospf_distance" in config_data: + command = "distance ospf" + if config_data["ospf_distance"].get("external"): + command += " external {0}".format( + config_data["ospf_distance"].get("external") + ) + if config_data["ospf_distance"].get("inter_area"): + command += " inter-area {0}".format( + config_data["ospf_distance"].get("inter_area") + ) + if config_data["ospf_distance"].get("intra_area"): + command += " intra-area {0}".format( + config_data["ospf_distance"].get("intra_area") + ) + return command + + +def _tmplt_ospf_nsr(config_data): + if "nsr" in config_data: + command = "nsr" + if "set" in config_data["nsr"]: + command += " nsr" + elif config_data["nsr"].get("disable"): + command += " nsr {0}".format("disable") + return command + + +def _tmplt_ospf_protocol(config_data): + if "protocol_shutdown" in config_data: + command = "protocol" + if "set" in config_data["protocol_shutdown"]: + command += " shutdown" + elif config_data["shutdown"].get("host_mode"): + command += " shutdown host-mode" + elif config_data["shutdown"].get("on_reload"): + command += " shutdown on-reload" + return command + + +def _tmplt_microloop_avoidance(config_data): + if "microloop_avoidance" in config_data: + command = "microloop avoidance" + if "protected" in config_data["microloop_avoidance"]: + command += " protected" + if "segment_routing" in config_data["microloop_avoidance"]: + command += " segment_routing" + if "rib_update_delay" in config_data["microloop_avoidance"]: + command += " rin-update-delay {0}".config_data[ + "microloop_avoidance" + ].get("rib_update_delay") + return command + + +def _tmplt_ospf_bfd_fast_detect(config_data): + if "bfd" in config_data: + if "fast_detect" in config_data["bfd"]: + command = "bfd" + fast_detect = config_data["bfd"].get("fast_detect") + command += " fast-detect" + if "strict_mode" in fast_detect: + command += " strict-mode" + return command + + +def _tmplt_ospf_mpls_traffic_eng(config_data): + if "traffic_eng" in config_data: + command = "mpls traffic-eng" + if "igp_intact" in config_data["traffic_eng"]: + command += " igp-intact" + if "ldp_sync_update" in config_data["traffic_eng"]: + command += " ldp_sync_update" + if "multicast_intact" in config_data["traffic_eng"]: + command += " multicast_intact" + if "auto_route_exclude" in config_data["traffic_eng"]: + policy = config_data["traffic_eng"].get("autoroute_exclude") + command += " autoroute-exlude route-policy {0}".format( + policy.get("route_policy") + ) + return command + + +def _tmplt_ospf_authentication_ipsec(config_data): + if "authentication" in config_data: + if config_data["authentication"].get("ipsec"): + command = "authentication ipsec" + md = config_data["authentication"].get("ipsec") + if md.get("spi"): + command += " spi " + str(md.get("spi")) + if md.get("algorithim_type"): + command += " " + md.get("algorithim_type") + if md.get("clear_key"): + command += " clear " + md.get("clear_key") + elif md.get("password_key"): + command += " password " + md.get("password_key") + elif md.get("key"): + command += " " + md.get("key") + return command + + +def _tmplt_ospfv3_demand_circuit(config_data): + if "demand_circuit" in config_data: + command = "demand-circuit" + return command + + +def _tmplt_ospfv3_passive(config_data): + if "passive" in config_data: + command = "passive" + return command + + +def _tmplt_ospfv3_demand_mtu_ignore(config_data): + if "mtu_ignore" in config_data: + command = "mtu-ignore" + return command + + +def _tmplt_ospfv3_authentication(config_data): + if "authentication" in config_data: + if config_data["authentication"].get("disable"): + command = "authentication disable" + return command + + +def _tmplt_ospf_adjacency_distribute_bgp_state(config_data): + if "distribute_link_list" in config_data: + command = "distribute link-state" + if config_data["distribute_link_list"].get("instance_id"): + command += " instance-id {0}".format( + config_data["distribute_link_list"].get("instance_id") + ) + elif config_data["distribute_link_list"].get("throttle"): + command += " throttle {0}".format( + config_data["distribute_link_list"].get("throttle") + ) + return command + elif "distribute_bgp_ls" in config_data: + command = "distribute bgp-ls" + if config_data["distribute_bgp_ls"].get("instance_id"): + command += " instance-id {0}".format( + config_data["distribute_bgp_ls"].get("instance_id") + ) + elif config_data["distribute_bgp_ls"].get("throttle"): + command += " throttle {0}".format( + config_data["distribute_bgp_ls"].get("throttle") + ) + return command + + +def _tmplt_ospf_capability_opaque(config_data): + if "capability" in config_data: + if "opaque" in config_data["capability"]: + command = "capability opaque" + opaque = config_data["capability"].get("opaque") + if "disable" in opaque: + command += "capability opaque disable" + return command + + +def _tmplt_ospfv3_area_authentication(config_data): + if "authentication" in config_data: + if config_data["authentication"].get("disable"): + command = "area {area_id} ".format(**config_data) + command += "authentication disable" + return command + + +def _tmplt_ospfv3_area_authentication_ipsec(config_data): + if "authentication" in config_data: + command = "area {area_id} ".format(**config_data) + if config_data["authentication"].get("ipsec"): + command += "authentication ipsec" + md = config_data["authentication"].get("ipsec") + if md.get("spi"): + command += " spi " + str(md.get("spi")) + if md.get("algorithim_type"): + command += " " + md.get("algorithim_type") + if md.get("clear_key"): + command += " clear " + md.get("clear_key") + elif md.get("password_key"): + command += " password " + md.get("password_key") + elif md.get("key"): + command += " " + md.get("key") + return command + + +def _tmplt_ospf_area_mpls_ldp(config_data): + commands = [] + if "mpls" in config_data: + command = "area {area_id} mpls".format(**config_data) + if config_data["mpls"].get("ldp"): + ldp = config_data["mpls"].get("ldp") + if "auto_config" in ldp: + command += " auto-config" + commands.append(command) + if "sync" in ldp: + command += " sync" + commands.append(command) + if "sync_igp_shortcuts" in ldp: + command += " sync-igp-shortcuts" + commands.append(command) + return commands + + +def _tmplt_ospfv3_area_bfd_minimum_interval(config_data): + if "bfd" in config_data: + if "minimum_interval" in config_data["bfd"]: + command = "area {area_id} ".format(**config_data) + command += " bfd minimum-interval {minimum_interval}".format( + **config_data["bfd"] + ) + return command + + +def _tmplt_ospfv3_area_bfd_multiplier(config_data): + if "bfd" in config_data: + if "multiplier" in config_data["bfd"]: + command = "area {area_id} ".format(**config_data) + command += "bfd multiplier {multiplier}".format( + **config_data["bfd"] + ) + return command + + +def _tmplt_ospfv3_area_bfd_fast_detect(config_data): + if "bfd" in config_data: + if "fast_detect" in config_data["bfd"]: + command = "area {area_id} ".format(**config_data) + fast_detect = config_data["bfd"].get("fast_detect") + command += "bfd fast-detect" + if "strict_mode" in fast_detect: + command += " strict-mode" + return command + + +def _tmplt_ospf_mpls_ldp(config_data): + commands = [] + if "mpls" in config_data: + command = "mpls".format(**config_data) + if config_data["mpls"].get("ldp"): + ldp = config_data["mpls"].get("ldp") + if "auto_config" in ldp: + command += " auto-config" + commands.append(command) + if "sync" in ldp: + command += " sync" + commands.append(command) + if "sync_igp_shortcuts" in ldp: + command += " sync-igp-shortcuts" + commands.append(command) + return commands + + +def _tmplt_ospf_area_nssa(config_data): + if "nssa" in config_data: + command = "area {area_id} nssa".format(**config_data) + 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_def_info_origin(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" + def_info_origin = config_data["nssa"].get( + "default_information_originate" + ) + if "metric" in def_info_origin: + command += " metric {metric}".format( + **config_data["nssa"]["default_information_originate"] + ) + if "metric_type" in def_info_origin: + command += " metric-type {metric_type}".format( + **config_data["nssa"]["default_information_originate"] + ) + return command + + +def _tmplt_ospf_area_nssa_translate(config_data): + if "nssa" in config_data: + command = "area {area_id} nssa".format(**config_data) + if config_data["nssa"].get("translate"): + command += " translate" + translate = config_data["nssa"].get("translate") + if "type7" in translate: + command += " type7" + if translate["type7"].get("always"): + command += " always" + return command + + +def _tmplt_ospf_area_vlink_authentication(config_data): + if "authentication" in config_data: + command = "area {area_id} virtual-link {id} authentication".format( + **config_data + ) + if config_data["authentication"].get("keychain"): + command += " keychain " + config_data["authentication"].get( + "keychain" + ) + elif config_data["authentication"].get("no_auth"): + command += " null" + return command + + +def _tmplt_ospf_area_vlink_authentication_md(config_data): + if "authentication" in config_data: + command = "area {area_id} virtual-link {id} authentication".format( + **config_data + ) + if config_data["authentication"].get("message_digest"): + command = "authentication message-digest" + md = config_data["authentication"].get("message_digest") + if md.get("keychain"): + command += " keychain " + md.get("keychain") + return command + + +def _tmplt_ospf_area_vlink_authentication_key(config_data): + if "authentication_key" in config_data: + command = "area {area_id} virtual-link {id} authentication-key".format( + **config_data + ) + if config_data["authentication_key"].get("password"): + command += " {0}".format( + config_data["authentication_key"].get("password") + ) + return command + + +def _tmplt_ospf_area_stub(config_data): + if "stub" in config_data: + command = "area {area_id} stub".format(**config_data) + if config_data["stub"].get("no_summary"): + command += " no-summary" + 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}".format(**v) + if "advertise" in v: + temp_cmd += " advertise" + elif "not_advertise" in v: + temp_cmd += " not-advertise" + cmd += temp_cmd + commands.append(cmd) + return commands + + +def _tmplt_prefix_suppression(config_data): + if "prefix_suppression" in config_data: + if "set" in config_data["prefix_suppression"]: + command = "prefix-suppression" + if "secondary_address" in config_data["prefix_suppression"]: + command = "prefix-suppression secondary-address" + return command + + +def _tmplt_protocol_shutdown(config_data): + if "protocol_shutdown" in config_data: + if "set" in config_data["protocol_shutdown"]: + command = "protocol-shutdown" + if "host_mode" in config_data["protocol_shutdown"]: + command = "protocol-shutdown host-mode" + if "on_reload" in config_data["protocol_shutdown"]: + command = "protocol-shutdown on-reload" + return command + + +def _tmplt_timers_lsa(config_data): + if "timers" in config_data: + command = "timers lsa" + if "group_pacing" in config_data["timers"]["lsa"]: + command += " group-pacing {group_pacing}".format( + **config_data["timers"]["lsa"] + ) + if "min_arrival" in config_data["timers"]["lsa"]: + command += " min-arrival {min_arrival}".format( + **config_data["timers"]["lsa"] + ) + if "refresh" in config_data["timers"]["lsa"]: + command += " refresh {refresh}".format( + **config_data["timers"]["lsa"] + ) + return command + + +def _tmplt_timers_graceful_shutdown(config_data): + if "timers" in config_data: + command = "timers graceful-shutdown" + if "initial_delay" in config_data["timers"]["graceful-shutdown"]: + command += " initial delay {initial_delay}".format( + **config_data["timers"]["graceful-shutdown"] + ) + if "retain_routes" in config_data["timers"]["graceful-shutdown"]: + command += " retain routes {retain_routes}".format( + **config_data["timers"]["graceful-shutdown"] + ) + return command + + +class Ospfv3Template(NetworkTemplate): + def __init__(self, lines=None): + super(Ospfv3Template, self).__init__(lines=lines, tmplt=self) + + # fmt: off + PARSERS = [ + { + "name": "pid", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + $""", + re.VERBOSE, + ), + "setval": "router ospfv3 {{ process_id }}", + "result": { + "processes": {"{{ pid }}": {"process_id": "{{ pid }}"}} + }, + "shared": True, + }, + { + "name": "cost", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \scost(?P<cost>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "cost {{ cost }}", + "result": { + "processes": { + "{{ pid }}": { + "cost": "{{ cost|int }}", + } + } + }, + }, + { + "name": "default_metric", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sdefault-metric(?P<default_metric>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "default-metric {{ default_metric }}", + "result": { + "processes": { + "{{ pid }}": { + "default_metric": "{{ default_metric|int }}", + } + } + }, + }, + { + "name": "packet_size", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \spacket-size(?P<packet_size>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "packet-size {{ packet_size }}", + "result": { + "processes": { + "{{ pid }}": { + "packet_size": "{{ packet_size|int }}", + } + } + }, + }, + { + "name": "dead_interval", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sdead-interval(?P<dead_interval>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "dead-interval {{ dead_interval }}", + "result": { + "processes": { + "{{ pid }}": { + "dead_interval": "{{ dead_interval|int }}", + } + } + }, + }, + { + "name": "hello_interval", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \shello-interval(?P<hello_interval>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "hello-interval {{ hello_interval }}", + "result": { + "processes": { + "{{ pid }}": { + "hello_interval": "{{ hello_interval|int }}", + } + } + }, + }, + { + "name": "priority", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \spriority(?P<priority>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "priority {{ priority }}", + "result": { + "processes": { + "{{ pid }}": { + "priority": "{{ priority|int }}", + } + } + }, + }, + { + "name": "weight", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sweight(?P<weight>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "weight {{ weight }}", + "result": { + "processes": { + "{{ pid }}": { + "weight": "{{ weight|int }}", + } + } + }, + }, + { + "name": "retransmit_interval", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sretransmit-interval(?P<retransmit_interval>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "retransmit-interval {{ retransmit_interval }}", + "result": { + "processes": { + "{{ pid }}": { + "retransmit_interval": "{{ retransmit_interval|int }}", + } + } + }, + }, + { + "name": "transmit_delay", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \stransmit-delay(?P<transmit_delay>\s\d+) + $""", + re.VERBOSE, + ), + + "setval": "transmit-delay {{ transmit_delay }}", + "result": { + "processes": { + "{{ pid }}": { + "transmit_delay": "{{ transmit_delay|int }}", + } + } + }, + }, + { + "name": "passive", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + (\spassive(?P<passive>))? + $""", + re.VERBOSE, + ), + + "setval": _tmplt_ospfv3_passive, + "result": { + "processes": { + "{{ pid }}": { + "passive": "{{ True if passive is defined }}", + } + } + }, + }, + { + "name": "process.database_filter", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sdatabase-filter + \sall + \sout\s(?P<database_filter>\s\S+) + $""", + re.VERBOSE, + ), + + "setval": "process.database_filter", + "result": { + "processes": { + "{{ pid }}": { + "database_filter": "{{ database_filter }}", + } + } + }, + }, + { + "name": "demand_circuit", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + (\sdemand-circuit(?P<demand_circuit>))? + $""", + re.VERBOSE, + ), + + "setval": _tmplt_ospfv3_demand_circuit, + "result": { + "processes": { + "{{ pid }}": { + "demand_circuit": "{{ True if demand_circuit is defined }}", + } + } + }, + }, + { + "name": "router_id", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \srouter-id\s(?P<router_id>\S+) + $""", + re.VERBOSE, + ), + + "setval": "router-id {{ router_id }}", + "result": { + "processes": { + "{{ pid }}": { + "router_id": "{{ router_id }}", + } + } + }, + }, + { + "name": "mtu_ignore", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + (\smtu-ignore(?P<mtu_ignore>))? + $""", + re.VERBOSE, + ), + + "setval": _tmplt_ospfv3_demand_mtu_ignore, + "result": { + "processes": { + "{{ pid }}": { + "mtu_ignore": "{{ True if mtu_ignore is defined }}", + } + } + }, + }, + { + "name": "flood_reduction", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sflood-reduction\s(?P<flood_reduction>\S+) + $""", + re.VERBOSE, + ), + + "setval": "flood-reduction {{ True if flood_reduction is defined }}", + "result": { + "processes": { + "{{ pid }}": { + "flood_reduction": "{{ flood_reduction }}", + } + } + }, + }, + { + "name": "loopback_stub_network", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sloopback(?P<loopback>) + \sstub-network\s(?P<stub_network>\S+) + $""", + re.VERBOSE, + ), + + "setval": "loopback stub-network {{ stub_network }}", + "result": { + "processes": { + "{{ pid }}": { + "loopback_stub_network": "{{ loopback_stub_network }}", + } + } + }, + }, + { + "name": "address_family_unicast", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \saddress-family(?P<address_family>) + \sipv4(?P<ipv4>) + \sunicast(?P<unicast>) + $""", + re.VERBOSE, + ), + + "setval": "address_family_unicast", + "result": { + "processes": { + "{{ pid }}": { + "address_family_unicast": "{{ True if unicast is defined }}", + } + } + }, + }, + { + "name": "default_weight", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sapply-weight(?P<apply_weight>) + \sdefault-weight(?P<default_weight>\s\d+) + $""", + re.VERBOSE, + ), + "setval": "apply-weight default-weight {{ default_weight }}", + "result": { + "processes": { + "{{ pid }}": { + "apply_weight": { + "default_weight": "{{ default_weight|int }}", + } + } + } + }, + }, + { + "name": "bandwidth", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sapply-weight(?P<apply_weight>) + \sbandwidth(?P<bandwidth>\s\d+)? + $""", + re.VERBOSE, + ), + "setval": "apply-weight bandwidth {{ bandwidth }}", + "result": { + "processes": { + "{{ pid }}": { + "apply_weight": { + "bandwidth": "{{ bandwidth|int }}", + } + } + } + }, + }, + { + "name": "authentication", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sauthentication(?P<auth>) + (\sdisable(?P<disable>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospfv3_authentication, + "result": { + "processes": { + "{{ pid }}": { + "authentication": { + "disable": "{{ True if disable is defined }}", + }, + } + } + }, + }, + { + "name": "authentication.ipsec", + "getval": re.compile( + r"""^router + \sospfv3\s(?P<pid>\S+) + \sauthentication(?P<auth>) + \sipsec(?P<ipsec>) + \sspi\s(?P<spi>\d+) + (\s(?P<algo_type>\S+))? + (\spassword\s(?P<password_key>\S+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_authentication_ipsec, + "result": { + "processes": { + "{{ pid }}": { + "authentication": { + "ipsec": { + "spi": "{{ spi }}", + "algorithim_type": "{{ algo_type }}", + "clear_key": "{{ clear_key }}", + "password_key": "{{ password_key }}", + "key": "{{ key }}", + } + }, + } + } + }, + }, + { + "name": "default_information_originate", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sdefault-information(?P<default_information>) + (\soriginate(?P<originate>))? + (\salways(?P<always>))? + (\smetric\s(?P<metric>\d+))? + (\smetric-type\s(?P<metric_type>\d+))? + (\sroute_policy\s(?P<route_policy>)\S+)? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_default_information, + "result": { + "processes": { + "{{ pid }}": { + "default_information_originate": { + "always": "{{ True if always is defined }}", + "metric": "{{ metric|int }}", + "metric_type": "{{ metric_type|int }}", + "route_policy": "{{ route_policy }}", + "set": "{{ True if default_information is defined and always is undefined and metric " + "is undefined and metric_type is undefined and route_policy is undefined }}" + }, + } + } + }, + }, + { + "name": "auto_cost", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sauto-cost(?P<auto_cost>) + (\sreference-bandwidth\s(?P<reference_bandwidth>\d+))? + (\sdisable(?P<disable>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_auto_cost, + "result": { + "processes": { + "{{ pid }}": { + "auto_cost": { + "disable": "{{ True if disable is defined }}", + "reference_bandwidth": "{{ reference_bandwidth|int }}", + }, + } + } + }, + }, + { + "name": "bfd.minimum_interval", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sbfd(?P<bfd>) + \sminimum-interval\s(?P<minimum_interval>\d+) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospfv3_bfd_minimum_interval, + "result": { + "processes": { + "{{ pid }}": { + "bfd": { + "minimum_interval": "{{ minimum_interval|int }}", + }, + } + } + }, + }, + { + "name": "bfd.multiplier", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sbfd(?P<bfd>) + \smultiplier\s(?P<multiplier>\d+) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospfv3_bfd_multiplier, + "result": { + "processes": { + "{{ pid }}": { + "bfd": { + "multiplier": "{{ multiplier|int }}", + }, + } + } + }, + }, + { + "name": "bfd.fast_detect", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sbfd(?P<bfd>) + \sfast-detect(?P<fast_detect>) + (\s(?P<disable>disable))? + (\s(?P<strict_mode>strict-mode))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_bfd_fast_detect, + "result": { + "processes": { + "{{ pid }}": { + "bfd": { + "fast_detect": { + "set": "{{ True if disable is undefined and strict_mode is undefined }}", + "strict_mode": "{{ True if strict_mode is defined }}", + }, + }, + } + }, + }, + }, + { + "name": "security", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \ssecurity(?P<security>) + \sttl(?P<ttl>)? + (\shops\s(?P<hops>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_security, + "result": { + "processes": { + "{{ pid }}": { + "security_ttl": { + "set": "{{ True if ttl is defined and hops is undefined }}", + "hops": "{{ hops }}", + }, + } + } + }, + }, + { + "name": "nsr", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \snsr(?P<nsr>) + \sdisable(?P<disable>)? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_nsr, + "result": { + "processes": { + "{{ pid }}": { + "nsr": { + "set": "{{ True if nsr is defined and disable is undefined }}", + "disable": "{{ True if disable is defined }}", + }, + } + } + }, + }, + { + "name": "protocol", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sprotocol(?P<protocol>) + \s(shutdown(?P<shutdown>)) + (\shost-mode(?P<host_mode>))? + (\son-reload\s(?P<on_reload>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_protocol, + "result": { + "processes": { + "{{ pid }}": { + "protocol_shutdown": { + "set": "{{ True if shutdown is defined and host-mode is undefined and on_reload is undefined }}", + "host_mode": "{{ True if host_mode is defined }}", + "on_reload": "{{ True if on_reload is defined }}", + }, + } + } + }, + }, + { + "name": "capability", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \scapability(?P<capability>) + (\stype7\s(?P<type7>\S+))? + $""", + re.VERBOSE, + ), + "setval": "capability type7 {{ type7 }}", + "result": { + "processes": { + "{{ pid }}": { + "capability": { + "type7": "{{ type7 }}" + }, + } + } + }, + }, + { + "name": "capability.opaque", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \scapability(?P<capability>)? + \sopaque(?P<opaque>) + (\sdisable(?P<disable>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_capability_opaque, + "result": { + "processes": { + "{{ pid }}": { + "capability": { + "opaque": { + "disable": "{{ True if disable is defined }}", + "set": "{{ True if opaque is defined and disable is undefined }}", + }, + }, + } + } + }, + }, + { + "name": "admin_distance", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sdistance\s(?P<value>d+) + \s(?P<source>\S+) + \s(?P<wildcard>\S+) + (\s(?P<access_list>\S+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_admin, + "result": { + "processes": { + "{{ pid }}": { + "distance": { + "admin_distance": { + "value": "{{ value|int }}", + "source": "{{ source }}", + "wildcard": "{{ wildcard }}", + "access_list": "{{ access_list }}", + } + }, + } + } + }, + }, + + { + "name": "ospf_distance", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sdistance(?P<value>) + \sospfv3(?P<ospf>) + (\sexternal\s(?P<external>\d+))? + (\sinter-area\s(?P<inter_area>\d+))? + (\sintra-area\s(?P<intra_area>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_ospf, + "result": { + "processes": { + "{{ pid }}": { + "distance": { + "ospf_distance": { + "external": "{{ external|int }}", + "inter_area": "{{ inter_area|int }}", + "intra_area": "{{ intra_area|int }}", + } + }, + } + } + }, + }, + { + "name": "area.default_cost", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sdefault-cost\s(?P<default_cost>\d+) + $""", + 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.dead_interval", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sdead-interval\s(?P<dead_interval>\d+) + $""", + re.VERBOSE, + ), + + "setval": "area {{ area_id }} dead-interval {{ dead_interval }}", + "compval": "dead_interval", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "dead_interval": "{{ dead_interval|int }}", + } + } + } + } + }, + }, + { + "name": "area.hello_interval", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \shello-interval\s(?P<hello_interval>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} hello-interval {{ hello_interval }}", + "compval": "hello_interval", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "hello_interval": "{{ hello_interval|int }}", + } + } + } + } + }, + }, + { + "name": "area.transmit_delay", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \stransmit-delay\s(?P<transmit_delay>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} transmit-delay {{ transmit_delay }}", + "compval": "transmit_delay", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "transmit_delay": "{{ transmit_delay|int }}", + } + } + } + } + }, + }, + { + "name": "area.cost", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \scost\s(?P<cost>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} cost {{ cost }}", + "compval": "cost", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "cost": "{{ cost|int }}", + } + } + } + } + }, + }, + { + "name": "area.priority", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \spriority\s(?P<priority>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} priority {{ priority }}", + "compval": "priority", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "priority": "{{ priority|int }}", + } + } + } + } + }, + }, + { + "name": "area.weight", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sweight\s(?P<weight>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} weight {{ weight }}", + "compval": "weight", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "weight": "{{ weight|int }}", + } + } + } + } + }, + }, + { + "name": "area.packet_size", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \spacket-size\s(?P<packet_size>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} packet-size {{ packet_size }}", + "compval": "packet_size", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "packet_size": "{{ packet_size|int }}", + } + } + } + } + }, + }, + { + "name": "area.summary_in", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \ssummary-in\s(?P<summary_in>\S+) + $""", + re.VERBOSE, + ), + + "setval": "area {{ area_id }} summary-in {{ summary_in }}", + "compval": "summary_in", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "summary_in": "{{ summary_in }}", + } + } + } + } + }, + }, + { + "name": "area.demand_circuit", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sdemand-circuit\s(?P<demand_circuit>\S+) + $""", + re.VERBOSE, + ), + + "setval": "area {{ area_id }} demand-circuit {{ demand_circuit }}", + "compval": "demand_circuit", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "demand_circuit": "{{ demand_circuit }}", + } + } + } + } + }, + }, + { + "name": "area.passive", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \spassive\s(?P<passive>\S+) + $""", + re.VERBOSE, + ), + + "setval": "area {{ area_id }} passive {{ passive }}", + "compval": "passive", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "passive": "{{ passive }}", + } + } + } + } + }, + }, + { + "name": "area.external_out", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sexternal-out\s(?P<external_out>\S+) + $""", + re.VERBOSE, + ), + + "setval": "area {{ area_id }} external-out {{ external_out }}", + "compval": "passive", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "external_out": "{{ external_out }}", + } + } + } + } + }, + }, + { + "name": "area.mtu_ignore", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \smtu-ignore\s(?P<mtu_ignore>\S+) + $""", + re.VERBOSE, + ), + + "setval": "area {{ area_id }} mtu-ignore {{ mtu_ignore }}", + "compval": "mtu_ignore", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "mtu_ignore": "{{ mtu_ignore }}", + } + } + } + } + }, + }, + { + "name": "area.authentication", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sauthentication(?P<auth>) + (\sdisable(?P<disable>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospfv3_area_authentication, + "compval": "authentication", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "authentication": { + "disable": "{{ True if disable is defined }}", + }, + } + } + } + } + }, + }, + { + "name": "area.authentication.ipsec", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sauthentication(?P<auth_key>) + \sipsec(?P<ipsec>) + \sspi\s(?P<spi>\d+) + (\s(?P<algo_type>\S+))? + (\spassword\s(?P<password_key>\S+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospfv3_area_authentication_ipsec, + "compval": "authentication.ipsec", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "authentication": { + "ipsec": { + "spi": "{{ spi }}", + "algorithim_type": "{{ algo_type }}", + "clear_key": "{{ clear_key }}", + "password_key": "{{ password_key }}", + "key": "{{ key }}", + }, + } + } + } + } + }, + }, + }, + { + "name": "area.mpls_traffic_eng", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \smpls(?P<mpls>) + \straffic-end(?P<traffic_eng>) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} mpls traffic-eng", + "compval": "mpls_traffic_eng", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "mpls": { + "traffic_eng": "{{ True if traffic_eng is defined }}", + }, + } + } + } + } + }, + }, + { + "name": "area.mpls_ldp", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \smpls(?P<mpls>) + (\sauto-config(?P<auto_config>))? + (\ssync(?P<sync>))? + (\ssync-igp-shortcuts(?P<syn_igp_shortcuts>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_mpls_ldp, + "compval": "mpls_ldp", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "mpls": { + "ldp": { + "auto_config": "{{ True if auto_config is defined }}", + "sync": "{{ True if sync is defined }}", + "sync_igp_shortcuts": "{{ True if sync_igp_shortcuts is defined }}", + + } + }, + } + } + } + } + }, + }, + { + "name": "area.bfd.minimum_interval", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sbfd(?P<bfd>) + \sminimum-interval\s(?P<minimum_interval>\d+) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospfv3_area_bfd_minimum_interval, + "compval": "bfd.minimum_interval", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "bfd": { + "minimum_interval": "{{ minimum_interval|int }}", + }, + } + } + } + } + }, + }, + { + "name": "area.bfd.multiplier", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sbfd(?P<bfd>) + \smultiplier\s(?P<multiplier>\d+) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospfv3_area_bfd_multiplier, + "compval": "bfd.multiplier", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "bfd": { + "multiplier": "{{ multiplier|int }}", + }, + } + } + } + } + }, + }, + { + "name": "area.bfd.fast_detect", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sbfd(?P<bfd>) + \sarea(?P<area_id>) + \sfast-detect(?P<fast_detect>) + (\s(?P<disable>disable))? + (\s(?P<strict_mode>strict-mode))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospfv3_area_bfd_fast_detect, + "compval": "bfd.fast_detect", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "bfd": { + "fast_detect": { + "set": "{{ True if disable is undefined and strict_mode is undefined }}", + "strict_mode": "{{ True if strict_mode is defined }}", + }, + }, + } + } + } + }, + }, + }, + { + "name": "area.stub", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \sstub(?P<nssa>) + (\sno-summary(?P<no_sum>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_stub, + "compval": "stub", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "area_id": "{{ area_id }}", + "stub": { + "set": "{{ True if stub is defined and no_summary is undefined }}", + "no_summary": "{{ True if no_summary is defined }}", + } + } + } + } + }, + }, + { + "name": "area.nssa", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \snssa(?P<nssa>) + (\sno-redistribution(?P<no_redis>))? + (\sno-summary(?P<no_sum>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa, + "compval": "nssa", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "area_id": "{{ area_id }}", + "nssa": { + "set": "{{ True if nssa is defined and no_summary is undefined and no_redis is undefined }}", + "no_summary": "{{ True if no_summary is defined }}", + "no_redistribution": "{{ True if no_redis is defined }}" + } + } + } + } + }, + }, + { + "name": "area.nssa.default_information_originate", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \snssa(?P<nssa>) + (\sno-redistribution(?P<no_redis>))? + (\sdefault-information-originate(?P<def_info_origin>))? + (\smetric\s(?P<metric>\d+))? + (\smetric-type\s(?P<metric_type>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa_def_info_origin, + "compval": "nssa.default_information_originate", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "default_information_originate": { + "metric": "{{ metric|int }}", + "metric_type": "{{ metric_type|int }}", + }, + } + } + } + } + }, + }, + }, + { + "name": "area.ranges", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \srange(?P<range>) + \s(?P<address>\S+) + (\sadvertise(?P<advertise>)) + (\snot-advertise(?P<not_advertise>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_ranges, + "compval": "ranges", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "ranges": [ + { + "address": "{{ address }}", + "advertise": "{{ True if advertise is defined }}", + "not_advertise": "{{ True if not_advertise is defined }}", + } + ], + } + } + } + } + }, + }, + { + "name": "area.nssa.translate", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \snssa(?P<nssa>) + \stranslate(?P<translate>) + \stype7(?P<type7>) + \salways\s(?P<always>) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa_translate, + "compval": "nssa.translate", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "area_id": "{{ area_id }}", + "nssa": { + "translate": { + "type7": { + "always": "{{ True if always is defined }}" + } + }, + } + } + } + } + }, + }, + { + "name": "virtual_link.hello_interval", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \svirtual-link\s(?P<id>\S+) + \shello-interval\s(?P<hello_interval>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} virtual-link {{ id }} hello-interval {{ hello_interval }}", + "compval": "hello_interval", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "virtual_link": { + "{{ id }}": + { + "id": "{{ id }}", + "hello_interval": "{{ hello_interval|int }}" + } + + } + } + } + } + } + }, + }, + { + "name": "virtual_link.dead_interval", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \svirtual-link\s(?P<id>\S+) + \sdead-interval\s(?P<dead_interval>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} virtual-link {{ id }} dead-interval {{ dead_interval }}", + "compval": "dead_interval", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "virtual_link": { + "{{ id }}": { + "id": "{{ id }}", + "dead_interval": "{{ dead_interval|int }}" + } + + } + } + } + } + } + }, + }, + { + "name": "virtual_link.retransmit_interval", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \svirtual-link\s(?P<id>\S+) + \sretransmit-interval\s(?P<retransmit_interval>\d+) + $""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} virtual-link {{ id }} retransmit-interval {{ retransmit_interval }}", + "compval": "retransmit_interval", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "virtual_link": { + "{{ id }}": { + "id": "{{ id }}", + "retransmit_interval": "{{ retransmit_interval|int }}" + } + } + + } + } + } + } + } + }, + { + "name": "virtual_link.authentication", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \savirtual-link\s(?P<id>\S+) + \sauthentication(?P<auth>) + (\skeychain\s(?P<keychain>\S+))? + (\snull(?P<no_auth>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_vlink_authentication, + "compval": "authentication", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "authentication": { + "no_auth": "{{ True if no_auth is defined }}", + "keychain": "{{ keychain }}", + }, + } + } + } + } + }, + }, + { + "name": "virtual_link.authentication_key", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \svirtual-link\s(?P<id>\S+) + \sauthentication-key(?P<auth_key>) + (\s(?P<password>\S+))? + (\sclear\s(?P<clear>)\S+)? + (\sencrypted(?P<encrypted>\S+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_vlink_authentication_key, + "compval": "authentication_key", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "virtual_link": { + "{{ id }}": { + "authentication_key": { + "clear": "{{ clear }}", + "encrypted": "{{ encrypted}}", + "password": "{{ password if clear is undefined and encrypted is undefined }}", + }, + } + } + } + } + } + } + }, + }, + { + "name": "virtual_link.authentication.message_digest", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sarea\s(?P<area_id>\S+) + \svirtual-link\s(?P<id>\S+) + \sauthentication(?P<auth>) + \smessage-digest(?P<md>) + \skeychain(?P<md_key>\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_vlink_authentication_md, + "compval": "authentication.message_digest", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "virtual_link": { + "{{ id }}": { + "authentication": { + "message_digest": { + "keychain": "{{ md_key }}", + } + }, + } + } + } + } + } + } + }, + }, + { + "name": "link_down_fast_detect", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \slink-down + \sfast-detect(?P<fast_detect>) + $""", + re.VERBOSE, + ), + "setval": "link-down fast-detect", + "result": { + "processes": { + "{{ pid }}": { + "link_down_fast_detect": "{{ True if fast_detect is defined }}", + } + } + }, + }, + { + "name": "nsr", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \snsr + \sdisable(?P<disable>) + $""", + re.VERBOSE, + ), + "setval": "nsr disable", + "result": { + "processes": { + "{{ pid }}": { + "disable": "{{ True if disable is defined }}", + } + } + }, + }, + { + "name": "database_filter", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sdatabase-filter + \sall + \sout\s(?P<outing>\S+) + $""", + re.VERBOSE, + ), + + "setval": "database-filter all out {{ outing }}", + "result": { + "processes": { + "{{ pid }}": { + "outing": "{{ outing }}" + } + } + }, + }, + { + "name": "distribute_link_state", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sdistribute(?P<distribute>) + \slink-state(?P<link_state>) + (\sinstance-id(?P<inst_id>\d+))? + (\sthrottle(?P<throttle>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_adjacency_distribute_bgp_state, + "result": { + "processes": { + "{{ pid }}": { + "distribute_link_list": { + "instance_id": "{{ inst_id|int }}", + "throttle": "{{ throttle }}", + }, + } + } + }, + }, + { + "name": "distribute_bgp_ls", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sdistribute(?P<distribute>) + \sbgp-ls(?P<bgp_ls>) + (\sinstance-id(?P<inst_id>\d+))? + (\sthrottle(?P<throttle>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_adjacency_distribute_bgp_state, + "result": { + "processes": { + "{{ pid }}": { + "distribute_bgp_ls": { + "instance_id": "{{ inst_id|int }}", + "throttle": "{{ throttle }}", + }, + } + } + }, + }, + { + "name": "log_adjacency", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \slog(?P<security>) + \sadjacency(?P<adjacency>)? + (\schanges(?P<changes>))? + (\sdisable(?P<disable>))? + (\sdetails(?P<details>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_log_adjacency, + "result": { + "processes": { + "{{ pid }}": { + "log_adjacency_changes": { + "set": "{{ True changes id defined and disable is undefined and detail is undefined }}", + "disable": "{{ True if disable is defined }}", + "details": "{{ True if details is defined }}", + }, + } + } + }, + }, + { + "name": "max_lsa", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + (\smax-lsa\s(?P<threshold>\d+))? + (\swarning-only\s(?P<warning_only>\d+)? + (\signore-time\s(?P<ignore_time>\d+))? + (\signore-count\s(?P<ignore_count>\d+))? + (\sreset-time\s(?P<reset_time>)\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_log_max_lsa, + "result": { + "processes": { + "{{ pid }}": { + "max_lsa": { + "threshold": "{{ threshold|int }}", + "warning_only": "{{ warning_only|int }}", + "ignore_time": "{{ ignore_time|int }}", + "ignore_count": "{{ ignore_count|int }}", + "reset_time": "{{ reset_time|int }}", + }, + } + } + }, + }, + { + "name": "max_metric", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \smax-metric + \s*(?P<router_lsa>) + (\s*external-lsa(?P<external_lsa>))? + (\s(?P<max_metric_value>\d+))? + \s*(?P<include_stub>include-stub)* + \s*(?P<on_startup>on-startup)* + \s*(?P<wait_period>\d+)* + \s*(wait-for\sbgp)* + \s*(?P<bgp_asn>\d+)* + \s*(?P<summary_lsa>summary-lsa)* + \s*(?P<sum_lsa_max_metric_value>\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_metric, + "remval": "max-metric router-lsa", + "result": { + "processes": { + '{{ "pid" }}': { + "max_metric": { + "router_lsa": { + "set": "{{ True if router_lsa is defined and external_lsa is undefined else None }}", + "external_lsa": { + "set": "{{ True if external_lsa is defined and max_metric_value is undefined else None }}", + "max_metric_value": "{{ max_metric_value }}", + }, + "include_stub": "{{ not not include_stub }}", + "on_startup": { + "set": "{{ True if on_startup is defined and (wait_period and bgp_asn) is undefined else None }}", + "wait_period": "{{ wait_period }}", + "wait_for_bgp_asn": "{{ bgp_asn }}", + }, + "summary_lsa": { + "set": "{{ True if summary_lsa is defined and sum_lsa_max_metric_value is undefined else None }}", + "max_metric_value": "{{ sum_lsa_max_metric_value }}", + }, + } + } + } + } + }, + }, + { + "name": "mpls_ldp", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \smpls(?P<mpls>) + (\sauto-config(?P<auto_config>))? + (\ssync(?P<sync>))? + (\ssync-igp-shortcuts(?P<syn_igp_shortcuts>))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_mpls_ldp, + "compval": "mpls_ldp", + "result": { + "processes": { + "{{ pid }}": { + "mpls": { + "ldp": { + "auto_config": "{{ True if auto_config is defined }}", + "sync": "{{ True if sync is defined }}", + "sync_igp_shortcuts": "{{ True if sync_igp_shortcuts is defined }}", + } + }, + } + } + }, + }, + { + "name": "microloop_avoidance", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \smicroloop(?P<microloop>) + \savoidance(?P<avoidance>) + (\s(?P<protected>protected))? + (\s(?P<segment_routing>segment-routing))? + (\srib-update-delay\s(?P<rib_update_delay>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_microloop_avoidance, + "compval": "microloop_avoidance", + "result": { + "processes": { + "{{ pid }}": { + "microloop_avoidance": { + "protected": "{{ True if protected is defined }}", + "segment_routing": "{{ True if segment_routing is defined }}", + "rib_update_delay": "{{ rib_update_delay }}", + }, + } + } + }, + }, + { + "name": "mpls_traffic_eng", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \smpls(?P<mpls>) + \straffic-end(?P<traffic_eng>) + (\sautoroute-exclude(?P<autoroute>))? + (\sroute-policy(?P<route_policy>\S+))? + (\s(?P<igp_intact>igp_intact))? + (\s(?P<ldp_sync_update>ldp-sync-update))? + (\s(?P<multicast_intact>multicast-intact))? + (\srouter-id\s(?P<router_id>\S+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_mpls_traffic_eng, + "result": { + "processes": { + "{{ pid }}": { + "mpls": { + "autoroute_exclude": { + "route_policy": "{{ route_policy }}" + }, + "igp_intact": "{{ True if igp_intact is defined }}", + "ldp_sync_update": "{{ True if ldp_sync_update is defined }}", + "multicast_intact": "{{ True if multicast_intact is defined }}", + "router_id": "{{ router_id }}" + }, + } + } + }, + }, + { + "name": "prefix_suppression", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sprefix-suppression(?P<prefix_suppression>) + (\s(?P<secondary_address>secondary-address))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_prefix_suppression, + "result": { + "processes": { + "{{ pid }}": { + "prefix_suppression": { + "set": "{{ True if prefix_suppression is defined and secondary_address is undefined }}", + "secondary_address": "{{ True if secondary_address is defined }}", + }, + } + } + }, + }, + { + "name": "protocol_shutdown", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \sprotocol-shutdown(?P<protocol_shutdown>) + (\s(?P<host_mode>host-mode))? + (\s(?P<on_reload>on-reload))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_protocol_shutdown, + "result": { + "processes": { + "{{ pid }}": { + "protocol_shutdown": { + "set": "{{ True if protocol_shutdown is defined and host_mode is undefined and on_reload is undefined }}", + "host_mode": "{{ True if host_mode is defined }}", + "on_reload": "{{ True if on_reload is defined }}", + }, + } + } + }, + }, + { + "name": "timers.lsa", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \stimers + \slsa + (\sgroup-pacing\s(?P<group_pacing>\d+))? + (\smin-arrival\s(?P<min_arrival>\d+))? + (\srefresh\s(?P<refresh>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_timers_lsa, + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "lsa": { + "group_pacing": "{{ group_pacing|int }}", + "min_arrival": "{{ min_arrival|int }}", + "refresh": "{{ refresh|int }}", + }, + } + } + } + }, + }, + { + "name": "timers.graceful_shutdown", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \stimers + \sgraceful_shutdown + (\sinitial delay\s(?P<initial_delay>\d+))? + (\sretain routes\s(?P<retain_routes>\d+))? + $""", + re.VERBOSE, + ), + "setval": _tmplt_timers_graceful_shutdown, + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "graceful_shutdown": { + "initial_delay": "{{ initial_delay|int }}", + "retain_routes": "{{ retain_routes|int }}", + }, + } + } + } + }, + }, + { + "name": "throttle.spf", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \stimers + \sthrottle + \sspf + (\s(?P<change_delay>\d+)) + (\s(?P<second_delay>\d+)) + (\s(?P<max_wait>\d+)) + $""", + re.VERBOSE, + ), + "setval": "timers throttle spf {{ throttle.spf.change_delay }} {{ throttle.spf.second_delay }} {{ throttle.spf.max_wait }}", + "compval": "throttle.lsa_all", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "lsa_all": { + "initial_delay": "{{ initial_delay }}", + "min_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + }, + } + } + } + } + }, + }, + { + "name": "throttle.lsa_all", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \stimers + \sthrottle + \slsa + \sall + (\s(?P<initial_delay>\d+)) + (\s(?P<min_delay>\d+)) + (\s(?P<max_delay>\d+)) + $""", + re.VERBOSE, + ), + "setval": "timers throttle lsa all {{ throttle.lsa_all.initial_delay }} {{ throttle.lsa_all.min_delay }} {{ throttle.lsa_all.max_delay }}", + "compval": "throttle.lsa_all", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "lsa_all": { + "initial_delay": "{{ initial_delay }}", + "min_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + }, + } + } + } + } + }, + }, + { + "name": "throttle.fast_reroute", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \stimers + \sthrottle + \sfast-reroute\s(?P<fast_reroute>\d+) + $""", + re.VERBOSE, + ), + "setval": "timers throttle fast-reroute {{ fast_reroute }}", + "compval": "throttle.fast_reroute", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "fast_reroute": "{{ fast_reroute }}", + } + } + } + } + }, + }, + { + "name": "timers.pacing_flood", + "getval": re.compile( + r""" + ^router + \sospfv3\s(?P<pid>\S+) + \stimers + \spacing + \sflood\s(?P<pacing_flood>\d+) + $""", + re.VERBOSE, + ), + "setval": "timers pacing flood {{ pacing_flood }}", + "compval": "timers.pacing_flood", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "pacing_flood": "{{ pacing_flood }}", + + } + } + } + }, + }, + ] + # fmt: on diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/utils/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/utils/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/utils/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/utils/utils.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/utils/utils.py new file mode 100644 index 00000000..059c8167 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/module_utils/network/iosxr/utils/utils.py @@ -0,0 +1,391 @@ +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# utils + + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +from ansible.module_utils._text import to_text +from ansible_collections.ansible.netcommon.plugins.module_utils.compat import ( + ipaddress, +) +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_diff, + is_masklen, + to_netmask, + search_obj_in_list, +) + + +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 dict_to_set(sample_dict): + # Generate a set with passed dictionary for comparison + test_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() + test_key_dict = dict() + name = want.get("name") + if name: + test_dict["name"] = name + diff_ip = False + want_ip = "" + for k, v in iteritems(want): + if isinstance(v, dict): + for key, value in iteritems(v): + if value is None and k in have and key in have.get(k): + dict_val = have.get(k).get(key) + test_key_dict.update({key: dict_val}) + test_dict.update({k: test_key_dict}) + if isinstance(v, list) and isinstance(v[0], dict): + for key, value in iteritems(v[0]): + if value is None and k in have and key in have.get(k): + dict_val = have.get(k).get(key) + test_key_dict.update({key: dict_val}) + 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") + for each in have_ip: + if len(want_ip) > 1 and each.get("secondary"): + have_ip = each.get("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}) + # Checks if want doesn't have secondary IP but have has secondary IP set + elif have.get("ipv4"): + if [ + True + for each_have in have.get("ipv4") + if "secondary" in each_have + ]: + test_dict.update({"ipv4": {"secondary": True}}) + if k == "l2protocol": + diff = True + h_proto = have.get("l2protocol") + w_proto = want.get("l2protocol") + if h_proto: + if w_proto: + for h in h_proto: + for w in w_proto: + if h == w: + diff = False + if not diff: + break + else: + diff = True + if diff: + test_dict.update({k: v}) + 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 flatten_dict(x): + result = {} + if not isinstance(x, dict): + return result + + for key, value in iteritems(x): + if isinstance(value, dict): + result.update(flatten_dict(value)) + else: + result[key] = value + + return result + + +def dict_delete(base, comparable): + """ + + This function generates a dict containing key, value pairs for keys + that are present in the `base` dict but not present in the `comparable` + dict. + + :param base: dict object to base the diff on + :param comparable: dict object to compare against base + + :returns: new dict object with key, value pairs that needs to be deleted. + + """ + to_delete = dict() + + for key in base: + if isinstance(base[key], dict): + sub_diff = dict_delete(base[key], comparable.get(key, {})) + if sub_diff: + to_delete[key] = sub_diff + else: + if key not in comparable: + to_delete[key] = base[key] + + return to_delete + + +def pad_commands(commands, interface): + commands.insert(0, "interface {0}".format(interface)) + + +def diff_list_of_dicts(w, h, key="member"): + """ + Returns a list containing diff between + two list of dictionaries + """ + if not w: + w = [] + if not h: + h = [] + + diff = [] + for w_item in w: + h_item = search_obj_in_list(w_item[key], h, key=key) or {} + d = dict_diff(h_item, w_item) + if d: + if key not in d.keys(): + d[key] = w_item[key] + diff.append(d) + + return diff + + +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 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("fa"): + if_type = "FastEthernet" + elif name.lower().startswith("fo"): + if_type = "FortyGigE" + elif name.lower().startswith("te"): + if_type = "TenGigE" + elif name.lower().startswith("twe"): + if_type = "TwentyFiveGigE" + elif name.lower().startswith("hu"): + if_type = "HundredGigE" + elif name.lower().startswith("vl"): + if_type = "Vlan" + elif name.lower().startswith("lo"): + if_type = "Loopback" + elif name.lower().startswith("be"): + if_type = "Bundle-Ether" + elif name.lower().startswith("bp"): + if_type = "Bundle-POS" + 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("FA"): + return "FastEthernet" + elif interface.upper().startswith("FO"): + return "FortyGigE" + elif interface.upper().startswith("ET"): + return "Ethernet" + elif interface.upper().startswith("LO"): + return "Loopback" + elif interface.upper().startswith("BE"): + return "Bundle-Ether" + 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("PRE"): + return "preconfigure" + else: + return "unknown" + + +def isipaddress(data): + """ + Checks if the passed string is + a valid IPv4 or IPv6 address + """ + isipaddress = True + + try: + ipaddress.ip_address(data) + except ValueError: + isipaddress = False + + return isipaddress + + +def is_ipv4_address(data): + """ + Checks if the passed string is + a valid IPv4 address + """ + if "/" in data: + data = data.split("/")[0] + + if not isipaddress(to_text(data)): + raise ValueError("{0} is not a valid IP address".format(data)) + + return ipaddress.ip_address(to_text(data)).version == 4 + + +def prefix_to_address_wildcard(prefix): + """ Converts a IPv4 prefix into address and + wildcard mask + + :returns: IPv4 address and wildcard mask + """ + wildcard = [] + + subnet = to_text(ipaddress.IPv4Network(to_text(prefix)).netmask) + + for x in subnet.split("."): + component = 255 - int(x) + wildcard.append(str(component)) + + wildcard = ".".join(wildcard) + + return prefix.split("/")[0], wildcard diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_acl_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_acl_interfaces.py new file mode 100644 index 00000000..378b845c --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_acl_interfaces.py @@ -0,0 +1,651 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for iosxr_acl_interfaces +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: iosxr_acl_interfaces +short_description: ACL interfaces resource module +description: +- This module manages adding and removing Access Control Lists (ACLs) from interfaces + on devices running IOS-XR software. +version_added: 1.0.0 +author: Nilashish Chakraborty (@NilashishC) +options: + config: + description: A dictionary of ACL options for interfaces. + type: list + elements: dict + suboptions: + name: + description: + - Name/Identifier for the interface + type: str + required: true + access_groups: + type: list + elements: dict + description: + - Specifies ACLs attached to the interfaces. + suboptions: + afi: + description: + - Specifies the AFI for the ACL(s) to be configured on this interface. + type: str + choices: + - ipv4 + - ipv6 + required: true + acls: + type: list + description: + - Specifies the ACLs for the provided AFI. + elements: dict + suboptions: + name: + description: + - Specifies the name of the IPv4/IPv6 ACL for the interface. + type: str + required: true + direction: + description: + - Specifies the direction of packets that the ACL will be applied + on. + type: str + choices: + - in + - out + 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-XR device + by executing the command B(show running-config 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. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged + +""" +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:ios#sh running-config interface +# Wed Jan 15 12:22:32.911 UTC +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! + +- name: Merge the provided configuration with the existing running configuration + cisco.iosxr.iosxr_acl_interfaces: + config: + - name: GigabitEthernet0/0/0/0 + access_groups: + - afi: ipv4 + acls: + - name: acl_1 + direction: in + - name: acl_2 + direction: out + - afi: ipv6 + acls: + - name: acl6_1 + direction: in + - name: acl6_2 + direction: out + + - name: GigabitEthernet0/0/0/1 + access_groups: + - afi: ipv4 + acls: + - name: acl_1 + direction: out + state: merged + +# After state: +# ------------- +# +# RP/0/RP0/CPU0:ios#sh running-config interface +# Wed Jan 15 12:27:49.378 UTC +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ipv4 access-group acl_1 ingress +# ipv4 access-group acl_2 egress +# ipv6 access-group acl6_1 ingress +# ipv6 access-group acl6_2 egress +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ipv4 access-group acl_1 egress +# ! + +# Using merged to update interface ACL configuration + +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:ios#sh running-config interface +# Wed Jan 15 12:27:49.378 UTC +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ipv4 access-group acl_1 ingress +# ipv4 access-group acl_2 egress +# ipv6 access-group acl6_1 ingress +# ipv6 access-group acl6_2 egress +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ipv4 access-group acl_1 egress +# ! +# + +- name: Update acl_interfaces configuration using merged + cisco.iosxr.iosxr_acl_interfaces: + config: + - name: GigabitEthernet0/0/0/1 + access_groups: + - afi: ipv4 + acls: + - name: acl_2 + direction: out + - name: acl_1 + direction: in + state: merged + +# After state: +# ------------- +# +# RP/0/RP0/CPU0:ios#sh running-config interface +# Wed Jan 15 12:27:49.378 UTC +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ipv4 access-group acl_1 ingress +# ipv4 access-group acl_2 egress +# ipv6 access-group acl6_1 ingress +# ipv6 access-group acl6_2 egress +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ipv4 access-group acl_1 ingress +# ipv4 access-group acl_2 egress +# ! +# + +# Using replaced + +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:ios#sh running-config interface +# Wed Jan 15 12:34:56.689 UTC +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ipv4 access-group acl_1 ingress +# ipv4 access-group acl_2 egress +# ipv6 access-group acl6_1 ingress +# ipv6 access-group acl6_2 egress +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ipv4 access-group acl_1 egress +# ! + +- name: Replace device configurations of listed interface with provided configurations + cisco.iosxr.iosxr_acl_interfaces: + config: + - name: GigabitEthernet0/0/0/0 + access_groups: + - afi: ipv6 + acls: + - name: acl6_3 + direction: in + state: replaced + +# After state: +# ------------- +# +# RP/0/RP0/CPU0:ios#sh running-config interface +# Wed Jan 15 12:34:56.689 UTC +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ipv6 access-group acl6_3 ingress +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ipv4 access-group acl_1 egress +# ! +# + +# Using overridden + +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:ios#sh running-config interface +# Wed Jan 15 12:34:56.689 UTC +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ipv4 access-group acl_1 ingress +# ipv4 access-group acl_2 egress +# ipv6 access-group acl6_1 ingress +# ipv6 access-group acl6_2 egress +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ipv4 access-group acl_1 egress +# ! +# + +- name: Overridde all interface ACL configuration with provided configuration + cisco.iosxr.iosxr_acl_interfaces: + config: + - name: GigabitEthernet0/0/0/1 + access_groups: + - afi: ipv4 + acls: + - name: acl_2 + direction: in + - afi: ipv6 + acls: + - name: acl6_3 + direction: out + state: overridden + +# After state: +# ------------- +# +# RP/0/RP0/CPU0:ios#sh running-config interface +# Wed Jan 15 12:34:56.689 UTC +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ipv4 access-group acl_2 ingress +# ipv6 access-group acl6_3 egress +# ! +# + +# Using 'deleted' to delete all ACL attributes of a single interface + +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:ios#sh running-config interface +# Wed Jan 15 12:34:56.689 UTC +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ipv4 access-group acl_1 ingress +# ipv4 access-group acl_2 egress +# ipv6 access-group acl6_1 ingress +# ipv6 access-group acl6_2 egress +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ipv4 access-group acl_1 egress +# ! +# + +- name: Delete all ACL attributes of GigabitEthernet0/0/0/1 + cisco.iosxr.iosxr_acl_interfaces: + config: + - name: GigabitEthernet0/0/0/1 + state: deleted + +# After state: +# ------------- +# +# RP/0/RP0/CPU0:ios#sh running-config interface +# Wed Jan 15 12:34:56.689 UTC +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ipv4 access-group acl_1 ingress +# ipv4 access-group acl_2 egress +# ipv6 access-group acl6_1 ingress +# ipv6 access-group acl6_2 egress +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# + +# Using 'deleted' to remove all ACLs attached to all the interfaces in the device + +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:ios#sh running-config interface +# Wed Jan 15 12:34:56.689 UTC +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ipv4 access-group acl_1 ingress +# ipv4 access-group acl_2 egress +# ipv6 access-group acl6_1 ingress +# ipv6 access-group acl6_2 egress +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ipv4 access-group acl_1 egress +# ! +# + +- name: Delete all ACL interfaces configuration from the device + cisco.iosxr.iosxr_acl_interfaces: + state: deleted + +# After state: +# ------------- +# +# RP/0/RP0/CPU0:ios#sh running-config interface +# Wed Jan 15 12:34:56.689 UTC +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# + +# Using parsed + +# parsed.cfg +# ------------ +# +# interface MgmtEth0/RP0/CPU0/0 +# ipv4 address dhcp +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ipv4 access-group acl_1 ingress +# ipv4 access-group acl_2 egress +# ipv6 access-group acl6_1 ingress +# ipv6 access-group acl6_2 egress +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ipv4 access-group acl_1 egress +# ! + +# - name: Convert ACL interfaces config to argspec without connecting to the appliance +# cisco.iosxr.iosxr_acl_interfaces: +# running_config: "{{ lookup('file', './parsed.cfg') }}" +# state: parsed + + +# Task Output (redacted) +# ----------------------- + +# "parsed": [ +# { +# "name": "MgmtEth0/RP0/CPU0/0" +# }, +# { +# "access_groups": [ +# { +# "acls": [ +# { +# "direction": "in", +# "name": "acl_1" +# }, +# { +# "direction": "out", +# "name": "acl_2" +# } +# ], +# "afi": "ipv4" +# }, +# { +# "acls": [ +# { +# "direction": "in", +# "name": "acl6_1" +# }, +# { +# "direction": "out", +# "name": "acl6_2" +# } +# ], +# "afi": "ipv6" +# } +# ], +# "name": "GigabitEthernet0/0/0/0" +# }, +# { +# "access_groups": [ +# { +# "acls": [ +# { +# "direction": "out", +# "name": "acl_1" +# } +# ], +# "afi": "ipv4" +# } +# ], +# "name": "GigabitEthernet0/0/0/1" +# } +# ] +# } + + +# Using gathered + +- name: Gather ACL interfaces facts using gathered state + cisco.iosxr.iosxr_acl_interfaces: + state: gathered + + +# Task Output (redacted) +# ----------------------- +# +# "gathered": [ +# { +# "name": "MgmtEth0/RP0/CPU0/0" +# }, +# { +# "access_groups": [ +# { +# "acls": [ +# { +# "direction": "in", +# "name": "acl_1" +# }, +# { +# "direction": "out", +# "name": "acl_2" +# } +# ], +# "afi": "ipv4" +# } +# "name": "GigabitEthernet0/0/0/0" +# }, +# { +# "access_groups": [ +# { +# "acls": [ +# { +# "direction": "in", +# "name": "acl6_1" +# } +# ], +# "afi": "ipv6" +# } +# "name": "GigabitEthernet0/0/0/1" +# } +# ] + + +# Using rendered + +- name: Render platform specific commands from task input using rendered state + cisco.iosxr.iosxr_acl_interfaces: + config: + - name: GigabitEthernet0/0/0/0 + access_groups: + - afi: ipv4 + acls: + - name: acl_1 + direction: in + - name: acl_2 + direction: out + state: rendered + +# Task Output (redacted) +# ----------------------- + +# "rendered": [ +# "interface GigabitEthernet0/0/0/0", +# "ipv4 access-group acl_1 ingress", +# "ipv4 access-group acl_2 egress" +# ] +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: + - "interface GigabitEthernet0/0/0/1" + - "ipv4 access-group acl_1 ingress" + - "ipv4 access-group acl_2 egress" + - "ipv6 access-group acl6_1 ingress" + - "interface GigabitEthernet0/0/0/2" + - "no ipv4 access-group acl_3 ingress" + - "ipv4 access-group acl_4 egress" +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.acl_interfaces.acl_interfaces import ( + Acl_interfacesArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.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/iosxr/plugins/modules/iosxr_acls.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_acls.py new file mode 100644 index 00000000..ef31676c --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_acls.py @@ -0,0 +1,1451 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for iosxr_acls +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: iosxr_acls +short_description: ACLs resource module +description: +- This module manages Access Control Lists (ACLs) on devices running IOS-XR. +version_added: 1.0.0 +author: Nilashish Chakraborty (@NilashishC) +options: + config: + description: A list of dictionaries specifying ACL configurations. + type: list + elements: dict + suboptions: + afi: + description: + - The Address Family Indicator (AFI) for the Access Control Lists (ACL). + type: str + required: true + choices: + - ipv4 + - ipv6 + acls: + description: + - A list of Access Control Lists (ACLs). + type: list + elements: dict + suboptions: + name: + description: + - The name of the Access Control List (ACL). + type: str + aces: + description: + - List of Access Control Entries (ACEs) for this Access Control List (ACL). + type: list + elements: dict + suboptions: + sequence: + description: + - Sequence number for the Access Control Entry (ACE). + type: int + grant: + description: + - Forward or drop packets matching the Access Control Entry (ACE). + type: str + choices: + - permit + - deny + remark: + description: + - Comments or a description for the access list. + type: str + line: + description: + - An ACE excluding the sequence number. + - This key is mutually exclusive with all the other attributes except + 'sequence'. + - When used with other attributes, the value of this key will get + precedence and the other keys will be ignored. + - This should only be used when an attribute doesn't exist in the + argspec but is valid for the device. + - For fact gathering, any ACE that is not fully parsed, will show + up as a value of this attribute, excluding the sequence number, + which will be populated as value of the sequence key. + type: str + aliases: + - ace + source: + description: + - Specifies the packet source. + type: dict + suboptions: + host: + description: + - The host IP address to match. + type: str + address: + description: + - The source IP address to match. + type: str + wildcard_bits: + description: + - The Wildcard bits to apply to source address. + type: str + any: + description: + - Match any source address. + type: bool + prefix: + description: + - Source network prefix. + type: str + port_protocol: + description: + - Specify the source port or protocol. + 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: + - Match only packets in the range of port numbers + type: dict + suboptions: + start: + description: + - Specify the start of the port range + type: str + end: + description: + - Specify the end of the port range + type: str + destination: + description: + - Specifies the packet destination. + type: dict + suboptions: + host: + description: + - The host IP address to match. + type: str + address: + description: + - The destination IP address to match. + type: str + wildcard_bits: + description: + - The Wildcard bits to apply to destination address. + type: str + any: + description: + - Match any destination address. + type: bool + prefix: + description: + - Destination network prefix. + type: str + port_protocol: + description: + - Specify the source port or protocol. + 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: + - Match only packets in the range of port numbers + type: dict + suboptions: + start: + description: + - Specify the start of the port range + type: str + end: + description: + - Specify the end of the port range + type: str + protocol: + description: + - Specify the protocol to match. + - Refer to vendor documentation for valid values. + type: str + protocol_options: + description: + - Additional suboptions for the protocol. + type: dict + suboptions: + icmpv6: + description: Internet Control Message Protocol settings for IPv6. + type: dict + suboptions: + address_unreachable: + description: Address Unreachable + type: bool + administratively_prohibited: + description: Administratively Prohibited + type: bool + beyond_scope_of_source_address: + description: Administratively Prohibited + type: bool + destination_unreachable: + description: Destination Unreachable + type: bool + echo: + description: Echo + type: bool + echo_reply: + description: Echo Reply + type: bool + erroneous_header_field: + description: Erroneous Header Field + type: bool + group_membership_query: + description: Group Membership Query + type: bool + group_membership_report: + description: Group Membership Report + type: bool + group_membership_termination: + description: Group Membership Termination + type: bool + host_unreachable: + description: Host Unreachable + type: bool + nd_na: + description: Neighbor Discovery - Neighbor Advertisement + type: bool + nd_ns: + description: Neighbor Discovery - Neighbor Solicitation + type: bool + neighbor_redirect: + description: Neighbor Redirect + type: bool + no_route_to_destination: + description: No Route To Destination + type: bool + node_information_request_is_refused: + description: Node Information Request Is Refused + type: bool + node_information_successful_reply: + description: Node Information Successful Reply + type: bool + packet_too_big: + description: Packet Too Big + type: bool + parameter_problem: + description: Parameter Problem + type: bool + port_unreachable: + description: Port Unreachable + type: bool + query_subject_is_IPv4address: + description: Query Subject Is IPv4 address + type: bool + query_subject_is_IPv6address: + description: Query Subject Is IPv6 address + type: bool + query_subject_is_domainname: + description: Query Subject Is Domain name + type: bool + reassembly_timeout: + description: Reassembly Timeout + type: bool + redirect: + description: Redirect + type: bool + router_advertisement: + description: Router Advertisement + type: bool + router_renumbering: + description: Router Renumbering + type: bool + router_solicitation: + description: Router Solicitation + type: bool + rr_command: + description: RR Command + type: bool + rr_result: + description: RR Result + type: bool + rr_seqnum_reset: + description: RR Seqnum Reset + type: bool + time_exceeded: + description: Time Exceeded + type: bool + ttl_exceeded: + description: TTL Exceeded + type: bool + unknown_query_type: + description: Unknown Query Type + type: bool + unreachable: + description: Unreachable + type: bool + unrecognized_next_header: + description: Unrecognized Next Header + type: bool + unrecognized_option: + description: Unrecognized Option + type: bool + whoareyou_reply: + description: Whoareyou Reply + type: bool + whoareyou_request: + description: Whoareyou Request + type: bool + icmp: + description: Internet Control Message Protocol settings. + 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 requests + 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 + 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 + igmp: + description: Internet Group Management Protocol (IGMP) settings. + type: dict + suboptions: + dvmrp: + description: Match Distance Vector Multicast Routing Protocol + type: bool + host_query: + description: Match Host Query + type: bool + host_report: + description: Match Host Report + type: bool + pim: + description: Match Protocol Independent Multicast + type: bool + trace: + description: Multicast trace + type: bool + mtrace: + description: Match mtrace + type: bool + mtrace_response: + description: Match mtrace response + type: bool + dscp: + description: + - Match packets with given DSCP value. + type: dict + suboptions: + eq: + description: Match only packets on a given dscp value + type: str + gt: + description: Match only packets with a greater dscp value + type: str + lt: + description: Match only packets with a lower dscp value + type: str + neq: + description: Match only packets not on a given dscp value + type: str + range: + description: Match only packets in the range of dscp values + type: dict + suboptions: + start: + description: Start of the dscp range + type: str + end: + description: End of the dscp range + type: str + fragments: + description: + - Check non-intial fragments. + type: bool + packet_length: + description: + - Match packets given packet length. + type: dict + suboptions: + eq: + description: Match only packets on a given packet length + type: int + gt: + description: Match only packets with a greater packet length + type: int + lt: + description: Match only packets with a lower packet length + type: int + neq: + description: Match only packets not on a given packet length + type: int + range: + description: Match only packets in the range of packet lengths + type: dict + suboptions: + start: + description: Start of the packet length range + type: int + end: + description: End of the packet length range + type: int + precedence: + description: Match packets with given precedence value + type: str + ttl: + description: Match against specified TTL value. + type: dict + suboptions: + eq: + description: Match only packets with exact TTL value. + type: int + gt: + description: Match only packets with a greater TTL value. + type: int + lt: + description: Match only packets with a lower TTL value. + type: int + neq: + description: Match only packets that won't have the given TTL + value. + type: int + range: + description: Match only packets in the range of given TTL values. + type: dict + suboptions: + start: + description: Start of the TTL range. + type: int + end: + description: End of the TTL range. + type: int + log: + description: + - Enable/disable log matches against this entry. + type: bool + log_input: + description: + - Enable/disable log matches against this entry, including input interface. + type: bool + icmp_off: + description: + - Enable/disable the ICMP message for this entry. + type: bool + capture: + description: + - Capture matched packet. + type: bool + destopts: + description: + - Match if destination opts header is present. + type: bool + authen: + description: + - Match if authentication header is present. + type: bool + routing: + description: + - Match if routing header is present. + type: bool + hop_by_hop: + description: + - Match if hop-by-hop opts header is present. + type: bool + 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 B(show running-config router static). + type: str + state: + description: + - The state the configuration should be left in. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged + +""" +EXAMPLES = """ +# Using merged to add new ACLs + +# Before state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 05:07:45.767 UTC +# RP/0/RP0/CPU0:ios# + +- name: Merge the provided configuration with the exisiting running configuration + cisco.iosxr.iosxr_acls: + config: + - afi: ipv6 + acls: + - name: acl6_1 + aces: + - sequence: 10 + grant: deny + protocol: tcp + source: + prefix: 2001:db8:1234::/48 + port_protocol: + range: + start: ftp + end: telnet + destination: + any: true + protocol_options: + tcp: + syn: true + ttl: + range: + start: 180 + end: 250 + routing: true + authen: true + log: true + + - sequence: 20 + grant: permit + protocol: icmpv6 + source: + any: true + destination: + any: true + protocol_options: + icmpv6: + router_advertisement: true + precedence: network + destopts: true + + - afi: ipv4 + acls: + - name: acl_1 + aces: + - sequence: 16 + remark: TEST_ACL_1_REMARK + + - sequence: 21 + grant: permit + protocol: tcp + source: + host: 192.0.2.10 + port_protocol: + range: + start: pop3 + end: 121 + destination: + address: 198.51.100.0 + wildcard_bits: 0.0.0.15 + protocol_options: + tcp: + rst: true + + - sequence: 23 + grant: deny + protocol: icmp + source: + any: true + destination: + prefix: 198.51.100.0/28 + protocol_options: + icmp: + reassembly_timeout: true + dscp: + lt: af12 + + - name: acl_2 + aces: + - sequence: 10 + remark: TEST_ACL_2_REMARK + state: merged + +# After state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 05:22:57.021 UTC +# ipv4 access-list acl_1 +# 16 remark TEST_ACL_1_REMARK +# 21 permit tcp host 192.0.2.10 range pop3 121 198.51.100.0 0.0.0.15 rst +# 23 deny icmp any 198.51.100.0 0.0.0.15 reassembly-timeout dscp lt af12 +# ipv4 access-list acl_2 +# 10 remark TEST_ACL_2_REMARK +# ipv6 access-list acl6_1 +# 10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 routing authen log +# 20 permit icmpv6 any any router-advertisement precedence network destopts + +# Using merged to update existing ACLs + +# Before state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 05:22:57.021 UTC +# ipv4 access-list acl_1 +# 16 remark TEST_ACL_1_REMARK +# 21 permit tcp host 192.0.2.10 range pop3 121 198.51.100.0 0.0.0.15 rst +# 23 deny icmp any 198.51.100.0 0.0.0.15 reassembly-timeout dscp lt af12 +# ipv4 access-list acl_2 +# 10 remark TEST_ACL_2_REMARK +# ipv6 access-list acl6_1 +# 10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 routing authen log +# 20 permit icmpv6 any any router-advertisement precedence network destopts + +- name: Update existing ACEs + cisco.iosxr.iosxr_acls: + config: + - afi: ipv4 + acls: + - name: acl_1 + aces: + - sequence: 21 + source: + prefix: 198.51.100.32/28 + port_protocol: + range: + start: pop3 + end: 121 + protocol_options: + tcp: + syn: true + + - sequence: 23 + protocol_options: + icmp: + router_advertisement: true + dscp: + eq: af23 + +# After state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 05:47:18.711 UTC +# ipv4 access-list acl_1 +# 16 remark TEST_ACL_1_REMARK +# 21 permit tcp 198.51.100.32 0.0.0.15 range pop3 121 198.51.100.0 0.0.0.15 syn +# 23 deny icmp any 198.51.100.0 0.0.0.15 router-advertisement dscp eq af23 +# ipv4 access-list acl_2 +# 10 remark TEST_ACL_2_REMARK +# ipv6 access-list acl6_1 +# 10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 routing authen log +# 20 permit icmpv6 any any router-advertisement precedence network destopts + +# Using replaced to replace a whole ACL + +# Before state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 05:22:57.021 UTC +# ipv4 access-list acl_1 +# 16 remark TEST_ACL_1_REMARK +# 21 permit tcp host 192.0.2.10 range pop3 121 198.51.100.0 0.0.0.15 rst +# 23 deny icmp any 198.51.100.0 0.0.0.15 reassembly-timeout dscp lt af12 +# ipv4 access-list acl_2 +# 10 remark TEST_ACL_2_REMARK +# ipv6 access-list acl6_1 +# 10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 routing authen log +# 20 permit icmpv6 any any router-advertisement precedence network destopts + +- name: Replace device configurations of listed ACL with provided configurations + cisco.iosxr.iosxr_acls: + config: + - afi: ipv4 + acls: + - name: acl_2 + aces: + - sequence: 11 + grant: permit + protocol: igmp + source: + host: 198.51.100.130 + destination: + any: true + ttl: + eq: 100 + + - sequence: 12 + grant: deny + source: + any: true + destination: + any: true + protocol: icmp + state: replaced + +# After state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 06:19:51.496 UTC +# ipv4 access-list acl_1 +# 16 remark TEST_ACL_1_REMARK +# 21 permit tcp 198.51.100.32 0.0.0.15 range pop3 121 198.51.100.0 0.0.0.15 syn +# 23 deny icmp any 198.51.100.0 0.0.0.15 router-advertisement dscp eq af23 +# ipv4 access-list acl_2 +# 11 permit igmp host 198.51.100.130 any ttl eq 100 +# 12 deny icmp any any +# ipv6 access-list acl6_1 +# 10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 routing authen log +# 20 permit icmpv6 any any router-advertisement precedence network destopts + +# Using overridden to override all ACLs in the device + +# Before state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 05:22:57.021 UTC +# ipv4 access-list acl_1 +# 16 remark TEST_ACL_1_REMARK +# 21 permit tcp host 192.0.2.10 range pop3 121 198.51.100.0 0.0.0.15 rst +# 23 deny icmp any 198.51.100.0 0.0.0.15 reassembly-timeout dscp lt af12 +# ipv4 access-list acl_2 +# 10 remark TEST_ACL_2_REMARK +# ipv6 access-list acl6_1 +# 10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 routing authen log +# 20 permit icmpv6 any any router-advertisement precedence network destopts + +- name: Overridde all ACLs configuration with provided configuration + cisco.iosxr.iosxr_acls: + config: + - afi: ipv4 + acls: + - name: acl_1 + aces: + - sequence: 10 + grant: permit + source: + any: true + destination: + any: true + protocol: tcp + + - name: acl_2 + aces: + - sequence: 20 + grant: permit + source: + any: true + destination: + any: true + protocol: igmp + state: overridden + +# After state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 06:31:22.178 UTC +# ipv4 access-list acl_1 +# 10 permit tcp any any +# ipv4 access-list acl_2 +# 20 permit igmp any any + +# Using deleted to delete an entire ACL + +# Before state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 05:22:57.021 UTC +# ipv4 access-list acl_1 +# 16 remark TEST_ACL_1_REMARK +# 21 permit tcp host 192.0.2.10 range pop3 121 198.51.100.0 0.0.0.15 rst +# 23 deny icmp any 198.51.100.0 0.0.0.15 reassembly-timeout dscp lt af12 +# ipv4 access-list acl_2 +# 10 remark TEST_ACL_2_REMARK +# ipv6 access-list acl6_1 +# 10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 routing authen log +# 20 permit icmpv6 any any router-advertisement precedence network destopts + +- name: Delete a single ACL + cisco.iosxr.iosxr_acls: + config: + - afi: ipv6 + acls: + - name: acl6_1 + state: deleted + +# After state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 05:22:57.021 UTC +# ipv4 access-list acl_1 +# 16 remark TEST_ACL_1_REMARK +# 21 permit tcp host 192.0.2.10 range pop3 121 198.51.100.0 0.0.0.15 rst +# 23 deny icmp any 198.51.100.0 0.0.0.15 reassembly-timeout dscp lt af12 +# ipv4 access-list acl_2 +# 10 remark TEST_ACL_2_REMARK + +# Using deleted to delete all ACLs under one AFI + +# Before state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 05:22:57.021 UTC +# ipv4 access-list acl_1 +# 16 remark TEST_ACL_1_REMARK +# 21 permit tcp host 192.0.2.10 range pop3 121 198.51.100.0 0.0.0.15 rst +# 23 deny icmp any 198.51.100.0 0.0.0.15 reassembly-timeout dscp lt af12 +# ipv4 access-list acl_2 +# 10 remark TEST_ACL_2_REMARK +# ipv6 access-list acl6_1 +# 10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 routing authen log +# 20 permit icmpv6 any any router-advertisement precedence network destopts + +- name: Delete all ACLs under one AFI + cisco.iosxr.iosxr_acls: + config: + - afi: ipv4 + state: deleted + +# After state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 05:22:57.021 UTC +# ipv6 access-list acl6_1 +# 10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 routing authen log +# 20 permit icmpv6 any any router-advertisement precedence network destopts + +# Using deleted to delete all ACLs from the device + +# Before state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 05:22:57.021 UTC +# ipv4 access-list acl_1 +# 16 remark TEST_ACL_1_REMARK +# 21 permit tcp host 192.0.2.10 range pop3 121 198.51.100.0 0.0.0.15 rst +# 23 deny icmp any 198.51.100.0 0.0.0.15 reassembly-timeout dscp lt af12 +# ipv4 access-list acl_2 +# 10 remark TEST_ACL_2_REMARK +# ipv6 access-list acl6_1 +# 10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 routing authen log +# 20 permit icmpv6 any any router-advertisement precedence network destopts + +- name: Delete all ACLs from the device + cisco.iosxr.iosxr_acls: + state: deleted + +# After state: +# ------------- + +# RP/0/RP0/CPU0:ios#sh access-lists afi-all +# Thu Feb 20 05:07:45.767 UTC +# RP/0/RP0/CPU0:ios# + +# Using gathered to gather ACL facts from the device + +- name: Gather ACL interfaces facts using gathered state + cisco.iosxr.iosxr_acls: + state: gathered + +# Task Output (redacted) +# ----------------------- +# + +# "gathered": [ +# { +# "acls": [ +# { +# "aces": [ +# { +# "remark": "TEST_ACL_1_REMARK", +# "sequence": 16 +# }, +# { +# "destination": { +# "address": "198.51.100.0", +# "wildcard_bits": "0.0.0.15" +# }, +# "grant": "permit", +# "protocol": "tcp", +# "protocol_options": { +# "tcp": { +# "rst": true +# } +# }, +# "sequence": 21, +# "source": { +# "host": "192.0.2.10", +# "port_protocol": { +# "range": { +# "end": "121", +# "start": "pop3" +# } +# } +# } +# }, +# { +# "destination": { +# "address": "198.51.100.0", +# "wildcard_bits": "0.0.0.15" +# }, +# "dscp": { +# "lt": "af12" +# }, +# "grant": "deny", +# "protocol": "icmp", +# "protocol_options": { +# "icmp": { +# "reassembly_timeout": true +# } +# }, +# "sequence": 23, +# "source": { +# "any": true +# } +# } +# ], +# "name": "acl_1" +# }, +# { +# "aces": [ +# { +# "remark": "TEST_ACL_2_REMARK", +# "sequence": 10 +# } +# ], +# "name": "acl_2" +# } +# ], +# "afi": "ipv4" +# }, +# { +# "acls": [ +# { +# "aces": [ +# { +# "authen": true, +# "destination": { +# "any": true +# }, +# "grant": "deny", +# "log": true, +# "protocol": "tcp", +# "protocol_options": { +# "tcp": { +# "syn": true +# } +# }, +# "routing": true, +# "sequence": 10, +# "source": { +# "port_protocol": { +# "range": { +# "end": "telnet", +# "start": "ftp" +# } +# }, +# "prefix": "2001:db8:1234::/48" +# }, +# "ttl": { +# "range": { +# "end": 250, +# "start": 180 +# } +# } +# }, +# { +# "destination": { +# "any": true +# }, +# "destopts": true, +# "grant": "permit", +# "precedence": "network", +# "protocol": "icmpv6", +# "protocol_options": { +# "icmpv6": { +# "router_advertisement": true +# } +# }, +# "sequence": 20, +# "source": { +# "any": true +# } +# } +# ], +# "name": "acl6_1" +# } +# ], +# "afi": "ipv6" +# } +# ] + +# Using rendered + +- name: Render platform specific commands (without connecting to the device) + cisco.iosxr.iosxr_acls: + config: + - afi: ipv4 + acls: + - name: acl_2 + aces: + - sequence: 11 + grant: permit + protocol: igmp + source: + host: 198.51.100.130 + destination: + any: true + ttl: + eq: 100 + + - sequence: 12 + grant: deny + source: + any: true + destination: + any: true + protocol: icmp + state: rendered + +# Task Output (redacted) +# ----------------------- + +# "rendered": [ +# "ipv4 access-list acl_2", +# "11 permit igmp host 198.51.100.130 any ttl eq 100", +# "12 deny icmp any any" + +# Using parsed + +# parsed.cfg +# ------------ +# +# ipv4 access-list acl_1 +# 10 remark TEST_ACL_2_REMARK +# ipv4 access-list acl_2 +# 11 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 authen routing log +# 21 permit icmpv6 any any router-advertisement precedence network packet-length eq 576 destopts +# ipv6 access-list acl6_1 +# 10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 routing authen log +# 20 permit icmpv6 any any router-advertisement precedence network packet-length eq 576 destopts + +- name: Parse externally provided ACL config to agnostic model + cisco.iosxr.iosxr_acls: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Task Output (redacted) +# ----------------------- +# "parsed": [ +# { +# "acls": [ +# { +# "aces": [ +# { +# "remark": "TEST_ACL_2_REMARK", +# "sequence": 10 +# } +# ], +# "name": "acl_1" +# }, +# { +# "aces": [ +# { +# "authen": true, +# "destination": { +# "any": true +# }, +# "grant": "deny", +# "log": true, +# "protocol": "tcp", +# "protocol_options": { +# "tcp": { +# "syn": true +# } +# }, +# "routing": true, +# "sequence": 11, +# "source": { +# "port_protocol": { +# "range": { +# "end": "telnet", +# "start": "ftp" +# } +# }, +# "prefix": "2001:db8:1234::/48" +# }, +# "ttl": { +# "range": { +# "end": 250, +# "start": 180 +# } +# } +# }, +# { +# "destination": { +# "any": true +# }, +# "destopts": true, +# "grant": "permit", +# "packet_length": { +# "eq": 576 +# }, +# "precedence": "network", +# "protocol": "icmpv6", +# "protocol_options": { +# "icmpv6": { +# "router_advertisement": true +# } +# }, +# "sequence": 21, +# "source": { +# "any": true +# } +# } +# ], +# "name": "acl_2" +# } +# ], +# "afi": "ipv4" +# }, +# { +# "acls": [ +# { +# "aces": [ +# { +# "authen": true, +# "destination": { +# "any": true +# }, +# "grant": "deny", +# "log": true, +# "protocol": "tcp", +# "protocol_options": { +# "tcp": { +# "syn": true +# } +# }, +# "routing": true, +# "sequence": 10, +# "source": { +# "port_protocol": { +# "range": { +# "end": "telnet", +# "start": "ftp" +# } +# }, +# "prefix": "2001:db8:1234::/48" +# }, +# "ttl": { +# "range": { +# "end": 250, +# "start": 180 +# } +# } +# }, +# { +# "destination": { +# "any": true +# }, +# "destopts": true, +# "grant": "permit", +# "packet_length": { +# "eq": 576 +# }, +# "precedence": "network", +# "protocol": "icmpv6", +# "protocol_options": { +# "icmpv6": { +# "router_advertisement": true +# } +# }, +# "sequence": 20, +# "source": { +# "any": true +# } +# } +# ], +# "name": "acl6_1" +# } +# ], +# "afi": "ipv6" +# } +# ] +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: + - ipv6 access-list acl6_1 + - 10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 authen routing log + - 20 permit icmpv6 any any router-advertisement precedence network destopts + - ipv4 access-list acl_1 + - 16 remark TEST_ACL_1_REMARK + - 21 permit tcp host 192.0.2.10 range pop3 121 198.51.100.0 0.0.0.15 rst + - 23 deny icmp any 198.51.100.0 0.0.0.15 reassembly-timeout dscp lt af12 +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.acls.acls import ( + AclsArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.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",)), + ] + + module = AnsibleModule( + argument_spec=AclsArgs.argument_spec, + required_if=required_if, + 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/iosxr/plugins/modules/iosxr_banner.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_banner.py new file mode 100644 index 00000000..be75ecaf --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_banner.py @@ -0,0 +1,316 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, 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 + + +DOCUMENTATION = """ +module: iosxr_banner +author: +- Trishna Guha (@trishnaguha) +- Kedar Kekan (@kedarX) +short_description: Manage multiline banners on Cisco IOS XR devices +description: +- This module will configure both exec and motd banners on remote device running Cisco + IOS XR. It allows playbooks to add or remove banner text from the running configuration. +version_added: 1.0.0 +requirements: +- ncclient >= 0.5.3 when using netconf +- lxml >= 4.1.1 when using netconf +extends_documentation_fragment: +- cisco.iosxr.iosxr +notes: +- Tested against IOS XRv 6.1.3. +- This module works with connection C(network_cli) and C(netconf). See L(the IOS-XR + Platform Options,../network/user_guide/platform_iosxr.html). +options: + banner: + description: + - Specifies the type of banner to configure on remote device. + required: true + type: str + choices: + - login + - motd + text: + description: + - Banner text to be configured. Accepts multi line string, without empty lines. + When using a multi line string, the first and last characters must be the + start and end delimiters for the banner + Requires I(state=present). + type: str + state: + description: + - Existential state of the configuration on the device. + default: present + type: str + choices: + - present + - absent +""" + +EXAMPLES = """ +- name: configure the login banner + cisco.iosxr.iosxr_banner: + banner: login + text: | + @this is my login banner + that contains a multiline + string@ + state: present +- name: remove the motd banner + cisco.iosxr.iosxr_banner: + banner: motd + state: absent +- name: Configure banner from file + cisco.iosxr.iosxr_banner: + banner: motd + text: "{{ lookup('file', './config_partial/raw_banner.cfg') }}" + state: present +""" + +RETURN = """ +commands: + description: The list of configuration mode commands sent to device with transport C(cli) + returned: always (empty list when no commands to send) + type: list + sample: + - banner login + - "@this is my login banner" + - that contains a multiline + - string@ + +xml: + description: NetConf rpc xml sent to device with transport C(netconf) + returned: always (empty list when no xml rpc to send) + type: list + sample: + - '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <banners xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-infra-infra-cfg"> + <banner xc:operation="merge"> + <banner-name>motd</banner-name> + <banner-text>Ansible banner example</banner-text> + </banner> + </banners> + </config>' +""" + +import re +import collections + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + get_config, + load_config, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + iosxr_argument_spec, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + build_xml, + is_cliconf, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + etree_find, + is_netconf, +) + + +class ConfigBase(object): + def __init__(self, module): + self._module = module + self._result = {"changed": False, "warnings": []} + self._want = {} + self._have = {} + + def map_params_to_obj(self): + text = self._module.params["text"] + if text: + text = "{0}".format(str(text).strip()) + self._want.update( + { + "banner": self._module.params["banner"], + "text": text, + "state": self._module.params["state"], + } + ) + + +class CliConfiguration(ConfigBase): + def __init__(self, module): + super(CliConfiguration, self).__init__(module) + + def map_obj_to_commands(self): + commands = list() + state = self._module.params["state"] + if state == "absent": + if self._have.get("state") != "absent" and ( + "text" in self._have.keys() and self._have["text"] + ): + commands.append( + "no banner {0!s}".format(self._module.params["banner"]) + ) + elif state == "present": + if self._want["text"] and self._want["text"].encode().decode( + "unicode_escape" + ) != self._have.get("text"): + banner_cmd = "banner {0!s} ".format( + self._module.params["banner"] + ) + banner_cmd += self._want["text"].strip() + commands.append(banner_cmd) + self._result["commands"] = commands + if commands: + commit = not self._module.check_mode + diff = load_config(self._module, commands, commit=commit) + if diff: + self._result["diff"] = dict(prepared=diff) + self._result["changed"] = True + + def map_config_to_obj(self): + cli_filter = "banner {0!s}".format(self._module.params["banner"]) + output = get_config(self._module, config_filter=cli_filter) + match = re.search(r"banner (\S+) (.*)", output, re.DOTALL) + if match: + text = match.group(2) + else: + text = None + obj = {"banner": self._module.params["banner"], "state": "absent"} + if output: + obj["text"] = text + obj["state"] = "present" + self._have.update(obj) + + def run(self): + self.map_params_to_obj() + self.map_config_to_obj() + self.map_obj_to_commands() + + return self._result + + +class NCConfiguration(ConfigBase): + def __init__(self, module): + super(NCConfiguration, self).__init__(module) + self._banners_meta = collections.OrderedDict() + self._banners_meta.update( + [ + ( + "banner", + { + "xpath": "banners/banner", + "tag": True, + "attrib": "operation", + }, + ), + ("a:banner", {"xpath": "banner/banner-name"}), + ( + "a:text", + {"xpath": "banner/banner-text", "operation": "edit"}, + ), + ] + ) + + def map_obj_to_xml_rpc(self): + state = self._module.params["state"] + _get_filter = build_xml( + "banners", + xmap=self._banners_meta, + params=self._module.params, + opcode="filter", + ) + + running = get_config( + self._module, source="running", config_filter=_get_filter + ) + + banner_name = None + banner_text = None + if etree_find(running, "banner-text") is not None: + banner_name = etree_find(running, "banner-name").text + banner_text = etree_find(running, "banner-text").text + + opcode = None + if ( + state == "absent" + and banner_name == self._module.params["banner"] + and len(banner_text) + ): + opcode = "delete" + elif state == "present": + opcode = "merge" + + self._result["xml"] = [] + if opcode: + _edit_filter = build_xml( + "banners", + xmap=self._banners_meta, + params=self._module.params, + opcode=opcode, + ) + + if _edit_filter is not None: + commit = not self._module.check_mode + diff = load_config( + self._module, + _edit_filter, + commit=commit, + running=running, + nc_get_filter=_get_filter, + ) + + if diff: + self._result["xml"] = _edit_filter + if self._module._diff: + self._result["diff"] = dict(prepared=diff) + + self._result["changed"] = True + + def run(self): + self.map_params_to_obj() + self.map_obj_to_xml_rpc() + + return self._result + + +def main(): + """ main entry point for module execution + """ + argument_spec = dict( + banner=dict(required=True, choices=["login", "motd"]), + text=dict(), + state=dict(default="present", choices=["present", "absent"]), + ) + + argument_spec.update(iosxr_argument_spec) + + required_if = [("state", "present", ("text",))] + + module = AnsibleModule( + argument_spec=argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + config_object = None + if is_cliconf(module): + # Commenting the below cliconf deprecation support call for Ansible 2.9 as it'll be continued to be supported + # module.deprecate("cli support for 'iosxr_interface' is deprecated. Use transport netconf instead", + # version='2.9') + config_object = CliConfiguration(module) + elif is_netconf(module): + config_object = NCConfiguration(module) + + result = None + if config_object is not None: + result = config_object.run() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_bgp.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_bgp.py new file mode 100644 index 00000000..7e496545 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_bgp.py @@ -0,0 +1,355 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# (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 + + +DOCUMENTATION = """ +module: iosxr_bgp +author: Nilashish Chakraborty (@NilashishC) +short_description: Configure global BGP protocol settings on Cisco IOS-XR +description: +- This module provides configuration management of global BGP parameters on devices + running Cisco IOS-XR +version_added: 1.0.0 +notes: +- Tested against Cisco IOS XR Software Version 6.1.3 +- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html). +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 + 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. + type: str + required: true + 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 + advertisement_interval: + description: + - Specifies the minimum interval (in seconds) between sending BGP routing + updates. + - The range is from 0 to 600. + type: int + tcp_mss: + description: + - Specifies the TCP initial maximum segment size to use. + - The range is from 68 to 10000. + type: int + ebgp_multihop: + description: + - Specifies the maximum hop count for EBGP neighbors not on directly connected + networks. + - The range is from 0 to 255. + type: int + timers: + description: + - Specifies BGP neighbor timer related configurations. + type: dict + suboptions: + keepalive: + description: + - Frequency with which the Cisco IOS-XR software sends keepalive messages + to its peer. + - The range is from 0 to 65535. + type: int + holdtime: + description: + - Interval after not receiving a keepalive message that the software + declares a peer dead. + - The range is from 3 to 65535. + type: int + min_neighbor_holdtime: + description: + - Interval 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 3 to 65535. + type: int + 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 + type: str + default: unicast + redistribute: + description: + - Specifies the redistribute information from another routing protocol. + type: list + elements: dict + suboptions: + protocol: + description: + - Specifies the protocol for configuring redistribute information. + type: str + choices: + - ospf + - ospfv3 + - eigrp + - isis + - static + - connected + - lisp + - mobile + - rip + - subscriber + required: true + id: + description: + - Identifier for the routing protocol for configuring redistribute + information. + - Valid for protocols 'ospf', 'eigrp', 'isis' and 'ospfv3'. + 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: + network: + description: + - Network ID to announce via BGP. + required: true + aliases: + - prefix + type: str + masklen: + description: + - Subnet mask length for the network to announce(e.g, 8, 16, 24, etc.). + type: int + required: true + route_map: + description: + - Route map to modify the attributes. + 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 65000 + cisco.iosxr.iosxr_bgp: + bgp_as: 65000 + router_id: 1.1.1.1 + neighbors: + - neighbor: 182.168.10.1 + remote_as: 500 + description: PEER_1 + - neighbor: 192.168.20.1 + remote_as: 500 + update_source: GigabitEthernet 0/0/0/0 + address_family: + - name: ipv4 + cast: unicast + networks: + - network: 192.168.2.0/23 + - network: 10.0.0.0/8 + redistribute: + - protocol: ospf + id: 400 + metric: 110 + +- name: remove bgp as 65000 from config + ios_bgp: + bgp_as: 65000 + state: absent +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - router bgp 65000 + - bgp router-id 1.1.1.1 + - neighbor 182.168.10.1 remote-as 500 + - neighbor 182.168.10.1 description PEER_1 + - neighbor 192.168.20.1 remote-as 500 + - neighbor 192.168.20.1 update-source GigabitEthernet0/0/0/0 + - address-family ipv4 unicast + - redistribute ospf 400 metric 110 + - network 192.168.2.0/23 + - network 10.0.0.0/8 + - exit +""" +from ansible.module_utils._text import to_text +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.providers.module import ( + NetworkModule, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.providers.cli.config.bgp.process import ( + REDISTRIBUTE_PROTOCOLS, +) + + +def main(): + """ main entry point for module execution + """ + network_spec = { + "network": dict(aliases=["prefix"], required=True), + "masklen": dict(type="int", required=True), + "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"), + "holdtime": dict(type="int"), + "min_neighbor_holdtime": dict(type="int"), + } + + neighbor_spec = { + "neighbor": dict(required=True), + "remote_as": dict(type="int", required=True), + "update_source": dict(), + "password": dict(no_log=True), + "enabled": dict(type="bool"), + "description": dict(), + "advertisement_interval": dict(type="int"), + "ebgp_multihop": dict(type="int"), + "tcp_mss": dict(type="int"), + "timers": dict(type="dict", options=timer_spec), + } + + address_family_spec = { + "afi": dict(choices=["ipv4", "ipv6"], required=True), + "safi": dict( + choices=["flowspec", "labeled-unicast", "multicast", "unicast"], + default="unicast", + ), + "networks": dict(type="list", elements="dict", options=network_spec), + "redistribute": dict( + type="list", elements="dict", options=redistribute_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 + ), + } + + 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="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/iosxr/plugins/modules/iosxr_command.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_command.py new file mode 100644 index 00000000..68e990b1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_command.py @@ -0,0 +1,213 @@ +#!/usr/bin/python +# +# Copyright: Ansible Project +# 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 + +DOCUMENTATION = """ +module: iosxr_command +author: Ricardo Carrillo Cruz (@rcarrillocruz) +short_description: Run commands on remote devices running Cisco IOS XR +description: +- Sends arbitrary commands to an IOS XR 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(iosxr_config) to configure iosxr devices. +version_added: 1.0.0 +extends_documentation_fragment: +- cisco.iosxr.iosxr +notes: +- Make sure the user has been authorized to execute commands terminal length 0, terminal + width 512 and terminal exec prompt no-timestamp. +- This module works with C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html). +- This module does not support C(netconf) connection. +- Tested against IOS XR 6.1.3 +options: + commands: + description: + - List of commands to send to the remote iosxr 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 command, answer and prompt. Common answers are 'y' or "\\r" + (carriage return, must be double quotes). See examples + type: list + elements: raw + required: true + 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 + choices: + - any + - all + type: str + 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.iosxr.iosxr_command: + commands: show version + +- name: run show version and check to see if output contains iosxr + cisco.iosxr.iosxr_command: + commands: show version + wait_for: result[0] contains IOS-XR + +- name: run multiple commands on remote nodes + cisco.iosxr.iosxr_command: + commands: + - show version + - show interfaces + - {command: example command that prompts, prompt: expected prompt, answer: yes} + +- name: run multiple commands and evaluate the output + cisco.iosxr.iosxr_command: + commands: + - show version + - show interfaces + wait_for: + - result[0] contains IOS-XR + - result[1] contains Loopback0 +""" + +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 ( + to_lines, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + run_commands, + iosxr_argument_spec, +) + + +def parse_commands(module, warnings): + commands = module.params["commands"] + for item in list(commands): + try: + command = item["command"] + except Exception: + command = item + if module.check_mode and not command.startswith("show"): + warnings.append( + "Only show commands are supported when using check mode, not " + "executing %s" % command + ) + commands.remove(item) + + return commands + + +def main(): + argument_spec = dict( + commands=dict(type="list", required=True, elements="raw"), + wait_for=dict(type="list", aliases=["waitfor"], elements="str"), + match=dict(default="all", choices=["all", "any"]), + retries=dict(default=10, type="int"), + interval=dict(default=1, type="int"), + ) + + argument_spec.update(iosxr_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/iosxr/plugins/modules/iosxr_config.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_config.py new file mode 100644 index 00000000..2a5d3080 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_config.py @@ -0,0 +1,480 @@ +#!/usr/bin/python +# +# Copyright: Ansible Project +# 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 + + +DOCUMENTATION = """ +module: iosxr_config +author: Ricardo Carrillo Cruz (@rcarrillocruz) +short_description: Manage Cisco IOS XR configuration sections +description: +- Cisco IOS XR configurations use a simple block indent file syntax for segmenting + configuration into sections. This module provides an implementation for working + with IOS XR configuration sections in a deterministic way. +version_added: 1.0.0 +extends_documentation_fragment: +- cisco.iosxr.iosxr +notes: +- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html). +- Tested against IOS XRv 6.1.3. +- This module does not support C(netconf) connection +- 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). +- Avoid service disrupting changes (viz. Management IP) from config replace. +- Do not use C(end) in the replace config file. +- To ensure idempotency and correct diff the configuration lines in the relevant module options should be similar to how they + appear if present in the running configuration on device including the indentation. +options: + lines: + description: + - The ordered set of commands that should be configured in the section. The commands + must be the exact same commands as found in the device running-config as found in the + device running-config to ensure idempotency and correct diff. Be sure to note the + configuration command syntax as some commands are automatically modified by the + device config parser. + type: list + elements: str + 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 to ensure idempotency and correct diff. + type: path + 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. + type: str + default: line + choices: + - line + - strict + - exact + - none + 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. + type: str + default: line + choices: + - line + - block + - config + force: + description: + - The force argument instructs the module to not consider the current devices + running-config. When set to true, this will cause the module to push the contents + of I(src) into the device without first checking if already configured. + - Note this argument should be considered deprecated. To achieve the equivalent, + set the C(match=none) which is idempotent. This argument will be removed in + a future release. + type: bool + default: no + 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(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 to ensure idempotency and correct diff. + 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 + comment: + description: + - Allows a commit description to be specified to be included when the configuration + is committed. If the configuration is not changed or committed, this argument + is ignored. + type: str + default: configured by iosxr_config + admin: + description: + - Enters into administration configuration mode for making config changes to the + device. + type: bool + default: no + label: + description: + - Allows a commit label to be specified to be included when the configuration + is committed. A valid label must begin with an alphabet and not exceed 30 characters, + only alphabets, digits, hyphens and underscores are allowed. If the configuration + is not changed or committed, this argument is ignored. + 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 + exclusive: + description: + - Enters into exclusive configuration mode that locks out all users from committing + configuration changes until the exclusive session ends. + type: bool + default: false +""" + +EXAMPLES = """ +- name: configure top level configuration + cisco.iosxr.iosxr_config: + lines: hostname {{ inventory_hostname }} + +- name: configure interface settings + cisco.iosxr.iosxr_config: + lines: + - description test interface + - ip address 172.31.1.1 255.255.255.0 + parents: interface GigabitEthernet0/0/0/0 + +- name: load a config from disk and replace the current config + cisco.iosxr.iosxr_config: + src: config.cfg + replace: config + backup: yes + +- name: for idempotency, use full-form commands + cisco.iosxr.iosxr_config: + lines: + # - shut + - shutdown + # parents: int g0/0/0/1 + parents: interface GigabitEthernet0/0/0/1 + +- name: configurable backup path + cisco.iosxr.iosxr_config: + src: config.cfg + backup: yes + backup_options: + filename: backup.cfg + dir_path: /home/user +""" + +RETURN = """ +commands: + description: The set of commands that will be pushed to the remote device + returned: If there are commands to run against the host + type: list + sample: ['hostname foo', 'router ospf 1', 'router-id 1.1.1.1'] +backup_path: + description: The full path to the backup file + returned: when backup is yes + type: str + sample: /playbooks/ansible/backup/iosxr01_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: iosxr01_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/iosxr01_config +date: + description: The date extracted from the backup file name + returned: when backup is yes + type: str + sample: "2016-07-16" +time: + description: The time extracted from the backup file name + returned: when backup is yes + type: str + sample: "22:28:34" +""" +import re + +from ansible.module_utils._text import to_text, to_bytes +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import ConnectionError +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + load_config, + get_config, + get_connection, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + iosxr_argument_spec, + copy_file, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( + NetworkConfig, + dumps, +) + +DEFAULT_COMMIT_COMMENT = "configured by iosxr_config" + + +def copy_file_to_node(module): + """ Copy config file to IOS-XR node. We use SFTP because older IOS-XR versions don't handle SCP very well. + """ + src = "/tmp/ansible_config.txt" + file = open(src, "wb") + file.write(to_bytes(module.params["src"], errors="surrogate_or_strict")) + file.close() + + dst = "/harddisk:/ansible_config.txt" + copy_file(module, src, dst, "sftp") + + return True + + +def check_args(module, warnings): + if module.params["comment"]: + if len(module.params["comment"]) > 60: + module.fail_json( + msg="comment argument cannot be more than 60 characters" + ) + if module.params["label"]: + label = module.params["label"] + if len(label) > 30: + module.fail_json( + msg="label argument cannot be more than 30 characters" + ) + if not label[0].isalpha(): + module.fail_json(msg="label argument must begin with an alphabet") + valid_chars = re.match(r"[\w-]*$", label) + if not valid_chars: + module.fail_json( + msg="label argument must only contain alphabets," + + "digits, underscores or hyphens" + ) + if module.params["force"]: + warnings.append( + "The force argument is deprecated, please use " + "match=none instead. This argument will be " + "removed in the future" + ) + + +def get_running_config(module): + contents = module.params["config"] + if not contents: + contents = get_config(module) + return contents + + +def get_candidate(module): + candidate = "" + if module.params["src"]: + candidate = module.params["src"] + elif module.params["lines"]: + candidate_obj = NetworkConfig(indent=1, comment_tokens=["!"]) + parents = module.params["parents"] or list() + candidate_obj.add(module.params["lines"], parents=parents) + candidate = dumps(candidate_obj, "raw") + return candidate + + +def run(module, result): + match = module.params["match"] + replace = module.params["replace"] + replace_config = replace == "config" + path = module.params["parents"] + comment = module.params["comment"] + admin = module.params["admin"] + exclusive = module.params["exclusive"] + check_mode = module.check_mode + label = module.params["label"] + + candidate_config = get_candidate(module) + running_config = get_running_config(module) + + commands = None + replace_file_path = None + connection = get_connection(module) + try: + response = connection.get_diff( + candidate=candidate_config, + running=running_config, + diff_match=match, + path=path, + diff_replace=replace, + ) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + + config_diff = response.get("config_diff") + + if replace_config: + running_base_diff_resp = connection.get_diff( + candidate=running_config, + running=candidate_config, + diff_match=match, + path=path, + diff_replace=replace, + ) + if config_diff or running_base_diff_resp["config_diff"]: + ret = copy_file_to_node(module) + if not ret: + module.fail_json(msg="Copy of config file to the node failed") + + commands = ["load harddisk:/ansible_config.txt"] + replace_file_path = "harddisk:/ansible_config.txt" + + if config_diff or commands: + if not replace_config: + commands = config_diff.split("\n") + + 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" + module.warn(msg) + + if module.params["before"]: + commands[:0] = module.params["before"] + + if module.params["after"]: + commands.extend(module.params["after"]) + + result["commands"] = commands + + commit = not check_mode + diff = load_config( + module, + commands, + commit=commit, + replace=replace_file_path, + comment=comment, + admin=admin, + exclusive=exclusive, + label=label, + ) + if diff: + result["diff"] = dict(prepared=diff) + + result["changed"] = True + + +def main(): + """main entry point for module execution + """ + backup_spec = dict(filename=dict(), dir_path=dict(type="path")) + argument_spec = dict( + src=dict(type="path"), + 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", "config"]), + # this argument is deprecated in favor of setting match: none + # it will be removed in a future version + force=dict(default=False, type="bool"), + config=dict(), + backup=dict(type="bool", default=False), + backup_options=dict(type="dict", options=backup_spec), + comment=dict(default=DEFAULT_COMMIT_COMMENT), + admin=dict(type="bool", default=False), + exclusive=dict(type="bool", default=False), + label=dict(), + ) + + argument_spec.update(iosxr_argument_spec) + + mutually_exclusive = [("lines", "src"), ("parents", "src")] + + required_if = [ + ("match", "strict", ["lines"]), + ("match", "exact", ["lines"]), + ("replace", "block", ["lines"]), + ("replace", "config", ["src"]), + ] + + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + required_if=required_if, + supports_check_mode=True, + ) + + if module.params["force"] is True: + module.params["match"] = "none" + + warnings = list() + check_args(module, warnings) + + result = dict(changed=False, warnings=warnings) + + if module.params["backup"]: + result["__backup__"] = get_config(module) + + if any((module.params["src"], module.params["lines"])): + run(module, result) + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_facts.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_facts.py new file mode 100644 index 00000000..5ac8effa --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_facts.py @@ -0,0 +1,218 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The module file for iosxr_facts +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: iosxr_facts +short_description: Get facts about iosxr devices. +extends_documentation_fragment: +- cisco.iosxr.iosxr +description: +- Collects facts from network devices running the iosxr operating system. This module + places the facts gathered in the fact tree keyed by the respective resource name. The + facts module will always collect a base set of facts from the device and can enable + or disable collection of additional facts. +version_added: 1.0.0 +notes: +- Tested against IOS-XR 6.1.3. +- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html). +author: +- Ricardo Carrillo Cruz (@rcarrillocruz) +- Nilashish Chakraborty (@Nilashishc) +options: + gather_subset: + description: + - When supplied, this argument will restrict the facts collected to a given subset. Possible + values for this argument include all, hardware, config, and interfaces. 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. + 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, + lacp 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', 'lacp', 'lacp_interfaces', 'lldp_global', + 'lldp_interfaces', 'interfaces', 'l2_interfaces', 'l3_interfaces', 'lag_interfaces', + 'acls', 'acl_interfaces', 'static_routes', 'ospfv2'. + required: false + type: list + elements: str +""" + +EXAMPLES = """ +# Gather all facts +- cisco.iosxr.iosxr_facts: + gather_subset: all + gather_network_resources: all + +# Collect only the config and default facts +- cisco.iosxr.iosxr_facts: + gather_subset: + - config + +# Do not collect hardware facts +- cisco.iosxr.iosxr_facts: + gather_subset: + - '!hardware' + +# Collect only the lacp facts +- cisco.iosxr.iosxr_facts: + gather_subset: + - '!all' + - '!min' + gather_network_resources: + - lacp + +# Do not collect lacp_interfaces facts +- cisco.iosxr.iosxr_facts: + gather_network_resources: + - '!lacp_interfaces' + +# Collect lacp and minimal default facts +- cisco.iosxr.iosxr_facts: + gather_subset: min + gather_network_resources: lacp + +# Collect only the interfaces facts +- cisco.iosxr.iosxr_facts: + gather_subset: + - '!all' + - '!min' + gather_network_resources: + - interfaces + - l2_interfaces +""" + +RETURN = """ +ansible_net_gather_subset: + description: The list of fact subsets collected from the device + returned: always + type: list + +# default +ansible_net_version: + description: The operating system version 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_api: + description: The name of the transport + returned: always + type: str +ansible_net_python_version: + description: The Python version Ansible controller is using + returned: always + type: str +ansible_net_model: + description: The model name returned from the device + 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_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 LLDP neighbors from the remote device + returned: when interfaces is configured + type: dict + +# network resources +ansible_net_gather_network_resources: + description: The list of fact resource subsets collected from the device + returned: always + type: list +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + iosxr_argument_spec, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.facts.facts import ( + FactsArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( + Facts, +) + + +def main(): + """ + Main entry point for module execution + + :returns: ansible_facts + """ + argument_spec = FactsArgs.argument_spec + argument_spec.update(iosxr_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/iosxr/plugins/modules/iosxr_interface.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_interface.py new file mode 100644 index 00000000..d3dca6e9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_interface.py @@ -0,0 +1,1057 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, 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 + + +DOCUMENTATION = """ +module: iosxr_interface +author: +- Ganesh Nalawade (@ganeshrn) +- Kedar Kekan (@kedarX) +short_description: (deprecated, removed after 2022-06-01) Manage Interface on Cisco + IOS XR network devices +description: +- This module provides declarative management of Interfaces on Cisco IOS XR network + devices. +version_added: 1.0.0 +deprecated: + alternative: iosxr_interfaces + why: Newer and updated modules released with more functionality in Ansible 2.9 + removed_at_date: '2022-06-01' +requirements: +- ncclient >= 0.5.3 when using netconf +- lxml >= 4.1.1 when using netconf +extends_documentation_fragment: +- cisco.iosxr.iosxr +notes: +- This module works with connection C(network_cli) and C(netconf). See L(the IOS-XR + Platform Options,../network/user_guide/platform_iosxr.html). +- Tested against IOS XRv 6.1.3. +- Preconfiguration of physical interfaces is not supported with C(netconf) transport. +options: + name: + description: + - Name of the interface to configure in C(type + path) format. e.g. C(GigabitEthernet0/0/0/0) + type: str + required: true + description: + description: + - Description of Interface being configured. + type: str + enabled: + description: + - Removes the shutdown configuration, which removes the forced administrative + down on the interface, enabling it to move to an up or down state. + type: bool + default: true + active: + description: + - Whether the interface is C(active) or C(preconfigured). Preconfiguration allows + you to configure modular services cards before they are inserted into the router. + When the cards are inserted, they are instantly configured. Active cards are + the ones already inserted. + type: str + choices: + - active + - preconfigure + default: active + speed: + description: + - Configure the speed for an interface. Default is auto-negotiation when not configured. + choices: + - '10' + - '100' + - '1000' + type: str + mtu: + description: + - Sets the MTU value for the interface. Range is between 64 and 65535' + type: str + duplex: + description: + - Configures the interface duplex mode. Default is auto-negotiation when not configured. + type: str + choices: + - full + - half + 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 + 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 + aggregate: + description: List of interfaces definition + type: list + elements: dict + suboptions: + name: + description: + - Name of the interface to configure in C(type + path) format. e.g. C(GigabitEthernet0/0/0/0) + type: str + description: + description: + - Description of Interface being configured. + type: str + enabled: + description: + - Removes the shutdown configuration, which removes the forced administrative + down on the interface, enabling it to move to an up or down state. + type: bool + active: + description: + - Whether the interface is C(active) or C(preconfigured). Preconfiguration allows + you to configure modular services cards before they are inserted into the router. + When the cards are inserted, they are instantly configured. Active cards are + the ones already inserted. + type: str + choices: + - active + - preconfigure + speed: + description: + - Configure the speed for an interface. Default is auto-negotiation when not configured. + choices: + - '10' + - '100' + - '1000' + type: str + mtu: + description: + - Sets the MTU value for the interface. Range is between 64 and 65535' + type: str + duplex: + description: + - Configures the interface duplex mode. Default is auto-negotiation when not configured. + type: str + choices: + - full + - half + 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 + 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) + type: str + choices: + - present + - absent + - up + - down + state: + description: + - State of the Interface configuration, C(up) means present and operationally + up and C(down) means present and operationally C(down) + type: str + default: present + choices: + - present + - absent + - up + - down + + +""" + +EXAMPLES = """ +- name: configure interface + cisco.iosxr.iosxr_interface: + name: GigabitEthernet0/0/0/2 + description: test-interface + speed: 100 + duplex: half + mtu: 512 + +- name: remove interface + cisco.iosxr.iosxr_interface: + name: GigabitEthernet0/0/0/2 + state: absent + +- name: make interface up + cisco.iosxr.iosxr_interface: + name: GigabitEthernet0/0/0/2 + enabled: true + +- name: make interface down + cisco.iosxr.iosxr_interface: + name: GigabitEthernet0/0/0/2 + enabled: false + +- name: Create interface using aggregate + cisco.iosxr.iosxr_interface: + aggregate: + - name: GigabitEthernet0/0/0/3 + - name: GigabitEthernet0/0/0/2 + speed: 100 + duplex: full + mtu: 512 + state: present + +- name: Create interface using aggregate along with additional params in aggregate + cisco.iosxr.iosxr_interface: + aggregate: + - {name: GigabitEthernet0/0/0/3, description: test-interface 3} + - {name: GigabitEthernet0/0/0/2, description: test-interface 2} + speed: 100 + duplex: full + mtu: 512 + state: present + +- name: Delete interface using aggregate + cisco.iosxr.iosxr_interface: + aggregate: + - name: GigabitEthernet0/0/0/3 + - name: GigabitEthernet0/0/0/2 + state: absent + +- name: Check intent arguments + cisco.iosxr.iosxr_interface: + name: GigabitEthernet0/0/0/5 + state: up + delay: 20 + +- name: Config + intent + cisco.iosxr.iosxr_interface: + name: GigabitEthernet0/0/0/5 + enabled: false + state: down + delay: 20 +""" + +RETURN = """ +commands: + description: The list of configuration mode commands sent to device with transport C(cli) + returned: always (empty list when no commands to send) + type: list + sample: + - interface GigabitEthernet0/0/0/2 + - description test-interface + - duplex half + - mtu 512 + +xml: + description: NetConf rpc xml sent to device with transport C(netconf) + returned: always (empty list when no xml rpc to send) + type: list + sample: + - '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg"> + <interface-configuration xc:operation="merge"> + <active>act</active> + <interface-name>GigabitEthernet0/0/0/0</interface-name> + <description>test-interface-0</description> + <mtus><mtu> + <owner>GigabitEthernet</owner> + <mtu>512</mtu> + </mtu></mtus> + <ethernet xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-drivers-media-eth-cfg"> + <speed>100</speed> + <duplex>half</duplex> + </ethernet> + </interface-configuration> + </interface-configurations></config>' +""" +import re +from time import sleep +from copy import deepcopy +import collections + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + get_config, + load_config, + build_xml, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + run_commands, + iosxr_argument_spec, + get_oper, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + is_netconf, + is_cliconf, + etree_findall, + etree_find, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + conditional, + remove_default_spec, +) + + +def validate_mtu(value): + if value and not 64 <= int(value) <= 65535: + return False, "mtu must be between 64 and 65535" + return True, None + + +class ConfigBase(object): + def __init__(self, module): + self._module = module + self._result = {"changed": False, "warnings": []} + self._want = list() + self._have = list() + + def validate_param_values(self, param=None): + for key, value in param.items(): + # validate the param value (if validator func exists) + validator = globals().get("validate_%s" % key) + if callable(validator): + rc, msg = validator(value) + if not rc: + self._module.fail_json(msg=msg) + + def map_params_to_obj(self): + aggregate = self._module.params.get("aggregate") + if aggregate: + for item in aggregate: + for key in item: + if item.get(key) is None: + item[key] = self._module.params[key] + + self.validate_param_values(item) + d = item.copy() + + match = re.match(r"(^[a-z]+)([0-9/]+$)", d["name"], re.I) + if match: + d["owner"] = match.groups()[0] + + if d["active"] == "preconfigure": + d["active"] = "pre" + else: + d["active"] = "act" + + self._want.append(d) + + else: + self.validate_param_values(self._module.params) + params = { + "name": self._module.params["name"], + "description": self._module.params["description"], + "speed": self._module.params["speed"], + "mtu": self._module.params["mtu"], + "duplex": self._module.params["duplex"], + "state": self._module.params["state"], + "delay": self._module.params["delay"], + "tx_rate": self._module.params["tx_rate"], + "rx_rate": self._module.params["rx_rate"], + "enabled": self._module.params["enabled"], + "active": self._module.params["active"], + } + + match = re.match(r"(^[a-z]+)([0-9/]+$)", params["name"], re.I) + if match: + params["owner"] = match.groups()[0] + + if params["active"] == "preconfigure": + params["active"] = "pre" + else: + params["active"] = "act" + + self._want.append(params) + + +class CliConfiguration(ConfigBase): + def __init__(self, module): + super(CliConfiguration, self).__init__(module) + + def parse_shutdown(self, intf_config): + for cfg in intf_config: + match = re.search(r"%s" % "shutdown", cfg, re.M) + if match: + return True + return False + + def parse_config_argument(self, intf_config, arg): + for cfg in intf_config: + match = re.search(r"%s (.+)$" % arg, cfg, re.M) + if match: + return match.group(1) + + def search_obj_in_list(self, name): + for obj in self._have: + if obj["name"] == name: + return obj + return None + + def map_config_to_obj(self): + data = get_config(self._module, config_filter="interface") + data_lines = data.splitlines() + start_indexes = [ + i for i, e in enumerate(data_lines) if e.startswith("interface") + ] + end_indexes = [i for i, e in enumerate(data_lines) if e == "!"] + + intf_configs = list() + for start_index, end_index in zip(start_indexes, end_indexes): + intf_configs.append( + [i.strip() for i in data_lines[start_index:end_index]] + ) + + if not intf_configs: + return list() + + for intf_config in intf_configs: + name = intf_config[0].strip().split()[1] + + active = "act" + if name == "preconfigure": + active = "pre" + name = intf_config[0].strip().split()[2] + + obj = { + "name": name, + "description": self.parse_config_argument( + intf_config, "description" + ), + "speed": self.parse_config_argument(intf_config, "speed"), + "duplex": self.parse_config_argument(intf_config, "duplex"), + "mtu": self.parse_config_argument(intf_config, "mtu"), + "enabled": not bool(self.parse_shutdown(intf_config)), + "active": active, + "state": "present", + } + self._have.append(obj) + + def map_obj_to_commands(self): + commands = list() + + args = ("speed", "description", "duplex", "mtu") + for want_item in self._want: + name = want_item["name"] + disable = not want_item["enabled"] + state = want_item["state"] + + obj_in_have = self.search_obj_in_list(name) + 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 = want_item.get(item) + running = obj_in_have.get(item) + if candidate != running: + if candidate: + cmd = ( + interface + + " " + + item + + " " + + str(candidate) + ) + commands.append(cmd) + + if disable and obj_in_have.get("enabled", False): + commands.append(interface + " shutdown") + elif not disable and not obj_in_have.get("enabled", False): + commands.append("no " + interface + " shutdown") + else: + for item in args: + value = want_item.get(item) + if value: + commands.append( + interface + " " + item + " " + str(value) + ) + if not disable: + commands.append("no " + interface + " shutdown") + self._result["commands"] = commands + + if commands: + commit = not self._module.check_mode + diff = load_config(self._module, commands, commit=commit) + if diff: + self._result["diff"] = dict(prepared=diff) + self._result["changed"] = True + + def check_declarative_intent_params(self): + failed_conditions = [] + for want_item in self._want: + want_state = want_item.get("state") + want_tx_rate = want_item.get("tx_rate") + want_rx_rate = want_item.get("rx_rate") + if ( + want_state not in ("up", "down") + and not want_tx_rate + and not want_rx_rate + ): + continue + + if self._result["changed"]: + sleep(want_item["delay"]) + + command = "show interfaces {0!s}".format(want_item["name"]) + out = run_commands(self._module, command)[0] + + if want_state in ("up", "down"): + match = re.search(r"%s (\w+)" % "line protocol is", out, re.M) + have_state = None + if match: + have_state = match.group(1) + if have_state.strip() == "administratively": + match = re.search( + r"%s (\w+)" % "administratively", out, re.M + ) + 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({0!s})".format(want_state) + ) + + if want_tx_rate: + match = re.search(r"%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(r"%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 failed_conditions: + msg = "One or more conditional statements have not been satisfied" + self._module.fail_json( + msg=msg, failed_conditions=failed_conditions + ) + + def run(self): + self.map_params_to_obj() + self.map_config_to_obj() + self.map_obj_to_commands() + self.check_declarative_intent_params() + + return self._result + + +class NCConfiguration(ConfigBase): + def __init__(self, module): + super(NCConfiguration, self).__init__(module) + + self._intf_meta = collections.OrderedDict() + self._shut_meta = collections.OrderedDict() + self._data_rate_meta = collections.OrderedDict() + self._line_state_meta = collections.OrderedDict() + + def map_obj_to_xml_rpc(self): + self._intf_meta.update( + [ + ( + "interface-configuration", + { + "xpath": "interface-configurations/interface-configuration", + "tag": True, + "attrib": "operation", + }, + ), + ( + "a:active", + { + "xpath": "interface-configurations/interface-configuration/active", + "operation": "edit", + }, + ), + ( + "a:name", + { + "xpath": "interface-configurations/interface-configuration/interface-name" + }, + ), + ( + "a:description", + { + "xpath": "interface-configurations/interface-configuration/description", + "operation": "edit", + }, + ), + ( + "mtus", + { + "xpath": "interface-configurations/interface-configuration/mtus", + "tag": True, + "operation": "edit", + }, + ), + ( + "mtu", + { + "xpath": "interface-configurations/interface-configuration/mtus/mtu", + "tag": True, + "operation": "edit", + }, + ), + ( + "a:owner", + { + "xpath": "interface-configurations/interface-configuration/mtus/mtu/owner", + "operation": "edit", + }, + ), + ( + "a:mtu", + { + "xpath": "interface-configurations/interface-configuration/mtus/mtu/mtu", + "operation": "edit", + }, + ), + ( + "CEthernet", + { + "xpath": "interface-configurations/interface-configuration/ethernet", + "tag": True, + "operation": "edit", + "ns": True, + }, + ), + ( + "a:speed", + { + "xpath": "interface-configurations/interface-configuration/ethernet/speed", + "operation": "edit", + }, + ), + ( + "a:duplex", + { + "xpath": "interface-configurations/interface-configuration/ethernet/duplex", + "operation": "edit", + }, + ), + ] + ) + + self._shut_meta.update( + [ + ( + "interface-configuration", + { + "xpath": "interface-configurations/interface-configuration", + "tag": True, + }, + ), + ( + "a:active", + { + "xpath": "interface-configurations/interface-configuration/active", + "operation": "edit", + }, + ), + ( + "a:name", + { + "xpath": "interface-configurations/interface-configuration/interface-name" + }, + ), + ( + "shutdown", + { + "xpath": "interface-configurations/interface-configuration/shutdown", + "tag": True, + "operation": "edit", + "attrib": "operation", + }, + ), + ] + ) + state = self._module.params["state"] + + _get_filter = build_xml( + "interface-configurations", + xmap=self._intf_meta, + params=self._want, + opcode="filter", + ) + + running = get_config( + self._module, source="running", config_filter=_get_filter + ) + intfcfg_nodes = etree_findall(running, "interface-configuration") + + intf_list = set() + shut_list = set() + for item in intfcfg_nodes: + intf_name = etree_find(item, "interface-name").text + if intf_name is not None: + intf_list.add(intf_name) + + if etree_find(item, "shutdown") is not None: + shut_list.add(intf_name) + + intf_params = list() + shut_params = list() + noshut_params = list() + for index, item in enumerate(self._want): + if item["name"] in intf_list: + intf_params.append(item) + if not item["enabled"]: + shut_params.append(item) + if item["name"] in shut_list and item["enabled"]: + noshut_params.append(item) + + opcode = None + if state == "absent": + if intf_params: + opcode = "delete" + elif state in ("present", "up", "down"): + intf_params = self._want + opcode = "merge" + + self._result["xml"] = [] + _edit_filter_list = list() + if opcode: + _edit_filter_list.append( + build_xml( + "interface-configurations", + xmap=self._intf_meta, + params=intf_params, + opcode=opcode, + ) + ) + + if opcode == "merge": + if len(shut_params): + _edit_filter_list.append( + build_xml( + "interface-configurations", + xmap=self._shut_meta, + params=shut_params, + opcode="merge", + ) + ) + if len(noshut_params): + _edit_filter_list.append( + build_xml( + "interface-configurations", + xmap=self._shut_meta, + params=noshut_params, + opcode="delete", + ) + ) + diff = None + if len(_edit_filter_list): + commit = not self._module.check_mode + diff = load_config( + self._module, + _edit_filter_list, + commit=commit, + running=running, + nc_get_filter=_get_filter, + ) + + if diff: + if self._module._diff: + self._result["diff"] = dict(prepared=diff) + + self._result["xml"] = _edit_filter_list + self._result["changed"] = True + + def check_declarative_intent_params(self): + failed_conditions = [] + + self._data_rate_meta.update( + [ + ( + "interfaces", + {"xpath": "infra-statistics/interfaces", "tag": True}, + ), + ( + "interface", + { + "xpath": "infra-statistics/interfaces/interface", + "tag": True, + }, + ), + ( + "a:name", + { + "xpath": "infra-statistics/interfaces/interface/interface-name" + }, + ), + ( + "cache", + { + "xpath": "infra-statistics/interfaces/interface/cache", + "tag": True, + }, + ), + ( + "data-rate", + { + "xpath": "infra-statistics/interfaces/interface/cache/data-rate", + "tag": True, + }, + ), + ( + "input-data-rate", + { + "xpath": "infra-statistics/interfaces/interface/cache/data-rate/input-data-rate", + "tag": True, + }, + ), + ( + "output-data-rate", + { + "xpath": "infra-statistics/interfaces/interface/cache/data-rate/output-data-rate", + "tag": True, + }, + ), + ] + ) + + self._line_state_meta.update( + [ + ( + "data-nodes", + {"xpath": "interface-properties/data-nodes", "tag": True}, + ), + ( + "data-node", + { + "xpath": "interface-properties/data-nodes/data-node", + "tag": True, + }, + ), + ( + "system-view", + { + "xpath": "interface-properties/data-nodes/data-node/system-view", + "tag": True, + }, + ), + ( + "interfaces", + { + "xpath": "interface-properties/data-nodes/data-node/system-view/interfaces", + "tag": True, + }, + ), + ( + "interface", + { + "xpath": "interface-properties/data-nodes/data-node/system-view/interfaces/interface", + "tag": True, + }, + ), + ( + "a:name", + { + "xpath": "interface-properties/data-nodes/data-node/system-view/interfaces/interface/interface-name" + }, + ), + ( + "line-state", + { + "xpath": "interface-properties/data-nodes/data-node/system-view/interfaces/interface/line-state", + "tag": True, + }, + ), + ] + ) + + _rate_filter = build_xml( + "infra-statistics", + xmap=self._data_rate_meta, + params=self._want, + opcode="filter", + ) + out = get_oper(self._module, filter=_rate_filter) + data_rate_list = etree_findall(out, "interface") + data_rate_map = dict() + for item in data_rate_list: + data_rate_map.update( + {etree_find(item, "interface-name").text: dict()} + ) + data_rate_map[etree_find(item, "interface-name").text].update( + { + "input-data-rate": etree_find( + item, "input-data-rate" + ).text, + "output-data-rate": etree_find( + item, "output-data-rate" + ).text, + } + ) + + _line_state_filter = build_xml( + "interface-properties", + xmap=self._line_state_meta, + params=self._want, + opcode="filter", + ) + out = get_oper(self._module, filter=_line_state_filter) + line_state_list = etree_findall(out, "interface") + line_state_map = dict() + for item in line_state_list: + line_state_map.update( + { + etree_find(item, "interface-name") + .text: etree_find(item, "line-state") + .text + } + ) + + for want_item in self._want: + want_state = want_item.get("state") + want_tx_rate = want_item.get("tx_rate") + want_rx_rate = want_item.get("rx_rate") + if ( + want_state not in ("up", "down") + and not want_tx_rate + and not want_rx_rate + ): + continue + + if self._result["changed"]: + sleep(want_item["delay"]) + + if want_state in ("up", "down"): + if want_state not in line_state_map[want_item["name"]]: + failed_conditions.append( + "state " + "eq({0!s})".format(want_state) + ) + + if want_tx_rate: + if ( + want_tx_rate + != data_rate_map[want_item["name"]]["output-data-rate"] + ): + failed_conditions.append("tx_rate " + want_tx_rate) + + if want_rx_rate: + if ( + want_rx_rate + != data_rate_map[want_item["name"]]["input-data-rate"] + ): + failed_conditions.append("rx_rate " + want_rx_rate) + + if failed_conditions: + msg = "One or more conditional statements have not been satisfied" + self._module.fail_json( + msg=msg, failed_conditions=failed_conditions + ) + + def run(self): + self.map_params_to_obj() + self.map_obj_to_xml_rpc() + self.check_declarative_intent_params() + return self._result + + +def main(): + """ main entry point for module execution + """ + element_spec = dict( + name=dict(type="str"), + description=dict(type="str"), + speed=dict(choices=["10", "100", "1000"]), + mtu=dict(), + duplex=dict(choices=["full", "half"]), + enabled=dict(default=True, type="bool"), + active=dict( + type="str", choices=["active", "preconfigure"], default="active" + ), + tx_rate=dict(), + rx_rate=dict(), + 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(iosxr_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, + ) + + config_object = None + if is_cliconf(module): + # Commenting the below cliconf deprecation support call for Ansible 2.9 as it'll be continued to be supported + # module.deprecate("cli support for 'iosxr_interface' is deprecated. Use transport netconf instead", + # version='2.9') + config_object = CliConfiguration(module) + elif is_netconf(module): + if module.params["active"] == "preconfigure": + module.fail_json( + msg="Physical interface pre-configuration is not supported with transport 'netconf'" + ) + config_object = NCConfiguration(module) + + result = {} + if config_object: + result = config_object.run() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_interfaces.py new file mode 100644 index 00000000..8a607f65 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_interfaces.py @@ -0,0 +1,558 @@ +#!/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 iosxr_interfaces +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: iosxr_interfaces +short_description: Interfaces resource module +description: This module manages the interface attributes on Cisco IOS-XR network + devices. +version_added: 1.0.0 +author: +- Sumit Jaiswal (@justjais) +- Rohit Thakur (@rohitthakur2590) +notes: +- Tested against Cisco IOS-XRv Version 6.1.3 on VIRL. +- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html). +options: + config: + description: A dictionary of interface options + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface to configure in C(type + path) format. e.g. C(GigabitEthernet0/0/0/0) + type: str + required: true + description: + description: + - Interface description. + type: str + enabled: + default: true + description: + - Administrative state of the interface. + - Set the value to C(True) to administratively enable the interface or C(False) + to disable it. + type: bool + speed: + description: + - Configure the speed for an interface. Default is auto-negotiation when not + configured. + type: int + mtu: + description: + - Sets the MTU value for the interface. Applicable for Ethernet interfaces + only. + - Refer to vendor documentation for valid values. + type: int + duplex: + description: + - Configures the interface duplex mode. Default is auto-negotiation when not + configured. + type: str + choices: + - full + - half + 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-XR device + by executing the command B(show running-config 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 + - parsed + - deleted + - replaced + - rendered + - gathered + - overridden + default: merged + description: + - The state of the configuration after module completion + type: str +""" + +EXAMPLES = """ +# Using merged +# Before state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# vrf custB +# ipv4 address 178.18.169.23 255.255.255.0 +# dot1q native vlan 30 +# ! +# interface GigabitEthernet0/0/0/3 +# description Replaced by Ansible Team +# mtu 2000 +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# dot1q native vlan 1021 +# ! +- name: Configure Ethernet interfaces + cisco.iosxr.iosxr_interfaces: + config: + - name: GigabitEthernet0/0/0/2 + description: Configured by Ansible + enabled: true + - name: GigabitEthernet0/0/0/3 + description: Configured by Ansible Network + enabled: false + duplex: full + state: merged +# After state: +# ------------ +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# description Configured and Merged by Ansible Network +# vrf custB +# ipv4 address 178.18.169.23 255.255.255.0 +# dot1q native vlan 30 +# ! +# interface GigabitEthernet0/0/0/3 +# description Configured and Merged by Ansible Network +# mtu 2600 +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex full +# shutdown +# dot1q native vlan 1021 +# ! +# Using replaced +# Before state: +# ------------ +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# description Configured by Ansible +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# description Test +# vrf custB +# ipv4 address 178.18.169.23 255.255.255.0 +# dot1q native vlan 30 +# ! +# interface GigabitEthernet0/0/0/3 +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# dot1q native vlan 1021 +# ! +- name: Configure following interfaces and replace their existing config + cisco.iosxr.iosxr_interfaces: + config: + - name: GigabitEthernet0/0/0/2 + description: Configured by Ansible + enabled: true + mtu: 2000 + - name: GigabitEthernet0/0/0/3 + description: Configured by Ansible Network + enabled: false + duplex: auto + state: replaced +# After state: +# ------------ +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# description Configured by Ansible +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# description Configured and Replaced by Ansible +# mtu 2000 +# vrf custB +# ipv4 address 178.18.169.23 255.255.255.0 +# dot1q native vlan 30 +# ! +# interface GigabitEthernet0/0/0/3 +# description Configured and Replaced by Ansible Network +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex half +# shutdown +# dot1q native vlan 1021 +# ! +# Using overridden +# Before state: +# ------------ +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# description Configured by Ansible +# vrf custB +# ipv4 address 178.18.169.23 255.255.255.0 +# dot1q native vlan 30 +# ! +# interface GigabitEthernet0/0/0/3 +# description Configured by Ansible +# mtu 2600 +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex full +# shutdown +# dot1q native vlan 1021 +# ! +- name: Override interfaces + cisco.iosxr.iosxr_interfaces: + config: + - name: GigabitEthernet0/0/0/2 + description: Configured by Ansible + enabled: true + duplex: auto + - name: GigabitEthernet0/0/0/3 + description: Configured by Ansible Network + enabled: false + speed: 1000 + state: overridden +# After state: +# ------------ +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# description Configured and Overridden by Ansible Network +# vrf custB +# ipv4 address 178.18.169.23 255.255.255.0 +# speed 1000 +# dot1q native vlan 30 +# ! +# interface GigabitEthernet0/0/0/3 +# description Configured and Overridden by Ansible Network +# mtu 2000 +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex full +# shutdown +# dot1q native vlan 1021 +# ! +# Using deleted +# Before state: +# ------------ +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# description Configured and Overridden by Ansible Network +# vrf custB +# ipv4 address 178.18.169.23 255.255.255.0 +# speed 1000 +# dot1q native vlan 30 +# ! +# interface GigabitEthernet0/0/0/3 +# description Configured and Overridden by Ansible Network +# mtu 2000 +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex full +# shutdown +# dot1q native vlan 1021 +# ! +- name: Delete IOSXR interfaces as in given arguments + cisco.iosxr.iosxr_interfaces: + config: + - name: GigabitEthernet0/0/0/2 + - name: GigabitEthernet0/0/0/3 + state: deleted +# After state: +# ------------ +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# vrf custB +# ipv4 address 178.18.169.23 255.255.255.0 +# dot1q native vlan 30 +# ! +# interface GigabitEthernet0/0/0/3 +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# dot1q native vlan 1021 +# ! +# Using parsed +# parsed.cfg +# ------------ +# +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 10.8.38.70 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/0 +# description Configured and Merged by Ansible-Network +# mtu 110 +# ipv4 address 172.31.1.1 255.255.255.0 +# duplex half +# ! +# interface GigabitEthernet0/0/0/3 +# shutdown +# ! +# interface GigabitEthernet0/0/0/4 +# shutdown +# ! +# - name: Convert ACL interfaces config to argspec without connecting to the appliance +# cisco.iosxr.iosxr_interfaces: +# running_config: "{{ lookup('file', './parsed.cfg') }}" +# state: parsed +# Task Output (redacted) +# ----------------------- +# "parsed": [ +# { +# "name": "MgmtEth0/RP0/CPU0/0" +# }, +# { +# "access_groups": [ +# { +# "acls": [ +# { +# "direction": "in", +# "name": "acl_1" +# }, +# { +# "direction": "out", +# "name": "acl_2" +# } +# ], +# "afi": "ipv4" +# }, +# { +# "acls": [ +# { +# "direction": "in", +# "name": "acl6_1" +# }, +# { +# "direction": "out", +# "name": "acl6_2" +# } +# ], +# "afi": "ipv6" +# } +# ], +# "name": "GigabitEthernet0/0/0/0" +# }, +# { +# "access_groups": [ +# { +# "acls": [ +# { +# "direction": "out", +# "name": "acl_1" +# } +# ], +# "afi": "ipv4" +# } +# ], +# "name": "GigabitEthernet0/0/0/1" +# } +# ] +# } +# Using rendered +- name: Render platform specific commands from task input using rendered state + cisco.iosxr.iosxr_interfaces: + config: + - name: GigabitEthernet0/0/0/0 + description: Configured and Merged by Ansible-Network + mtu: 110 + enabled: true + duplex: half + - name: GigabitEthernet0/0/0/1 + description: Configured and Merged by Ansible-Network + mtu: 2800 + enabled: false + speed: 100 + duplex: full + state: rendered +# Task Output (redacted) +# ----------------------- +# "rendered": [ +# "interface GigabitEthernet0/0/0/0", +# "description Configured and Merged by Ansible-Network", +# "mtu 110", +# "duplex half", +# "no shutdown", +# "interface GigabitEthernet0/0/0/1", +# "description Configured and Merged by Ansible-Network", +# "mtu 2800", +# "speed 100", +# "duplex full", +# "shutdown" +# ] +# Using gathered +# Before state: +# ------------ +# +# RP/0/0/CPU0:an-iosxr-02#show running-config interface +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 10.8.38.70 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/0 +# description Configured and Merged by Ansible-Network +# mtu 110 +# ipv4 address 172.31.1.1 255.255.255.0 +# duplex half +# ! +# interface GigabitEthernet0/0/0/3 +# shutdown +# ! +# interface GigabitEthernet0/0/0/4 +# shutdown +# ! +- name: Gather IOSXR interfaces as in given arguments + cisco.iosxr.iosxr_interfaces: + config: + state: gathered +# Task Output (redacted) +# ----------------------- +# +# "gathered": [ +# { +# "description": "test for ansible", +# "enabled": false, +# "name": "Loopback888" +# }, +# { +# "description": "Configured and Merged by Ansible-Network", +# "duplex": "half", +# "enabled": true, +# "mtu": 110, +# "name": "GigabitEthernet0/0/0/0" +# }, +# { +# "enabled": false, +# "name": "GigabitEthernet0/0/0/3" +# }, +# { +# "enabled": false, +# "name": "GigabitEthernet0/0/0/4" +# } +# ] +# After state: +# ------------ +# +# RP/0/0/CPU0:an-iosxr-02#show running-config interface +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 10.8.38.70 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/0 +# description Configured and Merged by Ansible-Network +# mtu 110 +# ipv4 address 172.31.1.1 255.255.255.0 +# duplex half +# ! +# interface GigabitEthernet0/0/0/3 +# shutdown +# ! +# interface GigabitEthernet0/0/0/4 +# shutdown +# ! +""" + +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/0/0/2', 'description: Configured by Ansible', 'shutdown'] +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.interfaces.interfaces import ( + InterfacesArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.config.interfaces.interfaces import ( + Interfaces, +) + + +def main(): + """ + Main entry point for module execution + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + module = AnsibleModule( + argument_spec=InterfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive, + ) + + result = Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_l2_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_l2_interfaces.py new file mode 100644 index 00000000..c92104d9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_l2_interfaces.py @@ -0,0 +1,687 @@ +#!/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 + +GENERATOR_VERSION = "1.0" + + +DOCUMENTATION = """ +module: iosxr_l2_interfaces +short_description: L2 interfaces resource module +description: This module manages the Layer-2 interface attributes on Cisco IOS-XR + devices. +version_added: 1.0.0 +author: +- Sumit Jaiswal (@justjais) +- Rohit Thakur (@rohitthakur2590) +notes: +- Tested against Cisco IOS-XRv Version 6.1.3 on VIRL. +- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html). +options: + config: + description: A dictionary of Layer-2 interface options + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface/sub-interface excluding any logical unit number, + e.g. GigabitEthernet0/0/0/1 or GigabitEthernet0/0/0/1.100. + type: str + required: true + native_vlan: + description: + - Configure a native VLAN ID for the trunk + type: int + l2transport: + description: + - Switchport mode access command to configure the interface as a layer 2 access + type: bool + l2protocol: + description: + - Configures Layer 2 protocol tunneling and protocol data unit (PDU) filtering + on an interface. + type: list + elements: dict + suboptions: + cdp: + description: + - Cisco Discovery Protocol (CDP) tunneling and data unit parameters. + choices: + - drop + - forward + - tunnel + type: str + pvst: + description: + - Configures the per-VLAN Spanning Tree Protocol (PVST) tunneling and + data unit parameters. + choices: + - drop + - forward + - tunnel + type: str + stp: + description: + - Spanning Tree Protocol (STP) tunneling and data unit parameters. + choices: + - drop + - forward + - tunnel + type: str + vtp: + description: + - VLAN Trunk Protocol (VTP) tunneling and data unit parameters. + choices: + - drop + - forward + - tunnel + type: str + q_vlan: + description: + - 802.1Q VLAN configuration. Note that it can accept either 2 VLAN IDs when + configuring Q-in-Q VLAN, or it will accept 1 VLAN ID and 'any' as input + list when configuring Q-in-any vlan as input. Note, that this option is + valid only with respect to Sub-Interface and is not valid when configuring + for Interface. + type: list + elements: int + propagate: + description: + - Propagate Layer 2 transport events. Note that it will work only when the + I(l2tranport) option is set to TRUE + 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-XR device + by executing the command B(show running-config 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 of the configuration after module completion + type: str +""" + +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/3 +# description Ansible Network +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex half +# shutdown +# ! +# interface GigabitEthernet0/0/0/4 +# description Test description +# ! + +- name: Merge provided configuration with device configuration + cisco.iosxr.iosxr_l2_interfaces: + config: + - name: GigabitEthernet0/0/0/3 + native_vlan: 20 + - name: GigabitEthernet0/0/0/4 + native_vlan: 40 + l2transport: true + l2protocol: + - stp: tunnel + - name: GigabitEthernet0/0/0/3.900 + l2transport: true + q_vlan: + - 20 + - 40 + state: merged + +# After state: +# ------------ +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/3 +# description Ansible Network +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex half +# shutdown +# dot1q native vlan 20 +# ! +# interface GigabitEthernet0/0/0/4 +# description Test description +# dot1q native vlan 10 +# l2transport +# l2protocol stp tunnel +# ! +# ! +# interface GigabitEthernet0/0/0/3.900 l2transport +# dot1q vlan 20 40 +# ! + +# Using replaced +# +# Before state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/3 +# description Ansible Network +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex half +# shutdown +# dot1q native vlan 20 +# ! +# interface GigabitEthernet0/0/0/4 +# description Test description +# dot1q native vlan 10 +# l2transport +# l2protocol stp tunnel +# ! +# ! +# interface GigabitEthernet0/0/0/3.900 l2transport +# dot1q vlan 20 40 +# ! + +- name: Replaces device configuration of listed interfaces with provided configuration + cisco.iosxr.iosxr_l2_interfaces: + config: + - name: GigabitEthernet0/0/0/4 + native_vlan: 40 + l2transport: true + l2protocol: + - stp: forward + - name: GigabitEthernet0/0/0/3.900 + q_vlan: + - 20 + - any + state: replaced + +# After state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/3 +# description Ansible Network +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex half +# shutdown +# dot1q native vlan 20 +# ! +# interface GigabitEthernet0/0/0/4 +# description Test description +# dot1q native vlan 40 +# l2transport +# l2protocol stp forward +# ! +# ! +# interface GigabitEthernet0/0/0/3.900 l2transport +# dot1q vlan 20 any +# ! + +# Using overridden +# +# Before state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/3 +# description Ansible Network +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex half +# shutdown +# dot1q native vlan 20 +# ! +# interface GigabitEthernet0/0/0/4 +# description Test description +# dot1q native vlan 10 +# l2transport +# l2protocol stp tunnel +# ! +# ! +# interface GigabitEthernet0/0/0/3.900 l2transport +# dot1q vlan 20 40 +# ! + +- name: Override device configuration of all interfaces with provided configuration + cisco.iosxr.iosxr_l2_interfaces: + config: + - name: GigabitEthernet0/0/0/4 + native_vlan: 40 + l2transport: true + l2protocol: + - stp: forward + - name: GigabitEthernet0/0/0/3.900 + q_vlan: + - 20 + - any + state: overridden + +# After state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/3 +# description Ansible Network +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex half +# shutdown +# ! +# interface GigabitEthernet0/0/0/4 +# description Test description +# dot1q native vlan 40 +# l2transport +# l2protocol stp forward +# ! +# ! +# interface GigabitEthernet0/0/0/3.900 +# dot1q vlan 20 any +# ! + +# Using deleted +# +# Before state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/3 +# description Ansible Network +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex half +# shutdown +# dot1q native vlan 20 +# ! +# interface GigabitEthernet0/0/0/4 +# description Test description +# dot1q native vlan 10 +# l2transport +# l2protocol stp tunnel +# ! +# ! +# + +- name: "Delete L2 attributes of given interfaces (Note: This won't delete the interface itself)" + cisco.iosxr.iosxr_l2_interfaces: + config: + - name: GigabitEthernet0/0/0/4 + state: deleted + +# After state: +# ------------ +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/3 +# description Ansible Network +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex half +# shutdown +# dot1q native vlan 20 +# ! +# interface GigabitEthernet0/0/0/4 +# description Test description +# ! + +# Using Deleted without any config passed +# "(NOTE: This will delete all of configured resource module attributes from each configured interface)" +# +# Before state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/3 +# description Ansible Network +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex half +# shutdown +# dot1q native vlan 20 +# ! +# interface GigabitEthernet0/0/0/4 +# description Test description +# dot1q native vlan 10 +# l2transport +# l2protocol stp tunnel +# ! +# ! + +- name: "Delete L2 attributes of all interfaces (Note: This won't delete the interface itself)" + cisco.iosxr.iosxr_l2_interfaces: + state: deleted + +# After state: +# ------------ +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/3 +# description Ansible Network +# vrf custB +# ipv4 address 10.10.0.2 255.255.255.0 +# duplex half +# shutdown +# ! +# interface GigabitEthernet0/0/0/4 +# description Test description +# ! + + +# Using parsed +# parsed.cfg +# ------------ +# +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 10.8.38.70 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/0 +# description Configured and Merged by Ansible-Network +# mtu 110 +# ipv4 address 172.31.1.1 255.255.255.0 +# duplex half +# ! +# interface GigabitEthernet0/0/0/1 +# dot1q native vlan 10 +# l2transport +# l2protocol cdp forward +# l2protocol pvst tunnel +# propagate remote-status +# ! +# ! +# interface GigabitEthernet0/0/0/3 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3.900 +# encapsulation dot1q 20 second-dot1q 40 +# ! +# interface GigabitEthernet0/0/0/4 +# shutdown +# dot1q native vlan 40 +# ! +- name: Convert L2 interfaces config to argspec without connecting to the appliance + cisco.iosxr.iosxr_l2_interfaces: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# Task Output (redacted) +# ----------------------- +# "parsed": [ +# { +# "name": "GigabitEthernet0/0/0/0" +# }, +# { +# "l2protocol": [ +# { +# "cdp": "forward" +# }, +# { +# "pvst": "tunnel" +# } +# ], +# "l2transport": true, +# "name": "GigabitEthernet0/0/0/1", +# "native_vlan": 10, +# "propagate": true +# }, +# { +# "name": "GigabitEthernet0/0/0/3" +# }, +# { +# "name": "GigabitEthernet0/0/0/3.900", +# "q_vlan": [ +# 20, +# 40 +# ] +# }, +# { +# "name": "GigabitEthernet0/0/0/4", +# "native_vlan": 40 +# } +# ] + + +# Using rendered +- name: Render platform specific commands from task input using rendered state + cisco.iosxr.iosxr_l2_interfaces: + config: + + - name: GigabitEthernet0/0/0/1 + native_vlan: 10 + l2transport: true + l2protocol: + + - pvst: tunnel + + - cdp: forward + propagate: true + + - name: GigabitEthernet0/0/0/3.900 + q_vlan: + - 20 + - 40 + + - name: GigabitEthernet0/0/0/4 + native_vlan: 40 + state: rendered +# Task Output (redacted) +# ----------------------- +# "rendered": [ +# "interface GigabitEthernet0/0/0/1", +# "dot1q native vlan 10", +# "l2transport l2protocol pvst tunnel", +# "l2transport l2protocol cdp forward", +# "l2transport propagate remote-status", +# "interface GigabitEthernet0/0/0/3.900", +# "dot1q vlan 20 40", +# "interface GigabitEthernet0/0/0/4", +# "dot1q native vlan 40" +# ] + + +# Using gathered +# Before state: +# ------------ +# +# RP/0/0/CPU0:an-iosxr-02#show running-config interface +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 10.8.38.70 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/0 +# description Configured and Merged by Ansible-Network +# mtu 110 +# ipv4 address 172.31.1.1 255.255.255.0 +# duplex half +# ! +# interface GigabitEthernet0/0/0/1 +# dot1q native vlan 10 +# l2transport +# l2protocol cdp forward +# l2protocol pvst tunnel +# propagate remote-status +# ! +# ! +# interface GigabitEthernet0/0/0/3 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3.900 +# encapsulation dot1q 20 second-dot1q 40 +# ! +# interface GigabitEthernet0/0/0/4 +# shutdown +# dot1q native vlan 40 +# ! +- name: Gather IOSXR l2 interfaces as in given arguments + cisco.iosxr.iosxr_l2_interfaces: + config: + state: gathered +# Task Output (redacted) +# ----------------------- +# +# "gathered": [ +# { +# "name": "GigabitEthernet0/0/0/0" +# }, +# { +# "l2protocol": [ +# { +# "cdp": "forward" +# }, +# { +# "pvst": "tunnel" +# } +# ], +# "l2transport": true, +# "name": "GigabitEthernet0/0/0/1", +# "native_vlan": 10, +# "propagate": true +# }, +# { +# "name": "GigabitEthernet0/0/0/3" +# }, +# { +# "name": "GigabitEthernet0/0/0/3.900", +# "q_vlan": [ +# 20, +# 40 +# ] +# }, +# { +# "name": "GigabitEthernet0/0/0/4", +# "native_vlan": 40 +# } +# ] +# After state: +# ------------ +# +# RP/0/0/CPU0:an-iosxr-02#show running-config interface +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 10.8.38.70 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/0 +# description Configured and Merged by Ansible-Network +# mtu 110 +# ipv4 address 172.31.1.1 255.255.255.0 +# duplex half +# ! +# interface GigabitEthernet0/0/0/1 +# dot1q native vlan 10 +# l2transport +# l2protocol cdp forward +# l2protocol pvst tunnel +# propagate remote-status +# ! +# ! +# interface GigabitEthernet0/0/0/3 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3.900 +# encapsulation dot1q 20 second-dot1q 40 +# ! +# interface GigabitEthernet0/0/0/4 +# shutdown +# dot1q native vlan 40 +# ! + + + +""" + +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/0/0/2', 'l2transport l2protocol pvst tunnel'] +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.l2_interfaces.l2_interfaces import ( + L2_InterfacesArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.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", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + module = AnsibleModule( + argument_spec=L2_InterfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive, + ) + + result = L2_Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_l3_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_l3_interfaces.py new file mode 100644 index 00000000..07af8314 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_l3_interfaces.py @@ -0,0 +1,671 @@ +#!/usr/bin/python +# -*- 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) + +############################################## +# 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_l3_interfaces +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: iosxr_l3_interfaces +short_description: L3 interfaces resource module +description: This module provides declarative management of Layer-3 interface on Cisco + IOS-XR devices. +version_added: 1.0.0 +author: +- Sumit Jaiswal (@justjais) +- Rohit Thakur (@rohitthakur2590) +notes: +- Tested against Cisco IOS-XRv Version 6.1.3 on VIRL. +- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html). +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 + 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-XR device + by executing the command B(show running-config 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 + - parsed + - rendered + - gathered + default: merged + description: + - The state of the configuration after module completion + type: str +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3 +# ipv4 address 192.168.0.2 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3.700 +# ! +# interface GigabitEthernet0/0/0/4 +# ipv6 address fd5d:12c9:2201:1::1/64 +# shutdown +# ! + +- name: Merge provided configuration with device configuration + cisco.iosxr.iosxr_l3_interfaces: + config: + - name: GigabitEthernet0/0/0/2 + ipv4: + - address: 192.168.0.1/24 + - name: GigabitEthernet0/0/0/3 + ipv4: + - address: 192.168.2.1/24 + secondary: true + state: merged + +# After state: +# ------------ +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# ipv4 address 192.168.0.1 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3 +# ipv4 address 192.168.1.0 255.255.255.0 +# ipv4 address 192.168.2.1 255.255.255.0 secondary +# shutdown +# ! +# interface GigabitEthernet0/0/0/3.700 +# ! +# interface GigabitEthernet0/0/0/4 +# ipv6 address fd5d:12c9:2201:1::1/64 +# shutdown +# ! + +# Using overridden + +# Before state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# ipv4 address 192.168.0.1 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3 +# ipv4 address 192.168.1.0 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3.700 +# ! +# interface GigabitEthernet0/0/0/4 +# ipv6 address fd5d:12c9:2201:1::1/64 +# shutdown +# ! + +- name: Override device configuration of all interfaces with provided configuration + cisco.iosxr.iosxr_l3_interfaces: + config: + - name: GigabitEthernet0/0/0/3 + ipv4: + - address: 192.168.0.1/24 + - name: GigabitEthernet0/0/0/3.700 + ipv4: + - address: 192.168.0.2/24 + - address: 192.168.2.1/24 + secondary: true + state: overridden + +# After state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3 +# ipv4 address 192.168.0.1 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3.700 +# ipv4 address 192.168.0.2 255.255.255.0 +# ipv4 address 192.168.2.1 255.255.255.0 secondary +# ! +# interface GigabitEthernet0/0/0/4 +# shutdown +# ! + +# Using replaced + +# Before state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3 +# ipv4 address 192.168.0.2 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3.700 +# ipv4 address 192.168.0.1 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/4 +# ipv6 address fd5d:12c9:2201:1::1/64 +# shutdown +# ! + +- name: Replaces device configuration of listed interfaces with provided configuration + cisco.iosxr.iosxr_l3_interfaces: + config: + - name: GigabitEthernet0/0/0/3 + ipv6: + - address: fd5d:12c9:2201:1::1/64 + - name: GigabitEthernet0/0/0/4 + ipv4: + - address: 192.168.0.2/24 + state: replaced + +# After state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3 +# ipv6 address fd5d:12c9:2201:1::1/64 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3.700 +# ipv4 address 192.168.0.1 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/4 +# ipv4 address 192.168.0.2 255.255.255.0 +# shutdown +# ! + +# Using deleted + +# Before state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# ipv4 address 192.168.2.1 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# ipv4 address 192.168.3.1 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3 +# ipv4 address 192.168.0.2 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3.700 +# ipv4 address 192.168.0.1 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/4 +# ipv6 address fd5d:12c9:2201:1::1/64 +# shutdown +# ! + +- name: "Delete L3 attributes of given interfaces (Note: This won't delete the interface itself)" + cisco.iosxr.iosxr_l3_interfaces: + config: + - name: GigabitEthernet0/0/0/3 + - name: GigabitEthernet0/0/0/4 + - name: GigabitEthernet0/0/0/3.700 + state: deleted + +# After state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# ipv4 address 192.168.2.1 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# ipv4 address 192.168.3.1 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3.700 +# ! +# interface GigabitEthernet0/0/0/4 +# shutdown +# ! + +# Using Deleted without any config passed +# "(NOTE: This will delete all of configured resource module attributes from each configured interface)" + +# Before state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# ipv4 address 192.168.2.1 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# ipv4 address 192.168.3.1 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3 +# ipv4 address 192.168.0.2 255.255.255.0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3.700 +# ipv4 address 192.168.0.1 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/4 +# ipv6 address fd5d:12c9:2201:1::1/64 +# shutdown +# ! + + +- name: "Delete L3 attributes of all interfaces (Note: This won't delete the interface itself)" + cisco.iosxr.iosxr_l3_interfaces: + state: deleted + +# After state: +# ------------- +# +# viosxr#show running-config interface +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3.700 +# ! +# interface GigabitEthernet0/0/0/4 +# shutdown +# ! + + +# Using parsed +# parsed.cfg +# ------------ +# +# nterface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 10.8.38.70 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/0 +# description Configured and Merged by Ansible-Network +# mtu 66 +# ipv4 address 192.0.2.1 255.255.255.0 +# ipv4 address 192.0.2.2 255.255.255.0 secondary +# ipv6 address 2001:db8:0:3::/64 +# duplex half +# ! +# interface GigabitEthernet0/0/0/1 +# description Configured and Merged by Ansible-Network +# mtu 66 +# speed 100 +# duplex full +# dot1q native vlan 10 +# l2transport +# l2protocol cdp forward +# l2protocol pvst tunnel +# propagate remote-status +# ! +# ! +# interface GigabitEthernet0/0/0/3 +# ipv4 address 192.0.22.1 255.255.255.0 +# ipv4 address 192.0.23.1 255.255.255.0 +# ! +# - name: Convert L3 interfaces config to argspec without connecting to the appliance +# cisco.iosxr.iosxr_l3_interfaces: +# running_config: "{{ lookup('file', './parsed.cfg') }}" +# state: parsed +# Task Output (redacted) +# ----------------------- +# "parsed": [ +# { +# "ipv4": [ +# { +# "address": "192.0.2.1 255.255.255.0" +# }, +# { +# "address": "192.0.2.2 255.255.255.0", +# "secondary": true +# } +# ], +# "ipv6": [ +# { +# "address": "2001:db8:0:3::/64" +# } +# ], +# "name": "GigabitEthernet0/0/0/0" +# }, +# { +# "name": "GigabitEthernet0/0/0/1" +# }, +# { +# "ipv4": [ +# { +# "address": "192.0.22.1 255.255.255.0" +# }, +# { +# "address": "192.0.23.1 255.255.255.0" +# } +# ], +# "name": "GigabitEthernet0/0/0/3" +# } +# ] + + +# Using rendered +- name: Render platform specific commands from task input using rendered state + cisco.iosxr.iosxr_l3_interfaces: + config: + + - name: GigabitEthernet0/0/0/0 + ipv4: + + - address: 198.51.100.1/24 + + - name: GigabitEthernet0/0/0/1 + ipv6: + + - address: 2001:db8:0:3::/64 + ipv4: + + - address: 192.0.2.1/24 + + - address: 192.0.2.2/24 + secondary: true + state: rendered +# Task Output (redacted) +# ----------------------- +# "rendered": [ +# "interface GigabitEthernet0/0/0/0", +# "ipv4 address 198.51.100.1 255.255.255.0", +# "interface GigabitEthernet0/0/0/1", +# "ipv4 address 192.0.2.2 255.255.255.0 secondary", +# "ipv4 address 192.0.2.1 255.255.255.0", +# "ipv6 address 2001:db8:0:3::/64" +# ] +# Using gathered +# Before state: +# ------------ +# +# RP/0/0/CPU0:an-iosxr-02#show running-config interface +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 10.8.38.70 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/0 +# description Configured and Merged by Ansible-Network +# mtu 66 +# ipv4 address 192.0.2.1 255.255.255.0 +# ipv4 address 192.0.2.2 255.255.255.0 secondary +# ipv6 address 2001:db8:0:3::/64 +# duplex half +# ! +# interface GigabitEthernet0/0/0/1 +# description Configured and Merged by Ansible-Network +# mtu 66 +# speed 100 +# duplex full +# dot1q native vlan 10 +# l2transport +# l2protocol cdp forward +# l2protocol pvst tunnel +# propagate remote-status +# ! +# ! +# interface GigabitEthernet0/0/0/3 +# shutdown +# ! +# interface GigabitEthernet0/0/0/4 +# shutdown +# dot1q native vlan 40 +# ! +- name: Gather IOSXR l3 interfaces as in given arguments + cisco.iosxr.iosxr_l3_interfaces: + config: + state: gathered +# Task Output (redacted) +# ----------------------- +# +# "gathered": [ +# { +# "name": "Loopback888" +# }, +# { +# "ipv4": [ +# { +# "address": "192.0.2.1 255.255.255.0" +# }, +# { +# "address": "192.0.2.2 255.255.255.0", +# "secondary": true +# } +# ], +# "ipv6": [ +# { +# "address": "2001:db8:0:3::/64" +# } +# ], +# "name": "GigabitEthernet0/0/0/0" +# }, +# { +# "name": "GigabitEthernet0/0/0/1" +# }, +# { +# "name": "GigabitEthernet0/0/0/3" +# }, +# { +# "name": "GigabitEthernet0/0/0/4" +# } +# ] +# After state: +# ------------ +# +# RP/0/0/CPU0:an-iosxr-02#show running-config interface +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 10.8.38.70 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/0 +# description Configured and Merged by Ansible-Network +# mtu 66 +# ipv4 address 192.0.2.1 255.255.255.0 +# ipv4 address 192.0.2.2 255.255.255.0 secondary +# ipv6 address 2001:db8:0:3::/64 +# duplex half +# ! +# interface GigabitEthernet0/0/0/1 +# description Configured and Merged by Ansible-Network +# mtu 66 +# speed 100 +# duplex full +# dot1q native vlan 10 +# l2transport +# l2protocol cdp forward +# l2protocol pvst tunnel +# propagate remote-status +# ! +# ! +# interface GigabitEthernet0/0/0/3 +# shutdown +# ! +# interface GigabitEthernet0/0/0/4 +# shutdown +# dot1q native vlan 40 +# ! + + +""" + +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/0/0/1', 'ipv4 address 192.168.0.1 255.255.255.0'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.l3_interfaces.l3_interfaces import ( + L3_InterfacesArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.config.l3_interfaces.l3_interfaces import ( + L3_Interfaces, +) + + +def main(): + """ + Main entry point for module execution + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + + mutually_exclusive = [("config", "running_config")] + module = AnsibleModule( + argument_spec=L3_InterfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive, + ) + + result = L3_Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lacp.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lacp.py new file mode 100644 index 00000000..a005f90a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lacp.py @@ -0,0 +1,392 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for iosxr_lacp +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: iosxr_lacp +short_description: LACP resource module +description: +- This module manages Global Link Aggregation Control Protocol (LACP) on IOS-XR devices. +version_added: 1.0.0 +author: +- Nilashish Chakraborty (@nilashishc) +- Rohit Thakur (@rohitthakur2590) +notes: +- Tested against IOS-XR 6.1.3. +- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html). +options: + config: + description: The provided configurations. + type: dict + suboptions: + system: + description: This option sets the default system parameters for LACP bundles. + type: dict + suboptions: + priority: + description: + - The system priority to use in LACP negotiations. + - Lower value is higher priority. + - Refer to vendor documentation for valid values. + type: int + mac: + type: dict + description: + - The system MAC related configuration for LACP. + suboptions: + address: + description: + - The system ID to use in LACP negotiations. + 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-XR device + by executing the command B(show running-config lacp). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + description: + - The state of the configuration after module completion. + type: str + choices: + - merged + - replaced + - deleted + - parsed + - rendered + - gathered + default: merged +""" +EXAMPLES = """ +# Using merged +# +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/0/CPU0:iosxr01#show running-config lacp +# Tue Jul 16 17:46:08.147 UTC +# % No such configuration item(s) +# +# + +- name: Merge provided configuration with device configuration + cisco.iosxr.iosxr_lacp: + config: + system: + priority: 10 + mac: + address: 00c1.4c00.bd15 + state: merged + +# +# +# ----------------------- +# Module Execution Result +# ----------------------- +# +# "before": {} +# +# +# "commands": [ +# "lacp system priority 10", +# "lacp system mac 00c1.4c00.bd15" +# ] +# +# +# "after": { +# "system": { +# "mac": { +# "address": "00c1.4c00.bd15" +# }, +# "priority": 10 +# } +# } +# +# ----------- +# After state +# ----------- +# +# +# RP/0/0/CPU0:iosxr01#sh run lacp +# Tue Jul 16 17:51:29.365 UTC +# lacp system mac 00c1.4c00.bd15 +# lacp system priority 10 +# +# + +# Using replaced +# +# +# ------------- +# Before state +# ------------- +# +# +# RP/0/0/CPU0:iosxr01#sh run lacp +# Tue Jul 16 17:53:59.904 UTC +# lacp system mac 00c1.4c00.bd15 +# lacp system priority 10 +# + +- name: Replace device global lacp configuration with the given configuration + cisco.iosxr.iosxr_lacp: + config: + system: + priority: 11 + state: replaced +# +# +# ----------------------- +# Module Execution Result +# ----------------------- +# "before": { +# "system": { +# "mac": { +# "address": "00c1.4c00.bd15" +# }, +# "priority": 10 +# } +# } +# +# +# "commands": [ +# "no lacp system mac", +# "lacp system priority 11" +# ] +# +# +# "after": { +# "system": { +# "priority": 11 +# } +# } +# +# ----------- +# After state +# ----------- +# +# +# RP/0/0/CPU0:iosxr01#sh run lacp +# Tue Jul 16 18:02:40.379 UTC +# lacp system priority 11 +# +# + +# Using deleted +# +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/0/CPU0:iosxr01#sh run lacp +# Tue Jul 16 18:37:09.727 UTC +# lacp system mac 00c1.4c00.bd15 +# lacp system priority 11 +# +# + +- name: Delete global LACP configurations from the device + cisco.iosxr.iosxr_lacp: + state: deleted + +# +# +# ----------------------- +# Module Execution Result +# ----------------------- +# "before": { +# "system": { +# "mac": { +# "address": "00c1.4c00.bd15" +# }, +# "priority": 11 +# } +# } +# +# +# "commands": [ +# "no lacp system mac", +# "no lacp system priority" +# ] +# +# +# "after": {} +# +# ------------ +# After state +# ------------ +# +# +# RP/0/0/CPU0:iosxr01#sh run lacp +# Tue Jul 16 18:39:44.116 UTC +# % No such configuration item(s) +# +# + + +# Using parsed +# parsed.cfg +# ------------ +# +# lacp system mac 00c1.4c00.bd15 +# lacp system priority 11 +# - name: Convert LACP config to argspec without connecting to the appliance +# cisco.iosxr.iosxr_lacp: +# running_config: "{{ lookup('file', './parsed.cfg') }}" +# state: parsed +# Task Output (redacted) +# ----------------------- +# "parsed": { +# "system": { +# "mac": { +# "address": "00c1.4c00.bd15" +# }, +# "priority": 11 +# } +# } + + +# Using rendered +- name: Render platform specific commands from task input using rendered state + cisco.iosxr.iosxr_lacp: + config: + system: + priority: 11 + mac: + address: 00c1.4c00.bd15 + state: rendered +# Task Output (redacted) +# ----------------------- +# "rendered": [ +# "lacp system priority 11", +# "lacp system mac 00c1.4c00.bd15" +# ] + + +# Using gathered +# Before state: +# ------------ +# +# RP/0/0/CPU0:an-iosxr-02#show running-config lacp +# lacp system mac 00c1.4c00.bd15 +# lacp system priority 11 +- name: Gather IOSXR LACP configuration + cisco.iosxr.iosxr_lacp: + config: + state: gathered +# Task Output (redacted) +# ----------------------- +# +# "gathered": { +# "system": { +# "mac": { +# "address": "00c1.4c00.bd15" +# }, +# "priority": 11 +# } +# } +# After state: +# ------------ +# +# RP/0/0/CPU0:an-iosxr-02#show running-config lacp +# lacp system mac 00c1.4c00.bd15 +# lacp system priority + + +""" +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: ['lacp system priority 10', 'lacp system mac 00c1.4c00.bd15'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.lacp.lacp import ( + LacpArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.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, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive, + ) + + result = Lacp(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lacp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lacp_interfaces.py new file mode 100644 index 00000000..dc156891 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lacp_interfaces.py @@ -0,0 +1,652 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for iosxr_lacp_interfaces +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: iosxr_lacp_interfaces +short_description: LACP interfaces resource module +description: +- This module manages Link Aggregation Control Protocol (LACP) attributes of interfaces + on IOS-XR devices. +version_added: 1.0.0 +notes: +- Tested against IOS-XR 6.1.3. +- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html). +author: Nilashish Chakraborty (@nilashishc) +options: + config: + description: A dictionary of LACP interfaces options. + type: list + elements: dict + suboptions: + name: + description: + - Name/Identifier of the interface or Ether-Bundle. + type: str + churn_logging: + description: + - Specifies the parameter for logging of LACP churn events. + - Valid only for ether-bundles. + - Mode 'actor' logs actor churn events only. + - Mode 'partner' logs partner churn events only. + - Mode 'both' logs actor and partner churn events only. + type: str + choices: + - actor + - partner + - both + collector_max_delay: + description: + - Specifies the collector max delay to be signaled to the LACP partner. + - Valid only for ether-bundles. + - Refer to vendor documentation for valid values. + type: int + period: + description: + - Specifies the rate at which packets are sent or received. + - For ether-bundles, this specifies the period to be used by its member links. + - Refer to vendor documentation for valid values. + type: int + switchover_suppress_flaps: + description: + - Specifies the time for which to suppress flaps during a LACP switchover. + - Valid only for ether-bundles. + - Refer to vendor documentation for valid values. + type: int + system: + description: + - This dict object contains configurable options related to LACP system parameters + for ether-bundles. + type: dict + suboptions: + priority: + description: + - Specifies the system priority to use in LACP negotiations for the bundle. + - Refer to vendor documentation for valid values. + type: int + mac: + description: + - Specifies the system ID to use in LACP negotiations for the bundle, + encoded as a MAC address. + type: str + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS-XR device + by executing the command B(show running-config int). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + description: + - The state of the configuration after module completion. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - parsed + - gathered + - rendered + default: merged + +""" +EXAMPLES = """ +# Using merged +# +# +# ------------ +# Before state +# ------------ +# +# +# +# RP/0/0/CPU0:an-iosxr#sh running-config interface +# Sun Jul 21 18:01:35.079 UTC +# interface Bundle-Ether10 +# ! +# interface Bundle-Ether11 +# ! +# interface Bundle-Ether12 +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1' +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# ! +# +# + +- name: Merge provided configuration with device configuration + cisco.iosxr.iosxr_lacp_interfaces: + config: + - name: Bundle-Ether10 + churn_logging: actor + collector_max_delay: 100 + switchover_suppress_flaps: 500 + + - name: Bundle-Ether11 + system: + mac: 00c2.4c00.bd15 + + - name: GigabitEthernet0/0/0/1 + period: 200 + state: merged + +# +# +# ----------- +# After state +# ----------- +# +# +# RP/0/0/CPU0:an-iosxr#sh run int +# Sun Jul 21 18:24:52.413 UTC +# interface Bundle-Ether10 +# lacp churn logging actor +# lacp switchover suppress-flaps 500 +# lacp collector-max-delay 100 +# ! +# interface Bundle-Ether11 +# lacp system mac 00c2.4c00.bd15 +# ! +# interface Bundle-Ether12 +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# lacp period 200 +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# ! +# + + +# Using replaced +# +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/0/CPU0:an-iosxr#sh run int +# Sun Jul 21 18:24:52.413 UTC +# interface Bundle-Ether10 +# lacp churn logging actor +# lacp switchover suppress-flaps 500 +# lacp collector-max-delay 100 +# ! +# interface Bundle-Ether11 +# lacp system mac 00c2.4c00.bd15 +# ! +# interface Bundle-Ether12 +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# lacp period 200 +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# ! +# + +- name: Replace LACP configuration of listed interfaces with provided configuration + cisco.iosxr.iosxr_lacp_interfaces: + config: + - name: Bundle-Ether10 + churn_logging: partner + + - name: GigabitEthernet0/0/0/2 + period: 300 + state: replaced + +# +# +# ----------- +# After state +# ----------- +# +# +# RP/0/0/CPU0:an-iosxr#sh run int +# Sun Jul 21 18:50:21.929 UTC +# interface Bundle-Ether10 +# lacp churn logging partner +# ! +# interface Bundle-Ether11 +# lacp system mac 00c2.4c00.bd15 +# ! +# interface Bundle-Ether12 +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# lacp period 200 +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# lacp period 300 +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# ! +# +# + + +# Using overridden +# +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/0/CPU0:an-iosxr#sh run int +# Sun Jul 21 18:24:52.413 UTC +# interface Bundle-Ether10 +# lacp churn logging actor +# lacp switchover suppress-flaps 500 +# lacp collector-max-delay 100 +# ! +# interface Bundle-Ether11 +# lacp system mac 00c2.4c00.bd15 +# ! +# interface Bundle-Ether12 +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# lacp period 200 +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# lacp period 200 +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# ! +# +# + +- name: Override all interface LACP configuration with provided configuration + cisco.iosxr.iosxr_lacp_interfaces: + config: + - name: Bundle-Ether12 + churn_logging: both + collector_max_delay: 100 + switchover_suppress_flaps: 500 + + - name: GigabitEthernet0/0/0/1 + period: 300 + state: overridden + +# +# +# ----------- +# After state +# ----------- +# +# +# RP/0/0/CPU0:an-iosxr(config-if)#do sh run int +# Sun Jul 21 19:32:36.115 UTC +# interface Bundle-Ether10 +# ! +# interface Bundle-Ether11 +# ! +# interface Bundle-Ether12 +# lacp churn logging both +# lacp switchover suppress-flaps 500 +# lacp collector-max-delay 100 +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# lacp period 300 +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# ! +# + + +# Using deleted +# +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/0/CPU0:an-iosxr#sh run int +# Sun Jul 21 18:24:52.413 UTC +# interface Bundle-Ether10 +# lacp churn logging actor +# lacp switchover suppress-flaps 500 +# lacp collector-max-delay 100 +# ! +# interface Bundle-Ether11 +# lacp non-revertive +# ! +# interface Bundle-Ether12 +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# lacp period 200 +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# lacp period 300 +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# ! +# + +- name: Deleted LACP configurations of provided interfaces (Note - This won't delete + the interface itself) + cisco.iosxr.iosxr_lacp_interfaces: + config: + - name: Bundle-Ether10 + - name: Bundle-Ether11 + - name: GigabitEthernet0/0/0/1 + - name: GigabitEthernet0/0/0/2 + state: deleted + +# +# +# ----------- +# After state +# ----------- +# +# +# Using parsed: + +# parsed.cfg +# interface Bundle-Ether10 +# lacp churn logging actor +# lacp switchover suppress-flaps 500 +# lacp collector-max-delay 100 +# ! +# interface Bundle-Ether11 +# lacp system mac 00c2.4c00.bd15 +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# lacp period 200 +# ! +# + +- name: Convert lacp interfaces config to argspec without connecting to the appliance + cisco.iosxr.iosxr_lacp_interfaces: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed + +# -------------- +# Output: +# -------------- + +# parsed: +# - name: Bundle-Ether10 +# churn_logging: actor +# collector_max_delay: 100 +# switchover_suppress_flaps: 500 +# +# - name: Bundle-Ether11 +# system: +# mac: 00c2.4c00.bd15 +# +# - name: GigabitEthernet0/0/0/1 +# period: 200 +# +# + +# Using gathered: + +# Native config: +# interface Bundle-Ether10 +# lacp churn logging actor +# lacp switchover suppress-flaps 500 +# lacp collector-max-delay 100 +# ! +# interface Bundle-Ether11 +# lacp system mac 00c2.4c00.bd15 +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# lacp period 200 +# ! +# + +- name: Gather IOSXR lacp interfaces configuration + cisco.iosxr.iosxr_lacp_interfaces: + config: + state: gathered + +# ---------- +# Output +# --------- +# gathered: +# - name: Bundle-Ether10 +# churn_logging: actor +# collector_max_delay: 100 +# switchover_suppress_flaps: 500 +# +# - name: Bundle-Ether11 +# system: +# mac: 00c2.4c00.bd15 +# +# - name: GigabitEthernet0/0/0/1 +# period: 200 + +# Using rendered: + +- name: Render platform specific commands from task input using rendered state + cisco.iosxr.iosxr_lacp_interfaces: + config: + - name: Bundle-Ether10 + churn_logging: actor + collector_max_delay: 100 + switchover_suppress_flaps: 500 + + - name: Bundle-Ether11 + system: + mac: 00c2.4c00.bd15 + + - name: GigabitEthernet0/0/0/1 + period: 200 + state: rendered + +# ------------- +# Output +# ------------- +# rendered: [ +# - "interface Bundle-Ether10" +# - " lacp churn logging actor" +# - " lacp switchover suppress-flaps 500" +# - " lacp collector-max-delay 100" +# - "interface Bundle-Ether11" +# - " lacp system mac 00c2.4c00.bd15" +# - "interface MgmtEth0/0/CPU0/0" +# - " ipv4 address 192.0.2.11 255.255.255.0" +# - "interface GigabitEthernet0/0/0/1" +# - " lacp period 200" +# + + +""" +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 Bundle-Ether10', 'lacp churn logging partner', 'lacp period 150'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.lacp_interfaces.lacp_interfaces import ( + Lacp_interfacesArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.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, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive, + ) + + result = Lacp_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lag_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lag_interfaces.py new file mode 100644 index 00000000..201365e8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lag_interfaces.py @@ -0,0 +1,853 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for iosxr_lag_interfaces +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +DOCUMENTATION = """ +module: iosxr_lag_interfaces +short_description: LAG interfaces resource module +description: +- This module manages the attributes of LAG/Ether-Bundle interfaces on IOS-XR devices. +version_added: 1.0.0 +notes: +- Tested against IOS-XR 6.1.3. +- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html). +author: Nilashish Chakraborty (@NilashishC) +options: + config: + description: A provided Link Aggregation Group (LAG) configuration. + type: list + elements: dict + suboptions: + name: + description: + - Name/Identifier of the LAG/Ether-Bundle to configure. + type: str + required: true + members: + description: + - List of member interfaces for the LAG/Ether-Bundle. + type: list + elements: dict + suboptions: + member: + description: + - Name of the member interface. + type: str + mode: + description: + - Specifies the mode of the operation for the member interface. + - Mode 'active' runs LACP in active mode. + - Mode 'on' does not run LACP over the port. + - Mode 'passive' runs LACP in passive mode over the port. + - Mode 'inherit' runs LACP as configured in the bundle. + type: str + choices: ["on", "active", "passive", "inherit"] + mode: + description: + - LAG mode. + - Mode 'active' runs LACP in active mode over the port. + - Mode 'on' does not run LACP over the port. + - Mode 'passive' runs LACP in passive mode over the port. + type: str + choices: ["on", "active", "passive"] + links: + description: + - This dict contains configurable options related to LAG/Ether-Bundle links. + type: dict + suboptions: + max_active: + description: + - Specifies the limit on the number of links that can be active in the LAG/Ether-Bundle. + - Refer to vendor documentation for valid values. + type: int + min_active: + description: + - Specifies the minimum number of active links needed to bring up the LAG/Ether-Bundle. + - Refer to vendor documentation for valid values. + type: int + load_balancing_hash: + description: + - Specifies the hash function used for traffic forwarded over the LAG/Ether-Bundle. + - Option 'dst-ip' uses the destination IP as the hash function. + - Option 'src-ip' uses the source IP as the hash function. + type: str + choices: ["dst-ip", "src-ip"] + 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-XR device + by executing the command B(show running-config int). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + description: + - The state of the configuration after module completion. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - parsed + - rendered + - gathered + default: merged + +""" +EXAMPLES = """ +# Using merged +# +# +# ------------ +# Before state +# ------------ +# +# RP/0/0/CPU0:iosxr01#show run int +# Sun Jul 7 19:42:59.416 UTC +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description "GigabitEthernet - 1" +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# ! +# +# +- name: Merge provided configuration with device configuration + cisco.iosxr.iosxr_lag_interfaces: + config: + - name: Bundle-Ether10 + members: + - member: GigabitEthernet0/0/0/1 + mode: inherit + - member: GigabitEthernet0/0/0/3 + mode: inherit + mode: active + links: + max_active: 5 + min_active: 2 + load_balancing_hash: src-ip + + - name: Bundle-Ether12 + members: + - member: GigabitEthernet0/0/0/2 + mode: passive + - member: GigabitEthernet0/0/0/4 + mode: passive + load_balancing_hash: dst-ip + state: merged +# +# +# ----------- +# After state +# ----------- +# +# RP/0/0/CPU0:iosxr01#show run int +# Sun Jul 7 20:51:17.685 UTC +# interface Bundle-Ether10 +# lacp mode active +# bundle load-balancing hash src-ip +# bundle maximum-active links 5 +# bundle minimum-active links 2 +# ! +# interface Bundle-Ether12 +# bundle load-balancing hash dst-ip +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# bundle id 12 mode passive +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# bundle id 12 mode passive +# ! +# + + +# Using replaced +# +# +# ------------- +# Before state +# ------------- +# +# +# RP/0/0/CPU0:iosxr01#sho run int +# Sun Jul 7 20:58:06.527 UTC +# interface Bundle-Ether10 +# lacp mode active +# bundle load-balancing hash src-ip +# bundle maximum-active links 5 +# bundle minimum-active links 2 +# ! +# interface Bundle-Ether12 +# bundle load-balancing hash dst-ip +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# bundle id 12 mode passive +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# bundle id 12 mode passive +# ! +# +# +- name: Replace device configuration of listed Bundles with provided configurations + cisco.iosxr.iosxr_lag_interfaces: + config: + - name: Bundle-Ether12 + members: + - name: GigabitEthernet0/0/0/2 + mode: passive + + - name: Bundle-Ether11 + members: + - name: GigabitEthernet0/0/0/4 + load_balancing_hash: src-ip + state: replaced +# +# +# ----------- +# After state +# ----------- +# +# +# RP/0/0/CPU0:iosxr01#sh run int +# Sun Jul 7 21:22:27.397 UTC +# interface Bundle-Ether10 +# lacp mode active +# bundle load-balancing hash src-ip +# bundle maximum-active links 5 +# bundle minimum-active links 2 +# ! +# interface Bundle-Ether11 +# bundle load-balancing hash src-ip +# ! +# interface Bundle-Ether12 +# lacp mode passive +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# bundle id 12 mode on +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# bundle id 11 mode on +# ! +# +# + + +# Using overridden +# +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/0/CPU0:iosxr01#sh run int +# Sun Jul 7 21:22:27.397 UTC +# interface Bundle-Ether10 +# lacp mode active +# bundle load-balancing hash src-ip +# bundle maximum-active links 5 +# bundle minimum-active links 2 +# ! +# interface Bundle-Ether11 +# bundle load-balancing hash src-ip +# ! +# interface Bundle-Ether12 +# lacp mode passive +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# bundle id 12 mode on +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# bundle id 11 mode on +# ! +# +# + +- name: Overrides all device configuration with provided configuration + cisco.iosxr.iosxr_lag_interfaces: + config: + - name: Bundle-Ether10 + members: + - member: GigabitEthernet0/0/0/1 + mode: inherit + - member: GigabitEthernet0/0/0/2 + mode: inherit + mode: active + load_balancing_hash: dst-ip + state: overridden +# +# +# ------------ +# After state +# ------------ +# +# +# RP/0/0/CPU0:iosxr01#sh run int +# Sun Jul 7 21:43:04.802 UTC +# interface Bundle-Ether10 +# lacp mode active +# bundle load-balancing hash dst-ip +# ! +# interface Bundle-Ether11 +# ! +# interface Bundle-Ether12 +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# ! +# +# + + +# Using deleted +# +# +# ------------ +# Before state +# ------------ +# +# RP/0/0/CPU0:iosxr01#sh run int +# Sun Jul 7 21:22:27.397 UTC +# interface Bundle-Ether10 +# lacp mode active +# bundle load-balancing hash src-ip +# bundle maximum-active links 5 +# bundle minimum-active links 2 +# ! +# interface Bundle-Ether11 +# bundle load-balancing hash src-ip +# ! +# interface Bundle-Ether12 +# lacp mode passive +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# bundle id 12 mode on +# !n +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# bundle id 11 mode on +# ! +# +# + +- name: Delete attributes of given bundles and removes member interfaces from them + (Note - This won't delete the bundles themselves) + cisco.iosxr.iosxr_lag_interfaces: + config: + - name: Bundle-Ether10 + - name: Bundle-Ether11 + - name: Bundle-Ether12 + state: deleted + +# +# +# ------------ +# After state +# ------------ +# +# RP/0/0/CPU0:iosxr01#sh run int +# Sun Jul 7 21:49:50.004 UTC +# interface Bundle-Ether10 +# ! +# interface Bundle-Ether11 +# ! +# interface Bundle-Ether12 +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# ! +# +# + +# Using deleted (without config) +# +# +# ------------ +# Before state +# ------------ +# +# RP/0/0/CPU0:an-iosxr#sh run int +# Sun Aug 18 19:49:51.908 UTC +# interface Bundle-Ether10 +# lacp mode active +# bundle load-balancing hash src-ip +# bundle maximum-active links 10 +# bundle minimum-active links 2 +# ! +# interface Bundle-Ether11 +# bundle load-balancing hash dst-ip +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/1 +# bundle id 10 mode inherit +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# bundle id 10 mode passive +# shutdown +# ! +# interface GigabitEthernet0/0/0/3 +# bundle id 11 mode passive +# shutdown +# ! +# interface GigabitEthernet0/0/0/4 +# bundle id 11 mode passive +# shutdown +# ! +# + +- name: Delete attributes of all bundles and removes member interfaces from them (Note + - This won't delete the bundles themselves) + cisco.iosxr.iosxr_lag_interfaces: + state: deleted + +# +# +# ------------ +# After state +# ------------ +# +# +# RP/0/0/CPU0:an-iosxr#sh run int +# Sun Aug 18 19:54:22.389 UTC +# interface Bundle-Ether10 +# ! +# interface Bundle-Ether11 +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 10.8.38.69 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/0 +# shutdown +# ! +# interface GigabitEthernet0/0/0/1 +# shutdown +# ! +# interface GigabitEthernet0/0/0/2 +# shutdown +# ! +# interface GigabitEthernet0/0/0/3 +# shutdown +# ! +# interface GigabitEthernet0/0/0/4 +# shutdown +# ! + +# Using parsed: + +# parsed.cfg + +# interface Bundle-Ether10 +# lacp mode active +# bundle load-balancing hash src-ip +# bundle maximum-active links 5 +# bundle minimum-active links 2 +# ! +# interface Bundle-Ether12 +# bundle load-balancing hash dst-ip +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# bundle id 12 mode passive +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# bundle id 12 mode passive +# ! +# +- name: Convert lag interfaces config to argspec without connecting to the appliance + cisco.iosxr.iosxr_lag_interfaces: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed + +# -------------- +# Output +# -------------- +# parsed: +# - name: Bundle-Ether10 +# members: +# - member: GigabitEthernet0/0/0/1 +# mode: inherit +# - member: GigabitEthernet0/0/0/3 +# mode: inherit +# mode: active +# links: +# max_active: 5 +# min_active: 2 +# load_balancing_hash: src-ip + +# - name: Bundle-Ether12 +# members: +# - member: GigabitEthernet0/0/0/2 +# mode: passive +# - member: GigabitEthernet0/0/0/4 +# mode: passive +# load_balancing_hash: dst-ip + +# using gathered + +# Device Config: +# ------------- + +# interface Bundle-Ether10 +# lacp mode active +# bundle load-balancing hash src-ip +# bundle maximum-active links 5 +# bundle minimum-active links 2 +# ! +# interface Bundle-Ether12 +# bundle load-balancing hash dst-ip +# ! +# interface Loopback888 +# description test for ansible +# shutdown +# ! +# interface MgmtEth0/0/CPU0/0 +# ipv4 address 192.0.2.11 255.255.255.0 +# ! +# interface GigabitEthernet0/0/0/1 +# description 'GigabitEthernet - 1" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/2 +# description "GigabitEthernet - 2" +# bundle id 12 mode passive +# ! +# interface GigabitEthernet0/0/0/3 +# description "GigabitEthernet - 3" +# bundle id 10 mode inherit +# ! +# interface GigabitEthernet0/0/0/4 +# description "GigabitEthernet - 4" +# bundle id 12 mode passive +# ! +# + +- name: Gather IOSXR lag interfaces configuration + cisco.iosxr.iosxr_lag_interfaces: + config: + state: gathered + +# -------------- +# Output +# -------------- +# gathered: +# - name: Bundle-Ether10 +# members: +# - member: GigabitEthernet0/0/0/1 +# mode: inherit +# - member: GigabitEthernet0/0/0/3 +# mode: inherit +# mode: active +# links: +# max_active: 5 +# min_active: 2 +# load_balancing_hash: src-ip + +# - name: Bundle-Ether12 +# members: +# - member: GigabitEthernet0/0/0/2 +# mode: passive +# - member: GigabitEthernet0/0/0/4 +# mode: passive +# load_balancing_hash: dst-ip + +# Using rendered: +- name: Render platform specific commands from task input using rendered state + cisco.iosxr.iosxr_lag_interfaces: + config: + - name: Bundle-Ether10 + members: + - member: GigabitEthernet0/0/0/1 + mode: inherit + - member: GigabitEthernet0/0/0/3 + mode: inherit + mode: active + links: + max_active: 5 + min_active: 2 + load_balancing_hash: src-ip + + - name: Bundle-Ether12 + members: + - member: GigabitEthernet0/0/0/2 + mode: passive + - member: GigabitEthernet0/0/0/4 + mode: passive + load_balancing_hash: dst-ip + state: rendered + +# Output: + +# rendered: +# [ +# - "interface Bundle-Ether10" +# - " lacp mode active" +# - " bundle load-balancing hash src-ip" +# - " bundle maximum-active links 5" +# - " bundle minimum-active links 2" +# - "interface Bundle-Ether12" +# - " bundle load-balancing hash dst-ip" +# - "interface Loopback888" +# - " description test for ansible" +# - " shutdown" +# - "interface MgmtEth0/0/CPU0/0" +# - " ipv4 address 192.0.2.11 255.255.255.0" +# - "interface GigabitEthernet0/0/0/1" +# - " description 'GigabitEthernet - 1"" +# - " bundle id 10 mode inherit" +# - "interface GigabitEthernet0/0/0/2" +# - " description "GigabitEthernet - 2"" +# - " bundle id 12 mode passive" +# - "interface GigabitEthernet0/0/0/3" +# - " description "GigabitEthernet - 3"" +# - " bundle id 10 mode inherit" +# - "interface GigabitEthernet0/0/0/4" +# - " description "GigabitEthernet - 4"" +# - " bundle id 12 mode passive" +# ] +# +# + + +""" +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 Bundle-Ether10', 'bundle minimum-active links 2', 'bundle load-balancing hash src-ip'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.lag_interfaces.lag_interfaces import ( + Lag_interfacesArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.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, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive, + ) + + result = Lag_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lldp_global.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lldp_global.py new file mode 100644 index 00000000..0aa7bb4b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lldp_global.py @@ -0,0 +1,489 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for iosxr_lldp_global +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: iosxr_lldp_global +short_description: LLDP resource module +description: +- This module manages Global Link Layer Discovery Protocol (LLDP) settings on IOS-XR + devices. +version_added: 1.0.0 +notes: +- Tested against IOS-XR 6.1.3. +- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html). +author: Nilashish Chakraborty (@NilashishC) +options: + config: + description: The provided global LLDP configuration. + type: dict + suboptions: + holdtime: + description: + - Specifies the holdtime (in sec) to be sent in packets. + type: int + reinit: + description: + - Specifies the delay (in sec) for LLDP initialization on any interface. + type: int + subinterfaces: + description: + - Enable or disable LLDP over sub-interfaces. + type: bool + timer: + description: + - Specifies the rate at which LLDP packets are sent (in sec). + type: int + tlv_select: + description: + - Specifies the LLDP TLVs to enable or disable. + type: dict + suboptions: + management_address: + description: + - Enable or disable management address TLV. + type: bool + port_description: + description: + - Enable or disable port description TLV. + type: bool + system_capabilities: + description: + - Enable or disable system capabilities TLV. + type: bool + system_description: + description: + - Enable or disable system description TLV. + type: bool + system_name: + description: + - Enable or disable 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-XR device + by executing the command B(show running-config lldp). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + description: + - The state of the configuration after module completion. + type: str + choices: + - merged + - replaced + - deleted + - parsed + - gathered + - rendered + default: merged + +""" +EXAMPLES = """ +# Using merged +# +# +# ------------- +# Before State +# ------------- +# +# +# RP/0/0/CPU0:an-iosxr#sh run lldp +# Tue Aug 6 19:27:54.933 UTC +# % No such configuration item(s) +# +# + +- name: Merge provided LLDP configuration with the existing configuration + cisco.iosxr.iosxr_lldp_global: + config: + holdtime: 100 + reinit: 2 + timer: 3000 + subinterfaces: true + tlv_select: + management_address: false + system_description: false + state: merged + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": {} +# +# "commands": [ +# "lldp subinterfaces enable", +# "lldp holdtime 100", +# "lldp reinit 2", +# "lldp tlv-select system-description disable", +# "lldp tlv-select management-address disable", +# "lldp timer 3000" +# ] +# +# "after": { +# "holdtime": 100, +# "reinit": 2, +# "subinterfaces": true, +# "timer": 3000, +# "tlv_select": { +# "management_address": false, +# "system_description": false +# } +# } +# +# +# ------------ +# After state +# ------------ +# +# +# RP/0/0/CPU0:an-iosxr#sh run lldp +# Tue Aug 6 21:31:10.587 UTC +# lldp +# timer 3000 +# reinit 2 +# subinterfaces enable +# holdtime 100 +# tlv-select +# management-address disable +# system-description disable +# ! +# ! +# +# + + +# Using replaced +# +# +# ------------- +# Before State +# ------------- +# +# RP/0/0/CPU0:an-iosxr#sh run lldp +# Tue Aug 6 21:31:10.587 UTC +# lldp +# timer 3000 +# reinit 2 +# subinterfaces enable +# holdtime 100 +# tlv-select +# management-address disable +# system-description disable +# ! +# ! +# +# + +- name: Replace existing LLDP device configuration with provided configuration + cisco.iosxr.iosxr_lldp_global: + config: + holdtime: 100 + tlv_select: + port_description: false + system_description: true + management_description: true + state: replaced + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": { +# "holdtime": 100, +# "reinit": 2, +# "subinterfaces": true, +# "timer": 3000, +# "tlv_select": { +# "management_address": false, +# "system_description": false +# } +# } +# +# "commands": [ +# "no lldp reinit 2", +# "no lldp subinterfaces enable", +# "no lldp timer 3000", +# "no lldp tlv-select management-address disable", +# "no lldp tlv-select system-description disable", +# "lldp tlv-select port-description disable" +# ] +# +# "after": { +# "holdtime": 100, +# "tlv_select": { +# "port_description": false +# } +# } +# +# +# ------------ +# After state +# ------------ +# +# RP/0/0/CPU0:an-iosxr#sh run lldp +# Tue Aug 6 21:53:08.407 UTC +# lldp +# holdtime 100 +# tlv-select +# port-description disable +# ! +# ! +# +# + + +# Using deleted +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/0/CPU0:an-iosxr#sh run lldp +# Tue Aug 6 21:31:10.587 UTC +# lldp +# timer 3000 +# reinit 2 +# subinterfaces enable +# holdtime 100 +# tlv-select +# management-address disable +# system-description disable +# ! +# ! +# +# + +- name: Deleted existing LLDP configurations from the device + cisco.iosxr.iosxr_lldp_global: + state: deleted + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": { +# "holdtime": 100, +# "reinit": 2, +# "subinterfaces": true, +# "timer": 3000, +# "tlv_select": { +# "management_address": false, +# "system_description": false +# } +# }, +# +# "commands": [ +# "no lldp holdtime 100", +# "no lldp reinit 2", +# "no lldp subinterfaces enable", +# "no lldp timer 3000", +# "no lldp tlv-select management-address disable", +# "no lldp tlv-select system-description disable" +# ] +# +# "after": {} +# +# +# ----------- +# After state +# ----------- +# +# RP/0/0/CPU0:an-iosxr#sh run lldp +# Tue Aug 6 21:38:31.187 UTC +# lldp +# ! +# +# Using parsed: + +# parsed.cfg +# lldp +# timer 3000 +# reinit 2 +# subinterfaces enable +# holdtime 100 +# tlv-select +# management-address disable +# system-description disable +# ! +# ! + +- name: Convert lldp global config to argspec without connecting to the appliance + cisco.iosxr.iosxr_lldp_global: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed + +# ------------------------ +# Module Execution Result +# ------------------------ +# parsed: +# holdtime: 100 +# reinit: 2 +# timer: 3000 +# subinterfaces: True +# tlv_select: +# management_address: False +# system_description: False + +# using gathered: + +# Device config: +# lldp +# timer 3000 +# reinit 2 +# subinterfaces enable +# holdtime 100 +# tlv-select +# management-address disable +# system-description disable +# ! +# ! + +- name: Gather IOSXR lldp global configuration + cisco.iosxr.iosxr_lldp_global: + config: + state: gathered + + +# ------------------------ +# Module Execution Result +# ------------------------ +# gathered: +# holdtime: 100 +# reinit: 2 +# timer: 3000 +# subinterfaces: True +# tlv_select: +# management_address: False +# system_description: False + +# using rendered: + +- name: Render platform specific commands from task input using rendered state + cisco.iosxr.iosxr_lldp_global: + config: + holdtime: 100 + reinit: 2 + timer: 3000 + subinterfaces: true + tlv_select: + management_address: false + system_description: false + state: rendered + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "rendered": [ +# "lldp subinterfaces enable", +# "lldp holdtime 100", +# "lldp reinit 2", +# "lldp tlv-select system-description disable", +# "lldp tlv-select management-address disable", +# "lldp timer 3000" +# ] + + + +""" +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 subinterfaces enable', 'lldp holdtime 100', 'no lldp tlv-select management-address disable'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.lldp_global.lldp_global import ( + Lldp_globalArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.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, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive, + ) + + result = Lldp_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lldp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lldp_interfaces.py new file mode 100644 index 00000000..f730f62a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_lldp_interfaces.py @@ -0,0 +1,724 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for iosxr_lldp_interfaces +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: iosxr_lldp_interfaces +short_description: LLDP interfaces resource module +description: +- This module manages Link Layer Discovery Protocol (LLDP) attributes of interfaces + on IOS-XR devices. +version_added: 1.0.0 +notes: +- Tested against IOS-XR 6.1.3. +- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html). +author: Nilashish Chakraborty (@nilashishc) +options: + config: + description: A dictionary of LLDP interfaces options. + type: list + elements: dict + suboptions: + name: + description: + - Name/Identifier of the interface or Ether-Bundle. + type: str + destination: + description: + - Specifies LLDP destination configuration on the interface. + suboptions: + mac_address: + description: + - Specifies the LLDP destination mac address on the interface. + type: str + choices: + - ieee-nearest-bridge + - ieee-nearest-non-tmpr-bridge + type: dict + receive: + description: + - Enable/disable LLDP RX on an interface. + type: bool + transmit: + description: + - Enable/disable LLDP TX on an interface. + 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-XR device + by executing the command B(show running-config int). + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into Ansible structured data as per the resource module's argspec + and the value is then returned in the I(parsed) key within the result. + type: str + state: + description: + - The state of the configuration after module completion. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - parsed + - rendered + - gathered + default: merged + +""" +EXAMPLES = """ +# Using merged +# +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/RP0/CPU0:ios#sh run int +# Mon Aug 12 12:40:23.104 UTC +# interface TenGigE0/0/0/0 +# ipv4 address 192.0.2.11 255.255.255.192 +# ! +# interface preconfigure GigabitEthernet0/0/0/1 +# ! +# interface preconfigure GigabitEthernet0/0/0/2 +# ! +# +# + +- name: Merge provided configuration with running configuration + cisco.iosxr.iosxr_lldp_interfaces: + config: + - name: GigabitEthernet0/0/0/1 + destination: + mac_address: ieee-nearest-non-tmpr-bridge + transmit: false + + - name: GigabitEthernet0/0/0/2 + destination: + mac_address: ieee-nearest-bridge + receive: false + state: merged + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# +# "before": [ +# { +# "name": "TenGigE0/0/0/0" +# }, +# { +# "name": "GigabitEthernet0/0/0/1" +# }, +# { +# "name": "GigabitEthernet0/0/0/2" +# } +# ] +# +# "commands": [ +# "interface GigabitEthernet0/0/0/2", +# "lldp destination mac-address ieee-nearest-non-tmpr-bridge", +# "lldp transmit disable", +# "interface GigabitEthernet0/0/0/1", +# "lldp receive disable", +# "lldp destination mac-address ieee-nearest-bridge" +# ] +# +# "after": [ +# { +# "name": "TenGigE0/0/0/0" +# }, +# { +# "destination": { +# "mac_address": "ieee-nearest-bridge" +# }, +# "name": "GigabitEthernet0/0/0/1", +# "receive": false +# }, +# { +# "destination": { +# "mac_address": "ieee-nearest-non-tmpr-bridge" +# }, +# "name": "GigabitEthernet0/0/0/2", +# "transmit": false +# } +# ] +# +# +# ------------ +# After state +# ------------ +# +# +# RP/0/RP0/CPU0:ios#sh run int +# Mon Aug 12 12:49:51.517 UTC +# interface TenGigE0/0/0/0 +# ipv4 address 192.0.2.11 255.255.255.192 +# ! +# interface preconfigure GigabitEthernet0/0/0/1 +# lldp +# receive disable +# destination mac-address +# ieee-nearest-bridge +# ! +# ! +# ! +# interface preconfigure GigabitEthernet0/0/0/2 +# lldp +# transmit disable +# destination mac-address +# ieee-nearest-non-tmpr-bridge +# ! +# ! +# ! +# +# + + +# Using replaced +# +# +# ------------- +# Before state +# ------------- +# +# +# RP/0/RP0/CPU0:ios#sh run int +# Mon Aug 12 12:49:51.517 UTC +# interface TenGigE0/0/0/0 +# ipv4 address 192.0.2.11 255.255.255.192 +# ! +# interface preconfigure GigabitEthernet0/0/0/1 +# lldp +# receive disable +# destination mac-address +# ieee-nearest-bridge +# ! +# ! +# ! +# interface preconfigure GigabitEthernet0/0/0/2 +# lldp +# transmit disable +# destination mac-address +# ieee-nearest-non-tmpr-bridge +# ! +# ! +# ! +# +# + +- name: Replace existing LLDP configurations of specified interfaces with provided + configuration + cisco.iosxr.iosxr_lldp_interfaces: + config: + - name: GigabitEthernet0/0/0/1 + destination: + mac_address: ieee-nearest-non-tmpr-bridge + state: replaced + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": [ +# { +# "name": "TenGigE0/0/0/0" +# }, +# { +# "destination": { +# "mac_address": "ieee-nearest-bridge" +# }, +# "name": "GigabitEthernet0/0/0/1", +# "receive": false +# }, +# { +# "destination": { +# "mac_address": "ieee-nearest-non-tmpr-bridge" +# }, +# "name": "GigabitEthernet0/0/0/2", +# "transmit": false +# } +# ] +# +# +# "commands": [ +# "interface GigabitEthernet0/0/0/1", +# "no lldp receive disable", +# "lldp destination mac-address ieee-nearest-non-tmpr-bridge" +# ] +# +# +# "after": [ +# { +# "name": "TenGigE0/0/0/0" +# }, +# { +# "destination": { +# "mac_address": "ieee-nearest-non-tmpr-bridge" +# }, +# "name": "GigabitEthernet0/0/0/1" +# }, +# { +# "destination": { +# "mac_address": "ieee-nearest-non-tmpr-bridge" +# }, +# "name": "GigabitEthernet0/0/0/2", +# "transmit": false +# } +# ] +# +# +# ------------ +# After state +# ------------ +# +# +# RP/0/RP0/CPU0:ios#sh run int +# Mon Aug 12 13:02:57.062 UTC +# interface TenGigE0/0/0/0 +# ipv4 address 192.0.2.11 255.255.255.192 +# ! +# interface preconfigure GigabitEthernet0/0/0/1 +# lldp +# destination mac-address +# ieee-nearest-non-tmpr-bridge +# ! +# ! +# ! +# interface preconfigure GigabitEthernet0/0/0/2 +# lldp +# transmit disable +# destination mac-address +# ieee-nearest-non-tmpr-bridge +# ! +# ! +# ! +# +# + + +# Using overridden +# +# +# ------------- +# Before state +# ------------- +# +# +# RP/0/RP0/CPU0:ios#sh run int +# Mon Aug 12 13:15:40.465 UTC +# interface TenGigE0/0/0/0 +# ipv4 address 192.0.2.11 255.255.255.192 +# ! +# interface preconfigure GigabitEthernet0/0/0/1 +# lldp +# receive disable +# destination mac-address +# ieee-nearest-bridge +# ! +# ! +# ! +# interface preconfigure GigabitEthernet0/0/0/2 +# lldp +# transmit disable +# destination mac-address +# ieee-nearest-non-tmpr-bridge +# ! +# ! +# ! +# +# + +- name: Override the LLDP configurations of all the interfaces with provided configurations + cisco.iosxr.iosxr_lldp_interfaces: + config: + - name: GigabitEthernet0/0/0/1 + transmit: false + state: overridden + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# +# "before": [ +# { +# "name": "TenGigE0/0/0/0" +# }, +# { +# "destination": { +# "mac_address": "ieee-nearest-bridge" +# }, +# "name": "GigabitEthernet0/0/0/1", +# "receive": false +# }, +# { +# "destination": { +# "mac_address": "ieee-nearest-non-tmpr-bridge" +# }, +# "name": "GigabitEthernet0/0/0/2", +# "transmit": false +# } +# ] +# +# "commands": [ +# "interface GigabitEthernet0/0/0/2", +# "no lldp destination mac-address ieee-nearest-non-tmpr-bridge", +# "no lldp transmit disable", +# "interface GigabitEthernet0/0/0/1", +# "no lldp destination mac-address ieee-nearest-bridge", +# "no lldp receive disable", +# "lldp transmit disable" +# ] +# +# +# "after": [ +# { +# "name": "TenGigE0/0/0/0" +# }, +# { +# "name": "GigabitEthernet0/0/0/1", +# "transmit": false +# }, +# { +# "name": "GigabitEthernet0/0/0/2" +# } +# ] +# +# +# ------------ +# After state +# ------------ +# +# +# RP/0/RP0/CPU0:ios#sh run int +# Mon Aug 12 13:22:25.604 UTC +# interface TenGigE0/0/0/0 +# ipv4 address 192.0.2.11 255.255.255.192 +# ! +# interface preconfigure GigabitEthernet0/0/0/1 +# lldp +# transmit disable +# ! +# ! +# interface preconfigure GigabitEthernet0/0/0/2 +# ! +# +# + + +# Using deleted +# +# +# ------------- +# Before state +# ------------- +# +# +# RP/0/RP0/CPU0:ios#sh run int +# Mon Aug 12 13:26:21.498 UTC +# interface TenGigE0/0/0/0 +# ipv4 address 192.0.2.11 255.255.255.192 +# ! +# interface preconfigure GigabitEthernet0/0/0/1 +# lldp +# receive disable +# destination mac-address +# ieee-nearest-bridge +# ! +# ! +# ! +# interface preconfigure GigabitEthernet0/0/0/2 +# lldp +# transmit disable +# destination mac-address +# ieee-nearest-non-tmpr-bridge +# ! +# ! +# ! +# +# + +- name: Delete LLDP configurations of all interfaces (Note - This won't delete the + interfaces themselves) + cisco.iosxr.iosxr_lldp_interfaces: + state: deleted + +# +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# +# "before": [ +# { +# "name": "TenGigE0/0/0/0" +# }, +# { +# "destination": { +# "mac_address": "ieee-nearest-bridge" +# }, +# "name": "GigabitEthernet0/0/0/1", +# "receive": false +# }, +# { +# "destination": { +# "mac_address": "ieee-nearest-non-tmpr-bridge" +# }, +# "name": "GigabitEthernet0/0/0/2", +# "transmit": false +# } +# ] +# +# +# "commands": [ +# "interface GigabitEthernet0/0/0/1", +# "no lldp destination mac-address ieee-nearest-bridge", +# "no lldp receive disable", +# "interface GigabitEthernet0/0/0/2", +# "no lldp destination mac-address ieee-nearest-non-tmpr-bridge", +# "no lldp transmit disable" +# ] +# +# +# "after": [ +# { +# "name": "TenGigE0/0/0/0" +# }, +# { +# "name": "GigabitEthernet0/0/0/1" +# }, +# { +# "name": "GigabitEthernet0/0/0/2" +# } +# ] +# +# +# ------------ +# After state +# ------------ +# +# +# RP/0/RP0/CPU0:ios#sh run int +# Mon Aug 12 13:30:14.618 UTC +# interface TenGigE0/0/0/0 +# ipv4 address 192.0.2.11 255.255.255.192 +# ! +# interface preconfigure GigabitEthernet0/0/0/1 +# ! +# interface preconfigure GigabitEthernet0/0/0/2 +# ! +# +# +# Using parsed: +# parsed.cfg + +# interface TenGigE0/0/0/0 +# ipv4 address 192.0.2.11 255.255.255.192 +# ! +# interface preconfigure GigabitEthernet0/0/0/1 +# lldp +# receive disable +# destination mac-address +# ieee-nearest-bridge +# ! +# ! +# ! +# interface preconfigure GigabitEthernet0/0/0/2 +# lldp +# transmit disable +# destination mac-address +# ieee-nearest-non-tmpr-bridge + +- name: Convert lacp interfaces config to argspec without connecting to the appliance + cisco.iosxr.iosxr_lldp_interfaces: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed + +# ------------------------ +# Module Execution Result +# ------------------------ + +# parsed: [ +# - name: GigabitEthernet0/0/0/1 +# destination: +# mac_address: ieee-nearest-non-tmpr-bridge +# transmit: False + +# - name: GigabitEthernet0/0/0/2 +# destination: +# mac_address: ieee-nearest-bridge +# receive: False +# ] + +# Using gathered: +# Device config: + +# RP/0/RP0/CPU0:ios#sh run int +# Mon Aug 12 12:49:51.517 UTC +# interface TenGigE0/0/0/0 +# ipv4 address 192.0.2.11 255.255.255.192 +# ! +# interface preconfigure GigabitEthernet0/0/0/1 +# lldp +# receive disable +# destination mac-address +# ieee-nearest-bridge +# ! +# ! +# ! +# interface preconfigure GigabitEthernet0/0/0/2 +# lldp +# transmit disable +# destination mac-address +# ieee-nearest-non-tmpr-bridge + +- name: Gather IOSXR lldp interfaces configuration + cisco.iosxr.iosxr_lldp_interfaces: + config: + state: gathered + +# ------------------------ +# Module Execution Result +# ------------------------ + +# gathered: +# - name: GigabitEthernet0/0/0/1 +# destination: +# mac_address: ieee-nearest-non-tmpr-bridge +# transmit: False + +# - name: GigabitEthernet0/0/0/2 +# destination: +# mac_address: ieee-nearest-bridge +# receive: False + +# Using rendred: +- name: Render platform specific commands from task input using rendered state + cisco.iosxr.iosxr_lldp_interfaces: + config: + - name: GigabitEthernet0/0/0/1 + destination: + mac_address: ieee-nearest-non-tmpr-bridge + transmit: false + + - name: GigabitEthernet0/0/0/2 + destination: + mac_address: ieee-nearest-bridge + receive: false + state: rendered + +# ------------------------ +# Module Execution Result +# ------------------------ + +# "rendered": [ +# "interface GigabitEthernet0/0/0/2", +# "lldp destination mac-address ieee-nearest-non-tmpr-bridge", +# "lldp transmit disable", +# "interface GigabitEthernet0/0/0/1", +# "lldp receive disable", +# "lldp destination mac-address ieee-nearest-bridge" +# ] + +""" +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/0/0/1', 'lldp destination mac-address ieee-nearest-non-tmpr-bridge', 'no lldp transmit disable'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.lldp_interfaces.lldp_interfaces import ( + Lldp_interfacesArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.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, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive, + ) + + result = Lldp_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_logging.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_logging.py new file mode 100644 index 00000000..bb6bf2e1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_logging.py @@ -0,0 +1,1220 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2017, 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 + + +DOCUMENTATION = """ +module: iosxr_logging +author: +- Trishna Guha (@trishnaguha) +- Kedar Kekan (@kedarX) +short_description: Configuration management of system logging services on network + devices +description: +- This module provides declarative management configuration of system logging (syslog) + on Cisco IOS XR devices. +version_added: 1.0.0 +requirements: +- ncclient >= 0.5.3 when using netconf +- lxml >= 4.1.1 when using netconf +notes: +- This module works with connection C(network_cli) and C(netconf). See L(the IOS-XR + Platform Options,../network/user_guide/platform_iosxr.html). +- Tested against IOS XRv 6.1.3 +options: + dest: + description: + - Destination for system logging (syslog) messages. + choices: + - host + - console + - monitor + - buffered + - file + type: str + name: + description: + - When C(dest) = I(file) name indicates file-name + - When C(dest) = I(host) name indicates the host-name or ip-address of syslog + server. + type: str + vrf: + description: + - vrf name when syslog server is configured, C(dest) = C(host) + type: str + default: default + size: + description: + - Size of buffer when C(dest) = C(buffered). The acceptable value is in the range + I(307200 to 125000000 bytes). Default 307200 + - Size of file when C(dest) = C(file). The acceptable value is in the range I(1 + to 2097152)KB. Default 2 GB + type: int + facility: + description: + - To configure the type of syslog facility in which system logging (syslog) messages + are sent to syslog servers Optional config for C(dest) = C(host) + default: local7 + type: str + hostnameprefix: + description: + - To append a hostname prefix to system logging (syslog) messages logged to syslog + servers. Optional config for C(dest) = C(host) + type: str + level: + description: + - Specifies the severity level for the logging. + type: str + default: debugging + aliases: + - severity + choices: ["emergencies", "alerts", "critical", "errors", "warning", "notifications", "informational", "debugging"] + aggregate: + description: List of syslog logging configuration definitions. + type: list + elements: dict + suboptions: + dest: + description: + - Destination for system logging (syslog) messages. + choices: + - host + - console + - monitor + - buffered + - file + type: str + name: + description: + - When C(dest) = I(file) name indicates file-name + - When C(dest) = I(host) name indicates the host-name or ip-address of syslog + server. + type: str + vrf: + description: + - vrf name when syslog server is configured, C(dest) = C(host) + type: str + size: + description: + - Size of buffer when C(dest) = C(buffered). The acceptable value is in the range + I(307200 to 125000000 bytes). Default 307200 + - Size of file when C(dest) = C(file). The acceptable value is in the range I(1 + to 2097152)KB. Default 2 GB + type: int + facility: + description: + - To configure the type of syslog facility in which system logging (syslog) messages + are sent to syslog servers Optional config for C(dest) = C(host) + type: str + hostnameprefix: + description: + - To append a hostname prefix to system logging (syslog) messages logged to syslog + servers. Optional config for C(dest) = C(host) + type: str + level: + description: + - Specifies the severity level for the logging. + type: str + aliases: + - severity + choices: ["emergencies", "alerts", "critical", "errors", "warning", "notifications", "informational", "debugging"] + state: + description: + - Existential state of the logging configuration on the node. + choices: + - present + - absent + type: str + state: + description: + - Existential state of the logging configuration on the node. + default: present + choices: + - present + - absent + type: str +extends_documentation_fragment: +- cisco.iosxr.iosxr +""" + +EXAMPLES = """ +- name: configure logging for syslog server host + cisco.iosxr.iosxr_logging: + dest: host + name: 10.10.10.1 + level: critical + state: present + +- name: add hostnameprefix configuration + cisco.iosxr.iosxr_logging: + hostnameprefix: host1 + state: absent + +- name: add facility configuration + cisco.iosxr.iosxr_logging: + facility: local1 + state: present + +- name: configure console logging level + cisco.iosxr.iosxr_logging: + dest: console + level: debugging + state: present + +- name: configure monitor logging level + cisco.iosxr.iosxr_logging: + dest: monitor + level: errors + state: present + +- name: configure syslog to a file + cisco.iosxr.iosxr_logging: + dest: file + name: file_name + size: 2048 + level: errors + state: present + +- name: configure buffered logging with size + cisco.iosxr.iosxr_logging: + dest: buffered + size: 5100000 + +- name: Configure logging using aggregate + cisco.iosxr.iosxr_logging: + aggregate: + - {dest: console, level: warning} + - {dest: buffered, size: 4800000} + - {dest: file, name: file3, size: 2048} + - {dest: host, name: host3, level: critical} + +- name: Delete logging using aggregate + cisco.iosxr.iosxr_logging: + aggregate: + - {dest: console, level: warning} + - {dest: buffered, size: 4800000} + - {dest: file, name: file3, size: 2048} + - {dest: host, name: host3, level: critical} + state: absent +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always (empty list when no commands to send) + type: list + sample: + - logging 10.10.10.1 vrf default severity debugging + - logging facility local7 + - logging hostnameprefix host1 + - logging console critical + - logging buffered 2097153 + - logging buffered warnings + - logging monitor errors + - logging file log_file maxfilesize 1024 severity info +xml: + description: NetConf rpc xml sent to device with transport C(netconf) + returned: always (empty list when no xml rpc to send) + type: list + sample: + - '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <syslog xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-infra-syslog-cfg"> + <files> + <file xc:operation="delete"> + <file-name>file1</file-name> + <file-log-attributes> + <max-file-size>2097152</max-file-size> + <severity>2</severity> + </file-log-attributes> + </file> + </files> + </syslog> + </config>' +""" + +import re +import collections +from copy import deepcopy + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + get_config, + load_config, + build_xml, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + iosxr_argument_spec, + etree_findall, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + is_netconf, + is_cliconf, + etree_find, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, +) + + +severity_level = { + "emergency": "0", + "alert": "1", + "critical": "2", + "error": "3", + "warning": "4", + "notice": "5", + "info": "6", + "debug": "7", + "disable": "15", +} + +severity_transpose = { + "emergencies": "emergency", + "alerts": "alert", + "critical": "critical", + "errors": "error", + "warning": "warning", + "notifications": "notice", + "informational": "info", + "debugging": "debug", +} + + +class ConfigBase(object): + def __init__(self, module): + self._flag = None + self._module = module + self._result = {"changed": False, "warnings": []} + self._want = list() + self._have = list() + + def validate_size(self, value, type=None): + if value: + if type == "buffer": + if value and not int(307200) <= value <= int(125000000): + self._module.fail_json( + msg="buffer size must be between 307200 and 125000000" + ) + elif type == "file": + if value and not int(1) <= value <= int(2097152): + self._module.fail_json( + msg="file size must be between 1 and 2097152" + ) + return value + + def map_params_to_obj(self, required_if=None): + aggregate = self._module.params.get("aggregate") + if aggregate: + for item in aggregate: + for key in item: + if item.get(key) is None: + item[key] = self._module.params[key] + + d = item.copy() + + if d["dest"] not in ("host", "file"): + d["name"] = None + + if d["dest"] == "buffered": + if d["size"] is not None: + d["size"] = str( + self.validate_size(d["size"], "buffer") + ) + else: + d["size"] = str(307200) + elif d["dest"] == "file": + if d["size"] is not None: + d["size"] = str(self.validate_size(d["size"], "file")) + else: + d["size"] = str(2097152) + else: + d["size"] = None + + if self._flag == "NC": + d["level"] = severity_transpose[d["level"]] + + self._want.append(d) + + else: + params = self._module.params + if params["dest"] not in ("host", "file"): + params["name"] = None + + if params["dest"] == "buffered": + if params["size"] is not None: + params["size"] = str( + self.validate_size(params["size"], "buffer") + ) + else: + params["size"] = str(307200) + elif params["dest"] == "file": + if params["size"] is not None: + params["size"] = str( + self.validate_size(params["size"], "file") + ) + else: + params["size"] = str(2097152) + else: + params["size"] = None + + if self._flag == "NC": + params["level"] = severity_transpose[params["level"]] + + self._want.append( + { + "dest": params["dest"], + "name": params["name"], + "vrf": params["vrf"], + "size": params["size"], + "facility": params["facility"], + "level": params["level"], + "hostnameprefix": params["hostnameprefix"], + "state": params["state"], + } + ) + + +class CliConfiguration(ConfigBase): + def __init__(self, module): + super(CliConfiguration, self).__init__(module) + self._file_list = set() + self._host_list = set() + + def map_obj_to_commands(self): + commands = list() + for want_item in self._want: + dest = want_item["dest"] + name = want_item["name"] + size = want_item["size"] + facility = want_item["facility"] + level = want_item["level"] + vrf = want_item["vrf"] + hostnameprefix = want_item["hostnameprefix"] + state = want_item["state"] + del want_item["state"] + + have_size = None + have_console_level = None + have_monitor_level = None + have_prefix = None + have_facility = None + + for item in self._have: + if item["dest"] == "buffered": + have_size = item["size"] + if item["dest"] == "console": + have_console_level = item["level"] + if item["dest"] == "monitor": + have_monitor_level = item["level"] + if item["dest"] is None and item["hostnameprefix"] is not None: + have_prefix = item["hostnameprefix"] + if ( + item["dest"] is None + and item["hostnameprefix"] is None + and item["facility"] is not None + ): + have_facility = item["facility"] + + if state == "absent": + if dest == "host" and name in self._host_list: + commands.append("no logging {0} vrf {1}".format(name, vrf)) + elif dest == "file" and name in self._file_list: + commands.append("no logging file {0}".format(name)) + elif dest == "console" and have_console_level is not None: + commands.append("no logging {0}".format(dest)) + elif dest == "monitor" and have_monitor_level: + commands.append("no logging {0}".format(dest)) + elif dest == "buffered" and have_size: + commands.append("no logging {0}".format(dest)) + + if ( + dest is None + and hostnameprefix is not None + and have_prefix == hostnameprefix + ): + commands.append( + "no logging hostnameprefix {0}".format(hostnameprefix) + ) + if ( + dest is None + and facility is not None + and have_facility == facility + ): + commands.append("no logging facility {0}".format(facility)) + + if state == "present": + if dest == "host" and name not in self._host_list: + if level == "errors" or level == "informational": + level = severity_transpose[level] + commands.append( + "logging {0} vrf {1} severity {2}".format( + name, vrf, level + ) + ) + elif dest == "file" and name not in self._file_list: + if level == "errors" or level == "informational": + level = severity_transpose[level] + commands.append( + "logging file {0} maxfilesize {1} severity {2}".format( + name, size, level + ) + ) + elif dest == "buffered" and ( + have_size is None + or (have_size is not None and size != have_size) + ): + commands.append("logging buffered {0}".format(size)) + elif dest == "console" and ( + have_console_level is None + or ( + have_console_level is not None + and have_console_level != level + ) + ): + commands.append("logging console {0}".format(level)) + elif dest == "monitor" and ( + have_monitor_level is None + or ( + have_monitor_level is not None + and have_monitor_level != level + ) + ): + commands.append("logging monitor {0}".format(level)) + + if ( + dest is None + and hostnameprefix is not None + and ( + have_prefix is None + or ( + have_prefix is not None + and hostnameprefix != have_prefix + ) + ) + ): + commands.append( + "logging hostnameprefix {0}".format(hostnameprefix) + ) + if ( + dest is None + and hostnameprefix is None + and facility != have_facility + ): + commands.append("logging facility {0}".format(facility)) + + self._result["commands"] = commands + if commands: + commit = not self._module.check_mode + diff = load_config(self._module, commands, commit=commit) + if diff: + self._result["diff"] = dict(prepared=diff) + self._result["changed"] = True + + def parse_facility(self, line): + match = re.search(r"logging facility (\S+)", line, re.M) + facility = None + if match: + facility = match.group(1) + + return facility + + def parse_size(self, line, dest): + size = None + + if dest == "buffered": + match = re.search(r"logging buffered (\S+)", line, re.M) + if match: + try: + int_size = int(match.group(1)) + except ValueError: + int_size = None + + if int_size is not None: + if isinstance(int_size, int): + size = str(match.group(1)) + return size + + def parse_hostnameprefix(self, line): + prefix = None + match = re.search(r"logging hostnameprefix (\S+)", line, re.M) + if match: + prefix = match.group(1) + return prefix + + def parse_name(self, line, dest): + name = None + if dest == "file": + match = re.search(r"logging file (\S+)", line, re.M) + if match: + name = match.group(1) + elif dest == "host": + match = re.search(r"logging (\S+)", line, re.M) + if match: + name = match.group(1) + + return name + + def parse_level(self, line, dest): + level_group = ( + "emergencies", + "alerts", + "critical", + "errors", + "warning", + "notifications", + "informational", + "debugging", + ) + + level = None + match = re.search(r"logging {0} (\S+)".format(dest), line, re.M) + if match: + if match.group(1) in level_group: + level = match.group(1) + + return level + + def parse_dest(self, line, group): + dest_group = ("console", "monitor", "buffered", "file") + dest = None + if group in dest_group: + dest = group + elif "vrf" in line: + dest = "host" + + return dest + + def parse_vrf(self, line, dest): + vrf = None + if dest == "host": + match = re.search(r"logging (\S+) vrf (\S+)", line, re.M) + if match: + vrf = match.group(2) + return vrf + + def map_config_to_obj(self): + data = get_config(self._module, config_filter="logging") + lines = data.split("\n") + + for line in lines: + match = re.search(r"logging (\S+)", line, re.M) + if match: + dest = self.parse_dest(line, match.group(1)) + name = self.parse_name(line, dest) + if dest == "host" and name is not None: + self._host_list.add(name) + if dest == "file" and name is not None: + self._file_list.add(name) + + self._have.append( + { + "dest": dest, + "name": name, + "size": self.parse_size(line, dest), + "facility": self.parse_facility(line), + "level": self.parse_level(line, dest), + "vrf": self.parse_vrf(line, dest), + "hostnameprefix": self.parse_hostnameprefix(line), + } + ) + + def run(self): + self.map_params_to_obj() + self.map_config_to_obj() + self.map_obj_to_commands() + + return self._result + + +class NCConfiguration(ConfigBase): + def __init__(self, module): + super(NCConfiguration, self).__init__(module) + self._flag = "NC" + self._log_file_meta = collections.OrderedDict() + self._log_host_meta = collections.OrderedDict() + self._log_console_meta = collections.OrderedDict() + self._log_monitor_meta = collections.OrderedDict() + self._log_buffered_size_meta = collections.OrderedDict() + self._log_buffered_level_meta = collections.OrderedDict() + self._log_facility_meta = collections.OrderedDict() + self._log_prefix_meta = collections.OrderedDict() + + def map_obj_to_xml_rpc(self): + self._log_file_meta.update( + [ + ( + "files", + { + "xpath": "syslog/files", + "tag": True, + "operation": "edit", + }, + ), + ( + "file", + { + "xpath": "syslog/files/file", + "tag": True, + "operation": "edit", + "attrib": "operation", + }, + ), + ( + "a:name", + { + "xpath": "syslog/files/file/file-name", + "operation": "edit", + }, + ), + ( + "file-attrib", + { + "xpath": "syslog/files/file/file-log-attributes", + "tag": True, + "operation": "edit", + }, + ), + ( + "a:size", + { + "xpath": "syslog/files/file/file-log-attributes/max-file-size", + "operation": "edit", + }, + ), + ( + "a:level", + { + "xpath": "syslog/files/file/file-log-attributes/severity", + "operation": "edit", + }, + ), + ] + ) + self._log_host_meta.update( + [ + ( + "host-server", + { + "xpath": "syslog/host-server", + "tag": True, + "operation": "edit", + }, + ), + ( + "vrfs", + { + "xpath": "syslog/host-server/vrfs", + "tag": True, + "operation": "edit", + }, + ), + ( + "vrf", + { + "xpath": "syslog/host-server/vrfs/vrf", + "tag": True, + "operation": "edit", + }, + ), + ( + "a:vrf", + { + "xpath": "syslog/host-server/vrfs/vrf/vrf-name", + "operation": "edit", + }, + ), + ( + "ipv4s", + { + "xpath": "syslog/host-server/vrfs/vrf/ipv4s", + "tag": True, + "operation": "edit", + }, + ), + ( + "ipv4", + { + "xpath": "syslog/host-server/vrfs/vrf/ipv4s/ipv4", + "tag": True, + "operation": "edit", + "attrib": "operation", + }, + ), + ( + "a:name", + { + "xpath": "syslog/host-server/vrfs/vrf/ipv4s/ipv4/address", + "operation": "edit", + }, + ), + ( + "ipv4-sev", + { + "xpath": "syslog/host-server/vrfs/vrf/ipv4s/ipv4/ipv4-severity-port", + "tag": True, + "operation": "edit", + }, + ), + ( + "a:level", + { + "xpath": "syslog/host-server/vrfs/vrf/ipv4s/ipv4/ipv4-severity-port/severity", + "operation": "edit", + }, + ), + ] + ) + self._log_console_meta.update( + [ + ( + "a:enable-console", + { + "xpath": "syslog/enable-console-logging", + "operation": "edit", + "attrib": "operation", + }, + ), + ( + "console", + { + "xpath": "syslog/console-logging", + "tag": True, + "operation": "edit", + "attrib": "operation", + }, + ), + ( + "a:console-level", + { + "xpath": "syslog/console-logging/logging-level", + "operation": "edit", + }, + ), + ] + ) + self._log_monitor_meta.update( + [ + ( + "monitor", + { + "xpath": "syslog/monitor-logging", + "tag": True, + "operation": "edit", + "attrib": "operation", + }, + ), + ( + "a:monitor-level", + { + "xpath": "syslog/monitor-logging/logging-level", + "operation": "edit", + }, + ), + ] + ) + self._log_buffered_size_meta.update( + [ + ( + "buffered", + { + "xpath": "syslog/buffered-logging", + "tag": True, + "operation": "edit", + "attrib": "operation", + }, + ), + ( + "a:size", + { + "xpath": "syslog/buffered-logging/buffer-size", + "operation": "edit", + }, + ), + ] + ) + self._log_buffered_level_meta.update( + [ + ( + "buffered", + { + "xpath": "syslog/buffered-logging", + "tag": True, + "operation": "edit", + "attrib": "operation", + }, + ), + ( + "a:level", + { + "xpath": "syslog/buffered-logging/logging-level", + "operation": "edit", + }, + ), + ] + ) + self._log_facility_meta.update( + [ + ( + "facility", + { + "xpath": "syslog/logging-facilities", + "tag": True, + "operation": "edit", + "attrib": "operation", + }, + ), + ( + "a:facility", + { + "xpath": "syslog/logging-facilities/facility-level", + "operation": "edit", + }, + ), + ] + ) + self._log_prefix_meta.update( + [ + ( + "a:hostnameprefix", + { + "xpath": "syslog/host-name-prefix", + "operation": "edit", + "attrib": "operation", + }, + ) + ] + ) + + state = self._module.params["state"] + + _get_filter = build_xml("syslog", opcode="filter") + running = get_config( + self._module, source="running", config_filter=_get_filter + ) + + file_ele = etree_findall(running, "file") + file_list = list() + if len(file_ele): + for file in file_ele: + file_name = etree_find(file, "file-name") + file_list.append( + file_name.text if file_name is not None else None + ) + vrf_ele = etree_findall(running, "vrf") + host_list = list() + for vrf in vrf_ele: + host_ele = etree_findall(vrf, "ipv4") + for host in host_ele: + host_name = etree_find(host, "address") + host_list.append( + host_name.text if host_name is not None else None + ) + + console_ele = etree_find(running, "console-logging") + console_level = ( + etree_find(console_ele, "logging-level") + if console_ele is not None + else None + ) + have_console = ( + console_level.text if console_level is not None else None + ) + + monitor_ele = etree_find(running, "monitor-logging") + monitor_level = ( + etree_find(monitor_ele, "logging-level") + if monitor_ele is not None + else None + ) + have_monitor = ( + monitor_level.text if monitor_level is not None else None + ) + + buffered_ele = etree_find(running, "buffered-logging") + buffered_size = ( + etree_find(buffered_ele, "buffer-size") + if buffered_ele is not None + else None + ) + have_buffered = ( + buffered_size.text if buffered_size is not None else None + ) + + facility_ele = etree_find(running, "logging-facilities") + facility_level = ( + etree_find(facility_ele, "facility-level") + if facility_ele is not None + else None + ) + have_facility = ( + facility_level.text if facility_level is not None else None + ) + + prefix_ele = etree_find(running, "host-name-prefix") + have_prefix = prefix_ele.text if prefix_ele is not None else None + + file_params = list() + host_params = list() + console_params = dict() + monitor_params = dict() + buffered_params = dict() + facility_params = dict() + prefix_params = dict() + + opcode = None + if state == "absent": + opcode = "delete" + for item in self._want: + if item["dest"] == "file" and item["name"] in file_list: + item["level"] = severity_level[item["level"]] + file_params.append(item) + elif item["dest"] == "host" and item["name"] in host_list: + item["level"] = severity_level[item["level"]] + host_params.append(item) + elif item["dest"] == "console" and have_console: + console_params.update({"console-level": item["level"]}) + elif item["dest"] == "monitor" and have_monitor: + monitor_params.update({"monitor-level": item["level"]}) + elif item["dest"] == "buffered" and have_buffered: + buffered_params["size"] = ( + str(item["size"]) if item["size"] else None + ) + buffered_params["level"] = ( + item["level"] if item["level"] else None + ) + elif ( + item["dest"] is None + and item["hostnameprefix"] is None + and item["facility"] is not None + and have_facility + ): + facility_params.update({"facility": item["facility"]}) + elif ( + item["dest"] is None + and item["hostnameprefix"] is not None + and have_prefix + ): + prefix_params.update( + {"hostnameprefix": item["hostnameprefix"]} + ) + elif state == "present": + opcode = "merge" + for item in self._want: + if item["dest"] == "file": + item["level"] = severity_level[item["level"]] + file_params.append(item) + elif item["dest"] == "host": + item["level"] = severity_level[item["level"]] + host_params.append(item) + elif item["dest"] == "console": + console_params.update({"console-level": item["level"]}) + elif item["dest"] == "monitor": + monitor_params.update({"monitor-level": item["level"]}) + elif item["dest"] == "buffered": + buffered_params["size"] = ( + str(item["size"]) if item["size"] else None + ) + buffered_params["level"] = ( + item["level"] if item["level"] else None + ) + elif ( + item["dest"] is None + and item["hostnameprefix"] is None + and item["facility"] is not None + ): + facility_params.update({"facility": item["facility"]}) + elif ( + item["dest"] is None and item["hostnameprefix"] is not None + ): + prefix_params.update( + {"hostnameprefix": item["hostnameprefix"]} + ) + + self._result["xml"] = [] + _edit_filter_list = list() + if opcode: + if len(file_params): + _edit_filter_list.append( + build_xml( + "syslog", + xmap=self._log_file_meta, + params=file_params, + opcode=opcode, + ) + ) + if len(host_params): + _edit_filter_list.append( + build_xml( + "syslog", + xmap=self._log_host_meta, + params=host_params, + opcode=opcode, + ) + ) + if len(console_params): + _edit_filter_list.append( + build_xml( + "syslog", + xmap=self._log_console_meta, + params=console_params, + opcode=opcode, + ) + ) + if len(monitor_params): + _edit_filter_list.append( + build_xml( + "syslog", + xmap=self._log_monitor_meta, + params=monitor_params, + opcode=opcode, + ) + ) + if len(buffered_params): + _edit_filter_list.append( + build_xml( + "syslog", + xmap=self._log_buffered_size_meta, + params=buffered_params, + opcode=opcode, + ) + ) + _edit_filter_list.append( + build_xml( + "syslog", + xmap=self._log_buffered_level_meta, + params=buffered_params, + opcode=opcode, + ) + ) + if len(facility_params): + _edit_filter_list.append( + build_xml( + "syslog", + xmap=self._log_facility_meta, + params=facility_params, + opcode=opcode, + ) + ) + if len(prefix_params): + _edit_filter_list.append( + build_xml( + "syslog", + xmap=self._log_prefix_meta, + params=prefix_params, + opcode=opcode, + ) + ) + + diff = None + if len(_edit_filter_list): + commit = not self._module.check_mode + diff = load_config( + self._module, + _edit_filter_list, + commit=commit, + running=running, + nc_get_filter=_get_filter, + ) + + if diff: + if self._module._diff: + self._result["diff"] = dict(prepared=diff) + + self._result["xml"] = _edit_filter_list + self._result["changed"] = True + + def run(self): + self.map_params_to_obj() + self.map_obj_to_xml_rpc() + + return self._result + + +def main(): + """ main entry point for module execution + """ + element_spec = dict( + dest=dict( + type="str", + choices=["host", "console", "monitor", "buffered", "file"], + ), + name=dict(type="str"), + size=dict(type="int"), + vrf=dict(type="str", default="default"), + facility=dict(type="str", default="local7"), + hostnameprefix=dict(type="str"), + level=dict( + type="str", + default="debugging", + aliases=["severity"], + choices=[ + "emergencies", + "alerts", + "critical", + "errors", + "warning", + "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) + + mutually_exclusive = [("dest", "facility", "hostnameprefix")] + + required_if = [ + ("dest", "host", ["name"]), + ("dest", "file", ["name"]), + ("dest", "buffered", ["size"]), + ("dest", "console", ["level"]), + ("dest", "monitor", ["level"]), + ] + + argument_spec = dict( + aggregate=dict( + type="list", + elements="dict", + options=aggregate_spec, + mutually_exclusive=mutually_exclusive, + required_if=required_if, + ) + ) + + argument_spec.update(element_spec) + argument_spec.update(iosxr_argument_spec) + + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + required_if=required_if, + supports_check_mode=True, + ) + + config_object = None + if is_cliconf(module): + # Commenting the below cliconf deprecation support call for Ansible 2.9 as it'll be continued to be supported + # module.deprecate("cli support for 'iosxr_interface' is deprecated. Use transport netconf instead", + # version='2.9') + config_object = CliConfiguration(module) + elif is_netconf(module): + config_object = NCConfiguration(module) + + if config_object: + result = config_object.run() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_netconf.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_netconf.py new file mode 100644 index 00000000..a075958f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_netconf.py @@ -0,0 +1,221 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2017, 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 + + +DOCUMENTATION = """ +module: iosxr_netconf +author: Kedar Kekan (@kedarX) +short_description: Configures NetConf sub-system service on Cisco IOS-XR devices +description: +- This module provides an abstraction that enables and configures the netconf system + service running on Cisco IOS-XR Software. This module can be used to easily enable + the Netconf API. Netconf provides a programmatic interface for working with configuration + and state resources as defined in RFC 6242. +version_added: 1.0.0 +extends_documentation_fragment: +- cisco.iosxr.iosxr +options: + netconf_port: + description: + - This argument specifies the port the netconf service should listen on for SSH + connections. The default port as defined in RFC 6242 is 830. + required: false + type: int + default: 830 + aliases: + - listens_on + netconf_vrf: + description: + - netconf vrf name + required: false + type: str + default: default + aliases: + - vrf + state: + description: + - Specifies the state of the C(iosxr_netconf) resource on the remote device. If + the I(state) argument is set to I(present) the netconf service will be configured. If + the I(state) argument is set to I(absent) the netconf service will be removed + from the configuration. + type: str + required: false + default: present + choices: + - present + - absent +notes: +- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html). +- Tested against Cisco IOS XR Software, Version 6.1.3 +""" + +EXAMPLES = """ +- name: enable netconf service on port 830 + cisco.iosxr.iosxr_netconf: + listens_on: 830 + state: present + +- name: disable netconf service + cisco.iosxr.iosxr_netconf: + state: absent +""" + +RETURN = """ +commands: + description: Returns the command sent to the remote device + returned: when changed is True + type: str + sample: 'ssh server netconf port 830' +""" +import re + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + iosxr_argument_spec, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + get_config, + load_config, +) +from ansible.module_utils.six import iteritems + +USE_PERSISTENT_CONNECTION = True + + +def map_obj_to_commands(updates): + want, have = updates + commands = list() + + if want["state"] == "absent": + if have["state"] == "present": + commands.append("no netconf-yang agent ssh") + + if "netconf_port" in have: + commands.append( + "no ssh server netconf port %s" % have["netconf_port"] + ) + + if have["netconf_vrf"]: + for vrf in have["netconf_vrf"]: + commands.append("no ssh server netconf vrf %s" % vrf) + else: + if have["state"] == "absent": + commands.append("netconf-yang agent ssh") + + if want["netconf_port"] is not None and ( + want["netconf_port"] != have.get("netconf_port") + ): + commands.append( + "ssh server netconf port %s" % want["netconf_port"] + ) + if want["netconf_vrf"] is not None and ( + want["netconf_vrf"] not in have["netconf_vrf"] + ): + commands.append("ssh server netconf vrf %s" % want["netconf_vrf"]) + + return commands + + +def parse_vrf(config): + match = re.search(r"vrf (\w+)", config) + if match: + return match.group(1) + + +def parse_port(config): + match = re.search(r"port (\d+)", config) + if match: + return int(match.group(1)) + + +def map_config_to_obj(module): + obj = {"state": "absent"} + + netconf_config = get_config(module, config_filter="netconf-yang agent") + + ssh_config = get_config(module, config_filter="ssh server") + ssh_config = [ + config_line + for config_line in (line.strip() for line in ssh_config.splitlines()) + if config_line + ] + obj["netconf_vrf"] = [] + for config in ssh_config: + if "netconf port" in config: + obj.update({"netconf_port": parse_port(config)}) + if "netconf vrf" in config: + obj["netconf_vrf"].append(parse_vrf(config)) + if "ssh" in netconf_config and ( + "netconf_port" in obj or obj["netconf_vrf"] + ): + obj.update({"state": "present"}) + + if "ssh" in netconf_config and "netconf_port" not in obj: + obj.update({"netconf_port": 830}) + + return obj + + +def validate_netconf_port(value, module): + if not 1 <= value <= 65535: + module.fail_json(msg="netconf_port must be between 1 and 65535") + + +def map_params_to_obj(module): + obj = { + "netconf_port": module.params["netconf_port"], + "netconf_vrf": module.params["netconf_vrf"], + "state": module.params["state"], + } + + for key, value in iteritems(obj): + # validate the param value (if validator func exists) + validator = globals().get("validate_%s" % key) + if callable(validator): + validator(value, module) + + return obj + + +def main(): + """main entry point for module execution + """ + argument_spec = dict( + netconf_port=dict(type="int", default=830, aliases=["listens_on"]), + netconf_vrf=dict(aliases=["vrf"], default="default"), + state=dict(default="present", choices=["present", "absent"]), + ) + argument_spec.update(iosxr_argument_spec) + + module = AnsibleModule( + argument_spec=argument_spec, 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((want, have)) + result["commands"] = commands + + if commands: + commit = not module.check_mode + diff = load_config(module, commands, commit=commit) + if diff: + result["diff"] = dict(prepared=diff) + result["changed"] = True + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_ospf_interfaces.py new file mode 100644 index 00000000..0626461c --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_ospf_interfaces.py @@ -0,0 +1,1210 @@ +#!/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) + +""" +The module file for iosxr_ospf_interfaces +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +module: iosxr_ospf_interfaces +version_added: 1.2.0 +short_description: OSPF Interfaces Resource Module. +description: + - This module manages OSPF(v2/v3) configuration of interfaces on devices running Cisco IOS-XR. +author: Rohit Thakur (@rohitthakur2590) +notes: + - Tested against IOS-XR 6.1.3 + - This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html) +options: + 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-XR device + by executing the command B(show running-config 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 + config: + description: A list of OSPF configuration for interfaces. + type: list + elements: dict + suboptions: + name: + description: + - Name/Identifier of the interface. + type: str + required: True + type: + description: + - Type of the interface. + type: str + required: True + address_family: + description: + - OSPF settings on the interfaces in address-family context. + type: list + elements: dict + suboptions: + afi: + description: + - Address Family Identifier (AFI) for OSPF settings on the interfaces. + type: str + choices: ['ipv4', 'ipv6'] + required: True + processes: + description: + - Interfaces configuration for an OSPF process. + type: list + elements: dict + suboptions: + process_id: + description: + - OSPF process tag. + type: str + required: True + area: + description: Specify the area-id + type: dict + suboptions: + 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 + apply_group_option: + description: Specify configuration from a group + type: dict + suboptions: + group_name: + description: Specify the name of the group + type: str + operation: + description: Specify the group config operation + type: str + choices: [add, remove, append] + authentication: + description: Enable authentication + type: dict + suboptions: + message_digest: + description: Use message-digest authentication + type: dict + suboptions: + keychain: + description: Specify keychain name + type: str + null_auth: + description: Use no authentication + type: bool + authentication_key: + description: Specify authentication password (key) + type: dict + suboptions: + password: + description: The OSPFv2 password (key) + type: str + clear: + description: Specifies an UNENCRYPTED password (key) will follow + type: str + encrypted: + description: Specifies an ENCRYPTED password (key) will follow + type: str + bfd: + description: Configure BFD parameters + type: dict + suboptions: + fast_detect: + description: Configure fast detection + type: dict + suboptions: + set: + description: Enable fast detection only + type: bool + strict_mode: + description: Hold down neighbor session until BFD session is up + type: bool + minimum_interval: + description: Hello interval in milli-seconds + type: int + multiplier: + description: Detect multiplier + type: int + cost: + description: Specify Interface cost + type: int + cost_fallback: + description: Specify Cost when cumulative bandwidth goes below the theshold + type: dict + suboptions: + cost: + description: Specify cost w.r.t cummulative bandwidth + type: int + threshold: + description: Specify threshold bandwidth when cost-fallback is applied + type: int + database_filter: + description: Filter OSPF LSAs during synchronization and flooding + type: dict + suboptions: + all_outgoing_lsa: + description: Filter all outgoing LSA + type: bool + dead_interval: + description: Specify interval after which a neighbor is declared dead + type: int + demand_circuit: + description: Enable/Disable demand circuits + type: bool + fast_reroute: + description: Specify IP Fast Reroute + type: dict + suboptions: + disabled: + description: Disable IP fast reroute + type: bool + per_link: + description: Specify per-prefix computation + type: dict + suboptions: + information_type: + description: Specify per-link LFA exclusion or FRR LFA candidate information + type: str + choices: ["exclude", "lfa_candidate"] + use_candidate_only: + description: Enable/Disable backup selection from candidate-list only + type: bool + interface: + description: Specify Per-link LFA exclusion information + type: dict + suboptions: + bvi: + description: Specify Bridge-Group Virtual Interface + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: int + bundle_ether: + description: Specify Aggregated Ethernet interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: int + pos_int: + description: Specify Aggregated pos interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: int + fast_ethernet: + description: Specify FastEthernet/IEEE 802.3 interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + fiftygige: + description: Specify FiftyGigE/IEEE 802.3 interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + fortygige: + description: Specify FortyGigE/IEEE 802.3 interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + fourhundredgige: + description: Specify FourHundredGigE/IEEE 802.3 interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + gigabitethernet: + description: Specify GigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + hundredgige: + description: Specify HundredGigE/IEEE 802.3 interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + mgmteth: + description: Specify MgmtEth/IEEE 802.3 interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + multilink: + description: Specify Multilink network interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + pw_ether: + description: Specify PWHE Ethernet Interface + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: int + pw_iw: + description: Specify PWHE VC11 IP Interworking Interface + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: int + srp: + description: Specify SRP interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + serial: + description: Specify Serial network interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + tengige: + description: Specify TenGigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + twentyfivegige: + description: Specify TwentyFiveGigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + twohundredgige: + description: Specify TwoHundredGigE/IEEE 802.3 interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + nve: + description: Specify Network Virtualization Endpoint Interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: int + tunnel_ip: + description: Specify GRE/IPinIP Tunnel Interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: int + tunnel_ipsec: + description: Specify IPSec Tunnel interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: int + tunnel_mte: + description: Specify MPLS Traffic Engineering P2MP Tunnel interface(s) + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: int + tunnel_mpls: + description: MPLS Transport Protocol Tunnel interface + type: list + elements: dict + suboptions: + name: + description: Specify the interface id + type: str + flood_reduction: + description: Enable/Disable flood reduction + type: bool + hello_interval: + description: Specify Time between HELLO packets + type: int + link_down_fast_detect: + description: Configure interface down parameters + type: bool + message_digest_key: + description: Message digest authentication password (key) + type: dict + suboptions: + id: + description: Key ID + type: int + required: true + md5: + description: Use MD5 Algorithm + type: dict + required: true + suboptions: + password: + description: The OSPFv2 password (key) + type: str + clear: + description: Specifies an UNENCRYPTED password (key) will follow + type: bool + encrypted: + description: Specifies an ENCRYPTED password (key) will follow + type: bool + mpls_ldp_sync: + description: Enable/Disable MPLS LDP Sync + type: bool + mtu_ignore: + description: Enable/Disable ignoring of MTU in DBD packets + type: bool + network: + description: Specify Network type + type: str + choices: ["broadcast", "non-broadcast", "point-to-multipoint", "point-to-point"] + neighbors: + description: Specify a neighbor routers + type: list + elements: dict + suboptions: + neighbor_id: + description: Specify Neighbor address (name) + type: str + cost: + description: Specify OSPF cost for point-to-multipoint neighbor + type: int + db_filter_all_out: + description: Specify Filter OSPF LSA during synchronization and flooding for point-to-multipoint neighbor + type: bool + poll_interval: + description: Specify OSPF dead-router polling interval + type: int + priority: + description: Specify OSPF priority of non-broadcast neighbor + type: int + packet_size: + description: Customize size of OSPF packets upto MTU + type: int + passive: + description: Enable/Disable passive + type: bool + prefix_suppression: + description: Suppress advertisement of the prefixes + type: bool + priority: + description: Specify Router priority + type: int + retransmit_interval: + description: Specify time between retransmitting lost link state advertisements + type: int + security_ttl: + description: Enable security + type: dict + suboptions: + set: + description: Enable ttl security + type: bool + hops: + description: Maximum number of IP hops allowed <1-254> + type: int + transmit_delay: + description: Specify estimated time needed to send link-state update packet + type: int + state: + description: + - The state the configuration should be left in. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged +""" +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:anton#show running-config router ospf +# % No such configuration item(s) +# + +- name: Merge provided OSPF interfaces configuration with the existing configuration + cisco.iosxr.iosxr_ospf_interfaces: + config: + - name: GigabitEthernet0/0/0/0 + type: gigabitethernet + address_family: + - afi: ipv4 + processes: + - process_id: "LAB3" + area: + area_id: 0.0.0.3 + cost: 20 + authentication: + message_digest: + keychain: cisco + - afi: ipv6 + processes: + - process_id: "LAB3" + area: + area_id: 0.0.0.2 + cost: 30 + state: merged + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": [] +# +# "commands": [ +# "router ospf LAB3 area 0.0.0.3 interface GigabitEthernet 0/0/0/0 cost 20", +# "router ospf LAB3 area 0.0.0.3 interface GigabitEthernet 0/0/0/0 authentication message-digest", +# "router ospf LAB3 area 0.0.0.3 interface GigabitEthernet 0/0/0/0 authentication message-digest keychain cisco", +# "router ospfv3 LAB3 area 0.0.0.2 interface GigabitEthernet 0/0/0/0 cost 30" +# ] +# +# "after": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "message_digest": { +# "keychain": "cisco" +# } +# }, +# "cost": 20, +# "processes": [ +# { +# "area": { +# "area_id": "0.0.0.3" +# }, +# "process_id": "LAB3" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "cost": 30, +# "processes": [ +# { +# "area": { +# "area_id": "0.0.0.2" +# }, +# "process_id": "LAB3" +# } +# ] +# } +# ], +# "name": "GigabitEthernet0/0/0/0", +# "type": "gigabitethernet" +# } +# ] +# +# +# ------------ +# After state +# ------------ +# +# RP/0/0/CPU0:an-iosxr-02#show running-config router ospf +# Thu Oct 23 06:00:57.217 UTC +# router ospf LAB +# area 0.0.0.0 +# ! +# area 0.0.0.9 +# ! +# ! +# router ospf LAB1 +# area 0.0.0.1 +# ! +# area 0.0.0.3 +# ! +# ! +# router ospf LAB3 +# area 0.0.0.3 +# interface GigabitEthernet0/0/0/0 +# cost 20 +# authentication message-digest keychain cisco +# ! +# ! +# ! +# router ospf ipv4 +# ! + +# Using replaced +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/0/CPU0:an-iosxr-02#show running-config router ospf +# Thu Oct 23 06:00:57.217 UTC +# router ospf LAB +# area 0.0.0.0 +# ! +# area 0.0.0.9 +# ! +# ! +# router ospf LAB1 +# area 0.0.0.1 +# ! +# area 0.0.0.3 +# ! +# ! +# router ospf LAB3 +# area 0.0.0.3 +# interface GigabitEthernet0/0/0/0 +# cost 20 +# authentication message-digest keychain cisco +# ! +# ! +# ! +# router ospf ipv4 +# ! + +- name: Replace OSPF interfaces configuration + cisco.iosxr.iosxr_ospf_interfaces: + config: + - name: GigabitEthernet0/0/0/0 + type: gigabitethernet + address_family: + - afi: ipv4 + processes: + - process_id: "LAB3" + area: + area_id: 0.0.0.3 + cost: 30 + authentication: + message_digest: + keychain: ciscoiosxr + - afi: ipv6 + processes: + - process_id: "LAB3" + area: + area_id: 0.0.0.2 + cost: 30 + state: replaced + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "message_digest": { +# "keychain": "cisco" +# } +# }, +# "cost": 20, +# "processes": [ +# { +# "area": { +# "area_id": "0.0.0.3" +# }, +# "process_id": "LAB3" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "cost": 30, +# "processes": [ +# { +# "area": { +# "area_id": "0.0.0.2" +# }, +# "process_id": "LAB3" +# } +# ] +# } +# ], +# "name": "GigabitEthernet0/0/0/0", +# "type": "gigabitethernet" +# } +# ] +# +# "commands": [ +# "router ospf LAB3 area 0.0.0.3 interface GigabitEthernet 0/0/0/0 cost 30", +# "router ospf LAB3 area 0.0.0.3 interface GigabitEthernet 0/0/0/0 authentication message-digest", +# "router ospf LAB3 area 0.0.0.3 interface GigabitEthernet 0/0/0/0 authentication message-digest keychain ciscoiosxr" +# ] +# +# "after": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "message_digest": { +# "keychain": "ciscoiosxr" +# } +# }, +# "cost": 30, +# "processes": [ +# { +# "area": { +# "area_id": "0.0.0.3" +# }, +# "process_id": "LAB3" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "cost": 30, +# "processes": [ +# { +# "area": { +# "area_id": "0.0.0.2" +# }, +# "process_id": "LAB3" +# } +# ] +# } +# ], +# "name": "GigabitEthernet0/0/0/0", +# "type": "gigabitethernet" +# } +# ] +# +# +# ----------- +# After state +# ----------- +# +# RP/0/0/CPU0:an-iosxr-02#show running-config router ospf +# Thu Oct 23 06:10:39.827 UTC +# router ospf LAB +# area 0.0.0.0 +# ! +# area 0.0.0.9 +# ! +# ! +# router ospf LAB1 +# area 0.0.0.1 +# ! +# area 0.0.0.3 +# ! +# ! +# router ospf LAB3 +# area 0.0.0.3 +# interface GigabitEthernet0/0/0/0 +# cost 30 +# authentication message-digest keychain ciscoiosxr +# ! +# ! +# ! +# router ospf ipv4 +# ! + +- name: Override existing OSPF interfaces configuration + cisco.iosxr.iosxr_ospf_interfaces: + config: + - name: GigabitEthernet0/0/0/1 + type: gigabitethernet + address_family: + - afi: ipv4 + processes: + - process_id: "LAB1" + area: + area_id: 0.0.0.3 + cost: 10 + authentication: + message_digest: + keychain: iosxr + state: overridden + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "message_digest": { +# "keychain": "ciscoiosxr" +# } +# }, +# "cost": 30, +# "processes": [ +# { +# "area": { +# "area_id": "0.0.0.3" +# }, +# "process_id": "LAB3" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "cost": 30, +# "processes": [ +# { +# "area": { +# "area_id": "0.0.0.2" +# }, +# "process_id": "LAB3" +# } +# ] +# } +# ], +# "name": "GigabitEthernet0/0/0/0", +# "type": "gigabitethernet" +# } +# ] +# +# "commands": [ +# "no router ospf LAB3 area 0.0.0.3 interface GigabitEthernet 0/0/0/0", +# "no router ospfv3 LAB3 area 0.0.0.2 interface GigabitEthernet 0/0/0/0", +# "router ospf LAB1 area 0.0.0.3 interface GigabitEthernet 0/0/0/1 cost 10", +# "router ospf LAB1 area 0.0.0.3 interface GigabitEthernet 0/0/0/1 authentication message-digest", +# "router ospf LAB1 area 0.0.0.3 interface GigabitEthernet 0/0/0/1 authentication message-digest keychain iosxr" +# ] +# +# "after": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "message_digest": { +# "keychain": "iosxr" +# } +# }, +# "cost": 10, +# "processes": [ +# { +# "area": { +# "area_id": "0.0.0.3" +# }, +# "process_id": "LAB1" +# } +# ] +# } +# ], +# "name": "GigabitEthernet0/0/0/1", +# "type": "gigabitethernet" +# } +# ] +# +# +# ----------- +# After state +# ----------- +# +# RP/0/0/CPU0:an-iosxr-02#show running-config router ospf +# Thu Oct 23 06:28:15.025 UTC +# router ospf LAB +# area 0.0.0.0 +# ! +# area 0.0.0.9 +# ! +# ! +# router ospf LAB1 +# area 0.0.0.1 +# ! +# area 0.0.0.3 +# interface GigabitEthernet0/0/0/1 +# cost 10 +# authentication message-digest keychain iosxr +# ! +# ! +# ! +# router ospf LAB3 +# area 0.0.0.3 +# ! +# ! +# router ospf ipv4 +# ! + +# Using deleted +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/0/CPU0:an-iosxr-02#show running-config router ospf +# Thu Oct 23 06:28:15.025 UTC +# router ospf LAB +# area 0.0.0.0 +# ! +# area 0.0.0.9 +# ! +# ! +# router ospf LAB1 +# area 0.0.0.1 +# ! +# area 0.0.0.3 +# interface GigabitEthernet0/0/0/1 +# cost 10 +# authentication message-digest keychain iosxr +# ! +# ! +# ! +# router ospf LAB3 +# area 0.0.0.3 +# ! +# ! +# router ospf ipv4 +# ! + +- name: Deleted existing OSPF interfaces from the device + cisco.iosxr.iosxr_ospf_interfaces: + config: + - name: GigabitEthernet0/0/0/1 + type: gigabitethernet + state: deleted + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "message_digest": { +# "keychain": "iosxr" +# } +# }, +# "cost": 10, +# "processes": [ +# { +# "area": { +# "area_id": "0.0.0.3" +# }, +# "process_id": "LAB1" +# } +# ] +# } +# ], +# "name": "GigabitEthernet0/0/0/1", +# "type": "gigabitethernet" +# } +# ], +# +# "commands": [ +# "no router ospf LAB1 area 0.0.0.3 interface GigabitEthernet 0/0/0/1" +# ] +# +# "after": [] +# +# +# ----------- +# After state +# ----------- +# +# RP/0/0/CPU0:an-iosxr-02#show running-config router ospf +# Thu Oct 23 06:34:38.319 UTC +# router ospf LAB +# area 0.0.0.0 +# ! +# area 0.0.0.9 +# ! +# ! +# router ospf LAB1 +# area 0.0.0.1 +# ! +# area 0.0.0.3 +# ! +# ! +# router ospf LAB3 +# area 0.0.0.3 +# ! +# ! +# router ospf ipv4 +# ! + +# Using parsed +# parsed.cfg +# ------------ +# router ospf LAB +# area 0.0.0.0 +# ! +# area 0.0.0.9 +# ! +# ! +# router ospf LAB1 +# area 0.0.0.1 +# ! +# area 0.0.0.3 +# ! +# ! +# router ospf LAB3 +# area 0.0.0.3 +# interface GigabitEthernet0/0/0/0 +# cost 20 +# authentication message-digest keychain cisco +# ! +# ! +# ! +# router ospf ipv4 +# ! +- name: Parsed the device configuration to get output commands + cisco.iosxr.iosxr_ospf_interfaces: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "message_digest": { +# "keychain": "cisco" +# } +# }, +# "cost": 20, +# "processes": [ +# { +# "area": { +# "area_id": "0.0.0.3" +# }, +# "process_id": "LAB3" +# } +# ] +# } +# ], +# "name": "GigabitEthernet0/0/0/0", +# "type": "gigabitethernet" +# } +# ] +# +# Using rendered +# +# +- name: Render the commands for provided configuration + cisco.iosxr.iosxr_ospf_interfaces: + config: + - name: GigabitEthernet0/0/0/0 + type: gigabitethernet + address_family: + - afi: ipv4 + processes: + - process_id: "LAB3" + area: + area_id: 0.0.0.3 + cost: 20 + authentication: + message_digest: + keychain: cisco + - afi: ipv6 + processes: + - process_id: "LAB3" + area: + area_id: 0.0.0.2 + cost: 30 + state: rendered + +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "rendered": [ +# "router ospf LAB3 area 0.0.0.3 interface GigabitEthernet 0/0/0/0 cost 20", +# "router ospf LAB3 area 0.0.0.3 interface GigabitEthernet 0/0/0/0 authentication message-digest", +# "router ospf LAB3 area 0.0.0.3 interface GigabitEthernet 0/0/0/0 authentication message-digest keychain cisco", +# "router ospfv3 LAB3 area 0.0.0.2 interface GigabitEthernet 0/0/0/0 cost 30" +# ] + + +# Using gathered +# +# Before state: +# ------------- +# +# RP/0/0/CPU0:an-iosxr-02#show running-config router ospf +# Thu Oct 23 06:50:38.743 UTC +# router ospf LAB +# area 0.0.0.0 +# ! +# area 0.0.0.9 +# ! +# ! +# router ospf LAB1 +# area 0.0.0.1 +# ! +# area 0.0.0.3 +# ! +# ! +# router ospf LAB3 +# area 0.0.0.3 +# interface GigabitEthernet0/0/0/0 +# cost 20 +# authentication message-digest keychain cisco +# ! +# ! +# ! +# router ospf ipv4 +# ! + + +- name: Gather ospf_interfaces routes configuration + cisco.iosxr.iosxr_ospf_interfaces: + state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "gathered": [ +# { +# "address_family": [ +# { +# "afi": "ipv4", +# "authentication": { +# "message_digest": { +# "keychain": "cisco" +# } +# }, +# "cost": 20, +# "processes": [ +# { +# "area": { +# "area_id": "0.0.0.3" +# }, +# "process_id": "LAB3" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "cost": 30, +# "processes": [ +# { +# "area": { +# "area_id": "0.0.0.2" +# }, +# "process_id": "LAB3" +# } +# ] +# } +# ], +# "name": "GigabitEthernet0/0/0/0", +# "type": "gigabitethernet" +# } +# ] +# +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.ospf_interfaces.ospf_interfaces import ( + Ospf_interfacesArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.config.ospf_interfaces.ospf_interfaces import ( + Ospf_interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Ospf_interfacesArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Ospf_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_ospfv2.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_ospfv2.py new file mode 100644 index 00000000..660387bb --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_ospfv2.py @@ -0,0 +1,2544 @@ +#!/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 iosxr_ospfv2 +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +module: iosxr_ospfv2 +short_description: OSPFv2 resource module +description: This module manages global OSPFv2 configuration on devices running Cisco + IOS-XR +version_added: 1.0.0 +author: +- Rohit Thakur (@rohitthakur2590) +notes: +- Tested against IOS-XR 6.1.3 +- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html) +options: + config: + description: A list of OSPFv2 process configuration + type: dict + suboptions: + processes: + description: A list of OSPFv2 instances configuration + type: list + elements: dict + suboptions: + address_family_unicast: + description: Enable unicast topology for ipv4 address family + type: bool + adjacency_stagger: + description: Stagger OSPFv2 adjacency bring up + type: dict + suboptions: + min_adjacency: + description: Initial number of neighbors to bring up per area (default + 2) + type: int + max_adjacency: + description: Maximum simultaneous neighbors to bring up + type: int + disable: + description: Disable stagger OSPFv2 adjacency + type: bool + authentication: + description: Enable authentication + type: dict + suboptions: + keychain: + description: Specify keychain name + type: str + message_digest: + description: Use message-digest authentication + type: dict + suboptions: + set: + description: Specify message-digest selection + type: bool + keychain: + description: Specify keychain name + type: str + no_auth: + description: Use no authentication + type: bool + apply_weight: + description: Enable weights configured under interfaces for load sharing + type: dict + suboptions: + bandwidth: + description: Reference bandwidth to use for calculation (Mbits/sec) + type: int + default_weight: + description: Specify default weight value to use when it is not configured + under interface + type: int + areas: + description: Configure OSPFv2 areas' properties + type: list + elements: dict + suboptions: + area_id: + description: Area ID as IP address or integer + type: str + required: true + authentication: + description: Enable authentication + type: dict + suboptions: + keychain: + description: Specify keychain name + type: str + message_digest: + description: Use message-digest authentication + type: dict + suboptions: + keychain: + description: Specify keychain name + type: str + no_auth: + description: Use no authentication + type: bool + authentication_key: + description: Used to mention authentication password (key) + type: dict + suboptions: + password: + description: The OSPFv2 password (key) + type: str + clear: + description: Specifies an UNENCRYPTED password (key) will follow + type: str + encrypted: + description: Specifies an ENCRYPTED password (key) will follow + type: str + default_cost: + description: Set the summary default-cost of a NSSA/stub area. Stub's + advertised external route metric + type: int + cost: + description: Interface cost + type: int + dead_interval: + description: Interval after which a neighbor is declared dead + type: int + hello_interval: + description: Time between HELLO packets + type: int + transmit_delay: + description: Estimated time needed to send link-state update packet + type: int + mpls: + description: Configure MPLS routing protocol parameters + type: dict + suboptions: + traffic_eng: + description: Configure an ospf area to run MPLS Traffic Engineering + type: bool + ldp: + description: Configure LDP parameters + type: dict + suboptions: + auto_config: + description: Enable LDP IGP interface auto-configuration + type: bool + sync: + description: Enable LDP IGP synchronization + type: bool + sync_igp_shortcuts: + description: LDP sync for igp-shortcut tunnels + type: bool + mtu_ignore: + description: Enable/Disable ignoring of MTU in DBD packets + type: str + choices: + - enable + - disable + bfd: + description: Configure BFD parameters + type: dict + suboptions: + fast_detect: + description: Configure fast detection + type: dict + suboptions: + set: + description: Enable fast detection only + type: bool + strict_mode: + description: Hold down neighbor session until BFD session is up + type: bool + minimum_interval: + description: Hello interval in milli-seconds + type: int + multiplier: + description: Detect multiplier + type: int + nssa: + description: + - NSSA settings for the area + type: dict + suboptions: + set: + description: Configure area as NSSA + type: bool + default_information_originate: + description: Originate default Type 7 LSA + type: dict + suboptions: + metric: + description: OSPFv2 default metric + type: int + metric_type: + description: Metric type for default routes + type: int + no_redistribution: + description: Do not send redistributed LSAs into NSSA area + type: bool + no_summary: + description: Do not send summary LSAs into NSSA area + type: bool + translate: + description: Translate LSA + type: dict + suboptions: + type7: + description: + - Translate from Type 7 to Type 5 + type: dict + suboptions: + always: + description: + - Always translate LSAs + type: bool + ranges: + description: Summarize routes matching address/mask (border routers + only) + type: list + elements: dict + suboptions: + address: + description: IP in Prefix format (x.x.x.x/len) + type: str + required: true + advertise: + description: Advertise this range (default) + type: bool + not_advertise: + description: DoNotAdvertise this range + type: bool + route_policy: + description: Specify the route-policy to filter type 3 LSAs (list + can have one inbound and/or one outbound policy only) + type: list + elements: dict + suboptions: + parameters: + description: Specify parameter values for the policy + type: list + elements: str + direction: + description: Specify inbound or outbound + type: str + choices: + - in + - out + stub: + description: + - Settings for configuring the area as a stub + type: dict + suboptions: + set: + description: + - Configure the area as a stub + type: bool + no_summary: + description: + - Do not send summary LSA into stub area + type: bool + virtual_link: + description: Define a virtual link + type: list + elements: dict + suboptions: + id: + description: Router-ID of virtual link neighbor (A.B.C.D) + type: str + required: true + authentication: + description: Enable authentication + type: dict + suboptions: + keychain: + description: Specify keychain name + type: str + message_digest: + description: Use message-digest authentication + type: dict + suboptions: + keychain: + description: Specify keychain name + type: str + no_auth: + description: Use no authentication + type: bool + authentication_key: + description: Used to mention authentication password (key) + type: dict + suboptions: + password: + description: The OSPFv2 password (key) + type: str + clear: + description: Specifies an UNENCRYPTED password (key) will + follow + type: str + encrypted: + description: Specifies an ENCRYPTED password (key) will follow + type: str + dead_interval: + description: Interval after which a neighbor is declared dead + type: int + hello_interval: + description: Time between HELLO packets + type: int + retransmit_interval: + description: Delay between LSA retransmissions + type: int + transmit_delay: + description: Link state transmit delay + type: int + message_digest_key: + description: Message digest authentication password (key) + type: dict + suboptions: + id: + description: Key ID (1-255) + type: int + required: true + md5: + description: Use MD5 Algorithm + type: dict + suboptions: + password: + description: The OSPFv2 password (key) + type: str + clear: + description: Specifies an UNENCRYPTED password (key) will + follow + type: bool + encrypted: + description: Specifies an ENCRYPTED password (key) will + follow + type: bool + + authentication_key: + description: Used to mention authentication password (key) + type: dict + suboptions: + password: + description: The OSPFv2 password (key) + type: str + clear: + description: Specifies an UNENCRYPTED password (key) will follow + type: bool + encrypted: + description: Specifies an ENCRYPTED password (key) will follow + type: bool + auto_cost: + description: Calculate OSPFv2 interface cost according to bandwidth + type: dict + suboptions: + reference_bandwidth: + description: Specify reference bandwidth in megabits per sec + type: int + disable: + description: Assign OSPFv2 cost based on interface type + type: bool + bfd: + description: Configure BFD parameters + type: dict + suboptions: + fast_detect: + description: Configure fast detection + type: dict + suboptions: + set: + description: Enable fast detection only + type: bool + strict_mode: + description: Hold down neighbor session until BFD session is up + type: bool + minimum_interval: + description: Hello interval in milli-seconds + type: int + multiplier: + description: Detect multiplier + type: int + capability: + description: Enable specific OSPFv2 feature + type: dict + suboptions: + type7: + description: NSSA capability + type: str + opaque: + description: Configure opaque LSA + type: dict + suboptions: + disable: + description: Disable Opaque LSA capability + type: bool + set: + description: Enable opaque LSA + type: bool + cost: + description: Interface cost (1-65535) + type: int + database_filter: + description: Filter OSPFv2 LSA during synchronization and flooding (all + outgoing LSA). Enable/Disable filtering + type: str + choices: [enable, disable] + dead_interval: + description: Interval after which a neighbor is declared dead + type: int + default_information_originate: + description: Distribute default route + type: dict + suboptions: + always: + description: Always advertise default route + type: bool + metric: + description: OSPFv2 default metric + type: int + metric_type: + description: OSPFv2 metric type for default routes + type: int + route_policy: + description: Apply route-policy to default-information origination + type: str + set: + description: Enable distribution of default route + type: bool + default_metric: + description: Set metric of redistributed routes + type: int + demand_circuit: + description: Enable/Disable OSPFv2 demand circuit + type: str + choices: [enable, disable] + distance: + description: Define an administrative distance + type: dict + suboptions: + admin_distance: + description: Administrative distance + type: list + elements: dict + suboptions: + value: + description: Distance value + type: int + source: + description: Source IP address + type: str + wildcard: + description: IP wild card bits (A.B.C.D) + type: str + access_list: + description: Access list name + type: str + ospf_distance: + description: OSPFv2 administrative distance + type: dict + suboptions: + external: + description: Distance for external routes + type: int + inter_area: + description: Distance for inter-area routes + type: int + intra_area: + description: Distance for intra-area routes + type: int + distribute_link_state: + description: Enable Distribution of LSAs to external services + type: dict + suboptions: + instance_id: + description: Set distribution process instance identifier + type: int + throttle: + description: Throttle time between successive LSA updates + type: int + distribute_bgp_ls: + description: Enable Distribution of LSAs to external services + type: dict + suboptions: + instance_id: + description: Set distribution process instance identifier + type: int + throttle: + description: Throttle time between successive LSA updates + type: int + distribute_list: + description: Filter networks in routing updates (list can have one inbound + and/or one outbound policy only) + type: list + elements: dict + suboptions: + access_list: + description: Inbound/outbound access-list + type: str + direction: + description: Filter incoming/outgoing routing updates + type: str + choices: + - in + - out + outgoing_params: + description: Specify additional parameters for outgoing updates only + type: dict + suboptions: + route_type: + description: Type of routes + type: str + choices: + - bgp + - connected + - dagr + - ospf + - static + id: + description: + - For BGP, specify AS number. 2-byte AS number (or) 4-byte AS + number in asdot (X.Y) format (or) 4-byte AS number in asplain + format + - For OSPF, specify OSPFv2 instance name + type: str + route_policy: + description: Route Policy to filter OSPFv2 prefixes (for incoming + updates only) + type: str + external_out: + description: Enable/Disable advertisement of intra-area prefixes as external + type: str + choices: + - enable + - disable + flood_reduction: + description: Enable/Disable OSPFv2 Flood Reduction + type: str + choices: + - enable + - disable + hello_interval: + description: Time between HELLO packets (<1-65535> seconds) + type: int + ignore_lsa_mospf: + description: Do not complain upon receiving MOSPFv2 Type 6 LSA + type: bool + link_down_fast_detect: + description: Enable fast or early detection of link-down events + type: bool + log_adjacency_changes: + description: Log adjacency state changes + type: dict + suboptions: + set: + description: Set log adjacency + type: bool + disable: + description: Disable log adjacency changes + type: bool + detail: + description: Log all state changes + type: bool + loopback_stub_network: + description: Advertise loopback as a stub network + type: str + choices: + - enable + - disable + max_lsa: + description: + - Feature to limit the number of non-self-originated LSAs + type: dict + suboptions: + threshold: + description: + - Threshold value (%) at which to generate a warning message + type: int + ignore_count: + description: + - Set count on how many times adjacencies can be suppressed + type: int + ignore_time: + description: + - Set number of minutes during which all adjacencies are suppressed + type: int + reset_time: + description: + - Set number of minutes after which ignore-count is reset to zero + type: int + warning_only: + description: + - Log 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: dict + suboptions: + set: + description: Set router-lsa attribute + type: bool + external_lsa: + description: External LSA configuration + type: dict + suboptions: + set: + description: Set external-lsa attribute + type: bool + max_metric_value: + description: Set max metric value for external LSAs + type: int + include_stub: + description: + - Advertise Max metric for Stub links as well + type: bool + on_startup: + description: + - Effective only at startup + type: dict + suboptions: + set: + description: + - Set on-startup attribute + type: bool + wait_period: + description: + - Wait period in seconds after startup + type: int + wait_for_bgp_asn: + description: + - ASN of BGP to wait for + type: int + summary_lsa: + description: + - Summary LSAs configuration + type: dict + suboptions: + set: + description: + - Set summary-lsa attribute + type: bool + max_metric_value: + description: + - Max metric value for summary LSAs + type: int + message_digest_key: + description: Message digest authentication password (key) + type: dict + suboptions: + id: + description: Key ID + type: int + required: true + md5: + description: Use MD5 Algorithm + type: dict + required: true + suboptions: + password: + description: The OSPFv2 password (key) + type: str + clear: + description: Specifies an UNENCRYPTED password (key) will follow + type: bool + encrypted: + description: Specifies an ENCRYPTED password (key) will follow + type: bool + microloop_avoidance: + description: Avoid microloops + type: dict + suboptions: + protected: + description: Avoid microloops for protected prefixes only) + type: bool + rib_update_delay: + description: Delay to introduce between SPF and RIB updates + type: int + segment_routing: + description: Enable segment routing microloop avoidance + type: bool + monitor_convergence: + description: Enables OSPFv2 route convergence monitoring + type: dict + suboptions: + prefix_list: + description: Enables Individual Prefix Monitoring + type: str + track_external_routes: + description: Enables Tracking External(Type-5/7) Prefix monitoring + type: bool + track_ip_frr: + description: Enables Tracking IP-Frr Convergence + type: bool + track_summary_routes: + description: Enables Tracking Summary(Inter-Area) Prefix monitoring + type: bool + mpls: + description: Configure MPLS routing protocol parameters + type: dict + suboptions: + traffic_eng: + description: Routing protocol commands for MPLS Traffic Engineering + type: dict + suboptions: + autoroute_exclude: + description: Exclude IP address destinations from using TE tunnels + type: dict + suboptions: + route_policy: + description: Policy name + type: str + parameters: + description: Specify parameter values for the policy + type: list + elements: str + igp_intact: + description: Retain one or more IPv4 nexthops with tunnel nexthops + type: bool + ldp_sync_update: + description: Enable LDP sync induced metric propagation + type: bool + multicast_intact: + description: Publish multicast-intact paths to RIB + type: bool + router_id: + description: Traffic Engineering stable IP address for system + type: str + ldp: + description: Configure LDP parameters + type: dict + suboptions: + auto_config: + description: Enable LDP IGP interface auto-configuration + type: bool + sync: + description: Enable LDP IGP synchronization + type: bool + sync_igp_shortcuts: + description: LDP sync for igp-shortcut tunnels + type: bool + mtu_ignore: + description: Enable/Disable ignoring of MTU in DBD packets + type: str + choices: + - enable + - disable + network: + description: Network type + type: dict + suboptions: + broadcast: + description: Specify OSPFv2 broadcast multi-access network + type: bool + non_broadcast: + description: Specify OSPFv2 NBMA network + type: bool + point_to_multipoint: + description: Specify OSPFv2 point-to-multipoint network + type: bool + point_to_point: + description: Specify OSPFv2 point-to-point network + type: bool + nsf: + description: Non-stop forwarding + type: dict + suboptions: + cisco: + description: Cisco Non-stop forwarding + type: dict + suboptions: + enforce_global: + description: Cancel NSF restart when non-NSF-aware neighbors detected + for the whole OSPFv2 process + type: bool + set: + description: Enable Cisco NSF + type: bool + flush_delay_time: + description: Maximum time allowed for external route learning + type: int + ietf: + description: IETF graceful restart + type: dict + suboptions: + helper_disable: + description: Disable router's helper support level + type: bool + set: + description: Only enable ietf option + type: bool + interval: + description: Minimum interval between NSF restarts (<90-3600> seconds) + type: int + lifetime: + description: Maximum route lifetime following restart (<90-1800> seconds) + type: int + nsr: + description: Enable NSR for all VRFs in this process. 'False' option to + disable NSR for all VRFs in this process + type: bool + packet_size: + description: Size of OSPFv2 packets to use. min=576 max=MTU bytes + type: int + passive: + description: Enable/Disable passive + type: str + choices: + - enable + - disable + prefix_suppression: + description: Suppress advertisement of the prefixes + type: dict + suboptions: + set: + description: Set the suppression option + type: bool + secondary_address: + description: Enable/Disable secondary address suppression + type: bool + priority: + description: Router priority + type: int + process_id: + description: The OSPFv2 Process ID + type: str + required: true + protocol_shutdown: + description: Protocol specific configuration + type: dict + suboptions: + host_mode: + description: Only traffic destined for this box allowed(cisco-support) + type: bool + on_reload: + description: Shutdown post reload only + type: bool + set: + description: Shutdown the OSPFv2 Protocol + type: bool + limit: + description: High watermark for incoming priority events + type: dict + suboptions: + high: + description: Hello events are dropped when incoming event queue + exceeds this value + type: int + low: + description: DBD/LS Update/Req packets are dropped when incoming + event queue exceeds this value + type: int + medium: + description: LSA ACKs are dropped when incoming event queue exceeds + this value + type: int + redistribute: + description: Redistribute information from another routing Protocol + type: dict + suboptions: + route_type: + description: Route type to redistribute + type: str + choices: [application, bgp, connected, dagr, eigrp, isis, mobile, + ospf, rip, static, subscriber] + id: + description: OnePK application name for application routes (or) AS + number for bgp and eigrp (or) instance name for isis and ospf + type: str + level: + description: ISIS levels + choices: [1, 2, 12] + type: int + lsa_type_summary: + description: LSA type 3 for redistributed routes + type: bool + match: + description: Redistribution of routes. For OSPFv2 - external/internal/nssa-external + 1/2. For EIGRP - external/internal + type: str + metric: + description: Metric for redistributed routes + type: int + metric_type: + description: OSPFv2 exterior metric type for redistributed routes + type: int + choices: [1, 2] + route_policy: + description: Apply route-policy to redistribution + type: dict + suboptions: + name: + description: Name of the policy + type: str + parameters: + description: Specify parameter values for the policy + type: list + elements: str + nssa_only: + description: Redistribute to NSSA areas only + type: bool + preserve_med: + description: Preserve med of BGP routes + type: bool + tag: + description: Set tag for routes redistributed into OSPFv2 + type: int + retransmit_interval: + description: Delay between LSA retransmissions + type: int + router_id: + description: OSPFv2 router-id in IPv4 address format (A.B.C.D) + type: str + security_ttl: + description: Enable security + type: dict + suboptions: + set: + description: Enable ttl security + type: bool + hops: + description: Maximum number of IP hops allowed <1-254> + type: int + summary_in: + description: Enable/Disable advertisement of external prefixes as inter-area + type: str + choices: [enable, disable] + summary_prefix: + description: Configure IP address summaries + type: list + elements: dict + suboptions: + prefix: + description: IP summary address/mask (A.B.C.D/prefix) + type: str + required: true + not_advertise: + description: Suppress routes that match the specified prefix/mask + pair + type: bool + tag: + description: Set tag + type: int + timers: + description: Configure timer related constants + type: dict + suboptions: + graceful_shutdown: + description: Timers for graceful shutdown(cisco-support) + type: dict + suboptions: + initial_delay: + description: Delay before starting graceful shutdown + type: int + retain_routes: + description: Time to keep routes active after graceful shutdown + type: int + lsa: + description: OSPFv2 global LSA timers + type: dict + suboptions: + group_pacing: + description: OSPFv2 LSA group pacing timer. Interval between group + of LSA being refreshed or maxaged + type: int + min_arrival: + description: OSPFv2 MinLSArrival timer. The minimum interval in + millisec between accepting the same LSA + type: int + refresh: + description: OSPFv2 LSA refresh interval. How often self-originated + LSAs should be refreshed, in seconds + type: int + throttle: + description: OSPFv2 throttle timers + type: dict + suboptions: + lsa_all: + description: LSA throttle timers for all types of OSPFv2 LSAs + type: dict + suboptions: + initial_delay: + description: Delay to generate first occurance of LSA in milliseconds + type: int + min_delay: + description: Minimum delay between originating the same LSA + in milliseconds + type: int + max_delay: + description: Maximum delay between originating the same LSA + in milliseconds + type: int + spf: + description: OSPFv2 SPF throttle timers + type: dict + suboptions: + change_delay: + description: Delay between receiving a change to SPF calculation + in milliseconds + type: int + second_delay: + description: Delay between first and second SPF calculation + in milliseconds + type: int + max_wait: + description: Maximum wait time in milliseconds for SPF calculations + type: int + fast_reroute: + description: Fast-reroute throttle timer. Delay between end of + SPF and start of the fast-reroute computation in milliseconds + type: int + pacing_flood: + description: OSPFv2 flood pacing timer. Interval in msec to pace flooding + on all interfaces + type: int + transmit_delay: + description: Estimated time needed to send link-state update packet + type: int + weight: + description: Interface weight + 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-XR device + by executing the command B(show running-config 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. + type: str + choices: + - merged + - replaced + - deleted + - parsed + - gathered + - rendered + - overridden + default: merged + +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:anton#show running-config router ospf +# Thu Jun 11 15:54:44.569 UTC +# % No such configuration item(s) +# + +- name: Merge provided OSPFv2 configuration with the existing configuration + cisco.iosxr.iosxr_ospfv2: + config: + processes: + - process_id: '27' + areas: + - area_id: '10' + hello_interval: 2 + authentication: + keychain: ansi11393 + - process_id: '26' + adjacency_stagger: + max_adjacency: 20 + min_adjacency: 10 + - process_id: '10' + authentication: + keychain: ansible_test1102 + areas: + - area_id: '11' + default_cost: 5 + cost: 11 + - area_id: 22 + default_cost: 6 + - process_id: '30' + areas: + - area_id: 11 + default_cost: 5 + - area_id: 22 + default_cost: 6 + + cost: 2 + default_metric: 10 + transmit_delay: 2 + hello_interval: 1 + dead_interval: 2 + retransmit_interval: 2 + weight: 2 + packet_size: 577 + priority: 1 + router_id: 2.2.2.2 + demand_circuit: enable + passive: disable + summary_in: enable + flood_reduction: disable + mtu_ignore: enable + external_out: disable + state: merged + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": {} +# +# "commands": [ +# "router ospf 30", +# "cost 2", +# "weight 2", +# "passive disable", +# "priority 1", +# "flood-reduction disable", +# "default-metric 10", +# "router-id 2.2.2.2", +# "demand-circuit enable", +# "packet-size 577", +# "transmit-delay 2", +# "summary-in enable", +# "external-out disable", +# "dead-interval 2", +# "hello-interval 1", +# "retransmit-interval 2", +# "mtu-ignore enable", +# "area 11 default-cost 5", +# "area 22 default-cost 6", +# "router ospf 26", +# "adjacency stagger 10 20", +# "authentication message-digest keychain ansible1101pass", +# "router ospf 27", +# "area 10 authentication keychain ansi11393", +# "area 10 hello-interval 2", +# "router ospf 10", +# "authentication keychain ansible_test1102", +# "area 11 default-cost 5", +# "area 11 cost 11", +# "area 22 default-cost 6" +# ] +# +# "after": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "11", +# "cost": 11, +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "authentication": { +# "keychain": "ansible_test1102" +# }, +# "process_id": "10" +# }, +# { +# "adjacency_stagger": { +# "max_adjacency": 20, +# "min_adjacency": 10 +# }, +# "authentication": { +# "message_digest": { +# "keychain": "ansible1101pass" +# } +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "authentication": { +# "keychain": "ansi11393" +# }, +# "hello_interval": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "areas": [ +# { +# "area_id": "11", +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "cost": 2, +# "dead_interval": 2, +# "default_metric": 10, +# "demand_circuit": "enable", +# "external_out": "disable", +# "flood_reduction": "disable", +# "hello_interval": 1, +# "mtu_ignore": "enable", +# "packet_size": 577, +# "passive": "disable", +# "priority": 1, +# "process_id": "30", +# "retransmit_interval": 2, +# "router_id": "2.2.2.2", +# "summary_in": "enable", +# "transmit_delay": 2, +# "weight": 2 +# } +# ] +# } +# +# +# ------------ +# After state +# ------------ +# +# RP/0/RP0/CPU0:anton#show running-config router ospf +# Thu Jun 11 16:06:44.406 UTC +# router ospf 10 +# authentication keychain ansible_test1102 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospf 26 +# authentication message-digest keychain ansible1101pass +# adjacency stagger 10 20 +# ! +# router ospf 27 +# area 10 +# authentication keychain ansi11393 +# hello-interval 2 +# ! +# ! +# router ospf 30 +# router-id 2.2.2.2 +# summary-in enable +# external-out disable +# cost 2 +# packet-size 577 +# weight 2 +# passive disable +# priority 1 +# mtu-ignore enable +# flood-reduction disable +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit enable +# hello-interval 1 +# transmit-delay 2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# + + +# Using replaced +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/RP0/CPU0:anton#show running-config router ospf +# Thu Jun 11 16:06:44.406 UTC +# router ospf 10 +# authentication keychain ansible_test1102 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospf 26 +# authentication message-digest keychain ansible1101pass +# adjacency stagger 10 20 +# ! +# router ospf 27 +# area 10 +# authentication keychain ansi11393 +# hello-interval 2 +# ! +# ! +# router ospf 30 +# router-id 2.2.2.2 +# summary-in enable +# external-out disable +# cost 2 +# packet-size 577 +# weight 2 +# passive disable +# priority 1 +# mtu-ignore enable +# flood-reduction disable +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit enable +# hello-interval 1 +# transmit-delay 2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# + +- name: Replace OSPFv2 routes configurations from the device + cisco.iosxr.iosxr_ospfv2: + config: + processes: + - process_id: 27 + areas: + - area_id: 10 + hello_interval: 2 + - area_id: 20 + cost: 2 + default_cost: 2 + authentication: + keychain: ansi11393 + - process_id: 26 + adjacency_stagger: + min_adjacency: 10 + max_adjacency: 20 + state: replaced + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "11", +# "cost": 11, +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "authentication": { +# "keychain": "ansible_test1102" +# }, +# "process_id": "10" +# }, +# { +# "adjacency_stagger": { +# "max_adjacency": 20, +# "min_adjacency": 10 +# }, +# "authentication": { +# "message_digest": { +# "keychain": "ansible1101pass" +# } +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "authentication": { +# "keychain": "ansi11393" +# }, +# "hello_interval": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "areas": [ +# { +# "area_id": "11", +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "cost": 2, +# "dead_interval": 2, +# "default_metric": 10, +# "demand_circuit": "enable", +# "external_out": "disable", +# "flood_reduction": "disable", +# "hello_interval": 1, +# "mtu_ignore": "enable", +# "packet_size": 577, +# "passive": "disable", +# "priority": 1, +# "process_id": "30", +# "retransmit_interval": 2, +# "router_id": "2.2.2.2", +# "summary_in": "enable", +# "transmit_delay": 2, +# "weight": 2 +# } +# ] +# } +# +# "commands": [ +# "router ospf 27", +# "no area 10 authentication keychain ansi11393", +# "area 20 authentication keychain ansi11393", +# "area 20 default-cost 2", +# "area 20 cost 2" +# ] +# +# "after": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "11", +# "cost": 11, +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "authentication": { +# "keychain": "ansible_test1102" +# }, +# "process_id": "10" +# }, +# { +# "adjacency_stagger": { +# "max_adjacency": 20, +# "min_adjacency": 10 +# }, +# "authentication": { +# "message_digest": { +# "keychain": "ansible1101pass" +# } +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "hello_interval": 2 +# }, +# { +# "area_id": "20", +# "authentication": { +# "keychain": "ansi11393" +# }, +# "cost": 2, +# "default_cost": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "areas": [ +# { +# "area_id": "11", +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "cost": 2, +# "dead_interval": 2, +# "default_metric": 10, +# "demand_circuit": "enable", +# "external_out": "disable", +# "flood_reduction": "disable", +# "hello_interval": 1, +# "mtu_ignore": "enable", +# "packet_size": 577, +# "passive": "disable", +# "priority": 1, +# "process_id": "30", +# "retransmit_interval": 2, +# "router_id": "2.2.2.2", +# "summary_in": "enable", +# "transmit_delay": 2, +# "weight": 2 +# } +# ] +# } +# +# +# ----------- +# After state +# ----------- +# +# RP/0/RP0/CPU0:anton(config)#do show running-config router ospf +# Thu Jun 11 16:40:31.038 UTC +# router ospf 10 +# authentication keychain ansible_test1102 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospf 26 +# authentication message-digest keychain ansible1101pass +# adjacency stagger 10 20 +# ! +# router ospf 27 +# area 10 +# hello-interval 2 +# ! +# area 20 +# cost 2 +# authentication keychain ansi11393 +# default-cost 2 +# ! +# ! +# router ospf 30 +# router-id 2.2.2.2 +# summary-in enable +# external-out disable +# cost 2 +# packet-size 577 +# weight 2 +# passive disable +# priority 1 +# mtu-ignore enable +# flood-reduction disable +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit enable +# hello-interval 1 +# transmit-delay 2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# + + +# Using overridden +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/RP0/CPU0:anton#show running-config router ospf +# Thu Jun 11 16:06:44.406 UTC +# router ospf 10 +# authentication keychain ansible_test1102 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospf 26 +# authentication message-digest keychain ansible1101pass +# adjacency stagger 10 20 +# ! +# router ospf 27 +# area 10 +# authentication keychain ansi11393 +# hello-interval 2 +# ! +# ! +# router ospf 30 +# router-id 2.2.2.2 +# summary-in enable +# external-out disable +# cost 2 +# packet-size 577 +# weight 2 +# passive disable +# priority 1 +# mtu-ignore enable +# flood-reduction disable +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit enable +# hello-interval 1 +# transmit-delay 2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# + +- name: Override existing OSPFv2 configurations from the device + cisco.iosxr.iosxr_ospfv2: + config: + processes: + - process_id: 27 + areas: + - area_id: 10 + hello_interval: 2 + authentication: + keychain: ansi11393 + - area_id: 20 + cost: 2 + default_cost: 2 + authentication: + keychain: ansi11393 + - process_id: 26 + adjacency_stagger: + min_adjacency: 10 + max_adjacency: 20 + state: overridden + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "11", +# "cost": 11, +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "authentication": { +# "keychain": "ansible_test1102" +# }, +# "process_id": "10" +# }, +# { +# "adjacency_stagger": { +# "max_adjacency": 20, +# "min_adjacency": 10 +# }, +# "authentication": { +# "message_digest": { +# "keychain": "ansible1101pass" +# } +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "authentication": { +# "keychain": "ansi11393" +# }, +# "hello_interval": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "areas": [ +# { +# "area_id": "11", +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "cost": 2, +# "dead_interval": 2, +# "default_metric": 10, +# "demand_circuit": "enable", +# "external_out": "disable", +# "flood_reduction": "disable", +# "hello_interval": 1, +# "mtu_ignore": "enable", +# "packet_size": 577, +# "passive": "disable", +# "priority": 1, +# "process_id": "30", +# "retransmit_interval": 2, +# "router_id": "2.2.2.2", +# "summary_in": "enable", +# "transmit_delay": 2, +# "weight": 2 +# } +# ] +# } +# +# "commands": [ +# "router ospf 10", +# "no authentication keychain ansible_test1102", +# "no area 11 default-cost 5", +# "no area 11 cost 11", +# "no area 22 default-cost 6", +# "router ospf 30", +# "no cost 2", +# "no weight 2", +# "no passive disable", +# "no priority 1", +# "no flood-reduction disable", +# "no default-metric 10", +# "no router-id 2.2.2.2", +# "no demand-circuit enable", +# "no packet-size 577", +# "no transmit-delay 2", +# "no summary-in enable", +# "no external-out disable", +# "no dead-interval 2", +# "no hello-interval 1", +# "no retransmit-interval 2", +# "no mtu-ignore enable", +# "no area 11 default-cost 5", +# "no area 22 default-cost 6", +# "router ospf 27", +# "area 20 authentication keychain ansi11393", +# "area 20 default-cost 2", +# "area 20 cost 2" +# ] +# +# "after": { +# "processes": [ +# { +# "process_id": "10" +# }, +# { +# "adjacency_stagger": { +# "max_adjacency": 20, +# "min_adjacency": 10 +# }, +# "authentication": { +# "message_digest": { +# "keychain": "ansible1101pass" +# } +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "authentication": { +# "keychain": "ansi11393" +# }, +# "hello_interval": 2 +# }, +# { +# "area_id": "20", +# "authentication": { +# "keychain": "ansi11393" +# }, +# "cost": 2, +# "default_cost": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "process_id": "30" +# } +# ] +# } +# +# +# ----------- +# After state +# ----------- +# +# RP/0/RP0/CPU0:anton#show running-config router ospf +# Thu Jun 11 16:50:36.332 UTC +# router ospf 10 +# ! +# router ospf 26 +# authentication message-digest keychain ansible1101pass +# adjacency stagger 10 20 +# ! +# router ospf 27 +# area 10 +# authentication keychain ansi11393 +# hello-interval 2 +# ! +# area 20 +# cost 2 +# authentication keychain ansi11393 +# default-cost 2 +# ! +# ! +# router ospf 30 +# ! +# + + +# Using deleted +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/RP0/CPU0:anton#show running-config router ospf +# Thu Jun 11 16:06:44.406 UTC +# router ospf 10 +# authentication keychain ansible_test1102 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospf 26 +# authentication message-digest keychain ansible1101pass +# adjacency stagger 10 20 +# ! +# router ospf 27 +# area 10 +# authentication keychain ansi11393 +# hello-interval 2 +# ! +# ! +# router ospf 30 +# router-id 2.2.2.2 +# summary-in enable +# external-out disable +# cost 2 +# packet-size 577 +# weight 2 +# passive disable +# priority 1 +# mtu-ignore enable +# flood-reduction disable +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit enable +# hello-interval 1 +# transmit-delay 2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# + +- name: Deleted existing OSPFv2 configurations from the device + cisco.iosxr.iosxr_ospfv2: + config: + processes: + - process_id: '10' + - process_id: '26' + - process_id: '27' + - process_id: '30' + state: deleted + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "11", +# "cost": 11, +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "authentication": { +# "keychain": "ansible_test1102" +# }, +# "process_id": "10" +# }, +# { +# "adjacency_stagger": { +# "max_adjacency": 20, +# "min_adjacency": 10 +# }, +# "authentication": { +# "message_digest": { +# "keychain": "ansible1101pass" +# } +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "authentication": { +# "keychain": "ansi11393" +# }, +# "hello_interval": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "areas": [ +# { +# "area_id": "11", +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "cost": 2, +# "dead_interval": 2, +# "default_metric": 10, +# "demand_circuit": "enable", +# "external_out": "disable", +# "flood_reduction": "disable", +# "hello_interval": 1, +# "mtu_ignore": "enable", +# "packet_size": 577, +# "passive": "disable", +# "priority": 1, +# "process_id": "30", +# "retransmit_interval": 2, +# "router_id": "2.2.2.2", +# "summary_in": "enable", +# "transmit_delay": 2, +# "weight": 2 +# } +# ] +# }, +# +# "commands": [ +# "router ospf 10", +# "no authentication keychain ansible_test1102", +# "no area 11 default-cost 5", +# "no area 11 cost 11", +# "no area 22 default-cost 6", +# "router ospf 26", +# "no adjacency stagger 10 20", +# "no authentication message-digest keychain ansible1101pass", +# "router ospf 27", +# "no area 10 authentication keychain ansi11393", +# "no area 10 hello-interval 2", +# "router ospf 30", +# "no cost 2", +# "no weight 2", +# "no passive disable", +# "no priority 1", +# "no flood-reduction disable", +# "no default-metric 10", +# "no router-id 2.2.2.2", +# "no demand-circuit enable", +# "no packet-size 577", +# "no transmit-delay 2", +# "no summary-in enable", +# "no external-out disable", +# "no dead-interval 2", +# "no hello-interval 1", +# "no retransmit-interval 2", +# "no mtu-ignore enable", +# "no area 11 default-cost 5", +# "no area 22 default-cost 6" +# ] +# +# "after": { +# "processes": [ +# { +# "process_id": "10" +# }, +# { +# "process_id": "26" +# }, +# { +# "process_id": "27" +# }, +# { +# "process_id": "30" +# } +# ] +# } +# +# +# ----------- +# After state +# ----------- +# +# RP/0/RP0/CPU0:anton(config)#show running-config router ospf +# Thu Jun 11 17:07:34.218 UTC +# router ospf 10 +# ! +# router ospf 26 +# ! +# router ospf 27 +# ! +# router ospf 30 +# ! + + +# Using parsed +# parsed.cfg +# ------------ +# Thu Jun 11 17:28:51.918 UTC +# router ospf 10 +# authentication keychain ansible_test1102 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospf 26 +# authentication message-digest keychain ansible1101pass +# adjacency stagger 10 20 +# ! +# router ospf 27 +# area 10 +# authentication keychain ansi11393 +# hello-interval 2 +# ! +# ! +# router ospf 30 +# router-id 2.2.2.2 +# summary-in enable +# external-out disable +# cost 2 +# packet-size 577 +# weight 2 +# passive disable +# priority 1 +# mtu-ignore enable +# flood-reduction disable +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit enable +# hello-interval 1 +# transmit-delay 2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +- name: Parsed the device configuration to get output commands + cisco.iosxr.iosxr_ospfv2: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "11", +# "cost": 11, +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "authentication": { +# "keychain": "ansible_test1102" +# }, +# "process_id": "10" +# }, +# { +# "adjacency_stagger": { +# "max_adjacency": 20, +# "min_adjacency": 10 +# }, +# "authentication": { +# "message_digest": { +# "keychain": "ansible1101pass" +# } +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "authentication": { +# "keychain": "ansi11393" +# }, +# "hello_interval": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "areas": [ +# { +# "area_id": "11", +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "cost": 2, +# "dead_interval": 2, +# "default_metric": 10, +# "demand_circuit": "enable", +# "external_out": "disable", +# "flood_reduction": "disable", +# "hello_interval": 1, +# "mtu_ignore": "enable", +# "packet_size": 577, +# "passive": "disable", +# "priority": 1, +# "process_id": "30", +# "retransmit_interval": 2, +# "router_id": "2.2.2.2", +# "summary_in": "enable", +# "transmit_delay": 2, +# "weight": 2 +# } +# ] +# } + + + +# Using rendered +# +# +- name: Render the commands for provided configuration + cisco.iosxr.iosxr_ospfv2: + config: + processes: + - process_id: 27 + areas: + - area_id: 10 + hello_interval: 2 + authentication: + keychain: ansi11393 + - process_id: 26 + adjacency_stagger: + min_adjacency: 10 + max_adjacency: 20 + - process_id: 10 + authentication: + keychain: ansible_test1102 + areas: + - area_id: 11 + default_cost: 5 + cost: 11 + - area_id: 22 + default_cost: 6 + - process_id: 30 + areas: + - area_id: 11 + default_cost: 5 + - area_id: 22 + default_cost: 6 + + cost: 2 + default_metric: 10 + transmit_delay: 2 + hello_interval: 1 + dead_interval: 2 + retransmit_interval: 2 + weight: 2 + packet_size: 577 + priority: 1 + router_id: 2.2.2.2 + demand_circuit: enable + passive: disable + summary_in: enable + flood_reduction: disable + mtu_ignore: enable + external_out: disable + state: rendered + +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "rendered": [ +# "router ospf 27", +# "area 10 authentication keychain ansi11393", +# "area 10 hello-interval 2", +# "router ospf 26", +# "adjacency stagger 10 20", +# "authentication message-digest keychain ansible1101pass", +# "router ospf 10", +# "authentication keychain ansible_test1102", +# "area 11 default-cost 5", +# "area 11 cost 11", +# "area 22 default-cost 6", +# "router ospf 30", +# "cost 2", +# "weight 2", +# "passive disable", +# "priority 1", +# "flood-reduction disable", +# "default-metric 10", +# "router-id 2.2.2.2", +# "demand-circuit enable", +# "packet-size 577", +# "transmit-delay 2", +# "summary-in enable", +# "external-out disable", +# "dead-interval 2", +# "hello-interval 1", +# "retransmit-interval 2", +# "mtu-ignore enable", +# "area 11 default-cost 5", +# "area 22 default-cost 6" +# ] + + +# Using gathered +# +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:anton#show running-config router ospf +# Thu Jun 11 16:06:44.406 UTC +# router ospf 10 +# authentication keychain ansible_test1102 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospf 26 +# authentication message-digest keychain ansible1101pass +# adjacency stagger 10 20 +# ! +# router ospf 27 +# area 10 +# authentication keychain ansi11393 +# hello-interval 2 +# ! +# ! +# router ospf 30 +# router-id 2.2.2.2 +# summary-in enable +# external-out disable +# cost 2 +# packet-size 577 +# weight 2 +# passive disable +# priority 1 +# mtu-ignore enable +# flood-reduction disable +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit enable +# hello-interval 1 +# transmit-delay 2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# +- name: Gather ospfv2 routes configuration + cisco.iosxr.iosxr_ospfv2: + state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "gathered": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "11", +# "cost": 11, +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "authentication": { +# "keychain": "ansible_test1102" +# }, +# "process_id": "10" +# }, +# { +# "adjacency_stagger": { +# "max_adjacency": 20, +# "min_adjacency": 10 +# }, +# "authentication": { +# "message_digest": { +# "keychain": "ansible1101pass" +# } +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "authentication": { +# "keychain": "ansi11393" +# }, +# "hello_interval": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "areas": [ +# { +# "area_id": "11", +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "cost": 2, +# "dead_interval": 2, +# "default_metric": 10, +# "demand_circuit": "enable", +# "external_out": "disable", +# "flood_reduction": "disable", +# "hello_interval": 1, +# "mtu_ignore": "enable", +# "packet_size": 577, +# "passive": "disable", +# "priority": 1, +# "process_id": "30", +# "retransmit_interval": 2, +# "router_id": "2.2.2.2", +# "summary_in": "enable", +# "transmit_delay": 2, +# "weight": 2 +# } +# ] +# } +# +# After state: +# ------------- +# +# RP/0/RP0/CPU0:anton#show running-config router ospf +# Thu Jun 11 16:06:44.406 UTC +# router ospf 10 +# authentication keychain ansible_test1102 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospf 26 +# authentication message-digest keychain ansible1101pass +# adjacency stagger 10 20 +# ! +# router ospf 27 +# area 10 +# authentication keychain ansi11393 +# hello-interval 2 +# ! +# ! +# router ospf 30 +# router-id 2.2.2.2 +# summary-in enable +# external-out disable +# cost 2 +# packet-size 577 +# weight 2 +# passive disable +# priority 1 +# mtu-ignore enable +# flood-reduction disable +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit enable +# hello-interval 1 +# transmit-delay 2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# +# + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: dict + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: dict + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: + - "router ospf 30" + - "authentication message-digest keychain 'ansible1101pass'" +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.ospfv2.ospfv2 import ( + Ospfv2Args, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.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, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive, + ) + result = Ospfv2(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_ospfv3.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_ospfv3.py new file mode 100644 index 00000000..ae9f5719 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_ospfv3.py @@ -0,0 +1,2783 @@ +#!/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 iosxr_ospfv3 +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +module: iosxr_ospfv3 +version_added: 1.1.0 +short_description: ospfv3 resource module +description: + - This module manages global ospfv3 configuration on devices running Cisco IOS-XR +author: Rohit Thakur (@rohitthakur2590) +notes: + - Tested against IOS-XR 6.1.3 + - This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html) +options: + 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-XR device + by executing the command B(show running-config 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 + config: + description: A list of ospfv3 process configuration + type: dict + suboptions: + processes: + description: A list of ospfv3 instances configuration + type: list + elements: dict + suboptions: + process_id: + description: The OSPFv3 Process ID + type: str + required: true + address_family_unicast: + description: Enable unicast topology for ipv4 address family + type: bool + authentication: + description: Enable authentication + type: dict + suboptions: + disable: + description: Do not authenticate OSPFv3 packets + type: bool + default: false + ipsec: + description: Specify IPSec AH authentication attributes + type: dict + suboptions: + spi: + description: Specify the Security Parameter Index value + type: int + algorithim_type: + description: Specify the type of algorithim + type: str + choices: ["md5", "sha1"] + key: + description: Specify key + type: str + clear_key: + description: Specify key in cleartext form + type: str + password_key: + description: Specify key in encrypted form + type: str + auto_cost: + description: Calculate ospfv3 interface cost according to bandwidth + type: dict + suboptions: + reference_bandwidth: + description: Specify reference bandwidth in megabits per sec + type: int + disable: + description: Assign ospfv3 cost based on interface type + type: bool + bfd: + description: Configure BFD parameters + type: dict + suboptions: + fast_detect: + description: Configure fast detection + type: dict + suboptions: + set: + description: Enable fast detection only + type: bool + strict_mode: + description: Hold down neighbor session until BFD session is up + type: bool + minimum_interval: + description: Hello interval in milli-seconds + type: int + multiplier: + description: Detect multiplier + type: int + areas: + description: Configure ospfv3 areas' properties + type: list + elements: dict + suboptions: + area_id: + description: Area ID as IP address or integer + type: str + required: True + authentication: + description: Enable authentication + type: dict + suboptions: + disable: + description: Do not authenticate OSPFv3 packets + type: bool + default: false + ipsec: + description: Specify IPSec AH authentication attributes + type: dict + suboptions: + spi: + description: Specify the Security Parameter Index value + type: int + algorithim_type: + description: Specify the type of algorithim + type: str + choices: ["md5", "sha1"] + key: + description: Specify key + type: str + clear_key: + description: Specify key in cleartext form + type: str + password_key: + description: Specify key in encrypted form + type: str + bfd: + description: Configure BFD parameters + type: dict + suboptions: + fast_detect: + description: Configure fast detection + type: dict + suboptions: + set: + description: Enable fast detection only + type: bool + strict_mode: + description: Hold down neighbor session until BFD session is up + type: bool + minimum_interval: + description: Hello interval in milli-seconds + type: int + multiplier: + description: Detect multiplier + type: int + cost: + description: Interface cost + type: int + database_filter: + description: Filter LSAs during synchronization and flooding + type: dict + suboptions: + all_outgoing_lsa: + description: Filter all outgoing LSA + type: bool + dead_interval: + description: Interval after which a neighbor is declared dead + type: int + default_cost: + description: Set the summary default-cost of a NSSA/stub area. Stub's advertised external route metric + type: int + demand_circuit: + description: Enable/Disable ospfv3 demand circuit + type: bool + distrinbute_rib_prefix_list_name: + description: Filter LSAs during synchronization and flooding + type: str + fast_reroute: + description: Specify IP Fast Reroute + type: dict + suboptions: + disabled: + description: Disable IP fast reroute + type: bool + per_link: + description: Specify per-prefix computation + type: dict + suboptions: + information_type: + description: Specify per-link LFA exclusion or FRR LFA candidate information + type: str + choices: ["exclude", "lfa_candidate"] + use_candidate_only: + description: Enable/Disable backup selection from candidate-list only + type: bool + interface: + description: Specify Per-link LFA exclusion information + type: dict + suboptions: + bvi: + description: Specify Bridge-Group Virtual Interface + type: list + elements: int + bundle_ether: + description: Specify Aggregated Ethernet interface(s) + type: list + elements: int + pos_int: + description: Specify Aggregated pos interface(s) + type: list + elements: int + fast_ethernet: + description: Specify FastEthernet/IEEE 802.3 interface(s) + type: list + elements: str + fiftygige: + description: Specify FiftyGigE/IEEE 802.3 interface(s) + type: list + elements: str + fortygige: + description: Specify FortyGigE/IEEE 802.3 interface(s) + type: list + elements: str + fourhundredgige: + description: Specify FourHundredGigE/IEEE 802.3 interface(s) + type: list + elements: str + gigabitethernet: + description: Specify GigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: str + hundredgige: + description: Specify HundredGigE/IEEE 802.3 interface(s) + type: list + elements: str + mgmteth: + description: Specify MgmtEth/IEEE 802.3 interface(s) + type: list + elements: str + multilink: + description: Specify Multilink network interface(s) + type: list + elements: str + pw_ether: + description: Specify PWHE Ethernet Interface + type: list + elements: int + pw_iw: + description: Specify PWHE VC11 IP Interworking Interface + type: list + elements: int + srp: + description: Specify SRP interface(s) + type: list + elements: str + serial: + description: Specify Serial network interface(s) + type: list + elements: str + tengige: + description: Specify TenGigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: str + twentyfivegige: + description: Specify TwentyFiveGigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: str + twohundredgige: + description: Specify TwoHundredGigE/IEEE 802.3 interface(s) + type: list + elements: str + nve: + description: Specify Network Virtualization Endpoint Interface(s) + type: list + elements: int + tunnel_ip: + description: Specify GRE/IPinIP Tunnel Interface(s) + type: list + elements: int + tunnel_ipsec: + description: Specify IPSec Tunnel interface(s) + type: list + elements: int + tunnel_mte: + description: Specify MPLS Traffic Engineering P2MP Tunnel interface(s) + type: list + elements: int + tunnel_mpls: + description: MPLS Transport Protocol Tunnel interface + type: int + per_prefix: + description: Specify per-prefix computation + type: dict + suboptions: + information_type: + description: Specify per_prefix LFA exclusion or FRR LFA candidate information + type: str + choices: ["exclude", "lfa_candidate"] + use_candidate_only: + description: Enable/Disable backup selection from candidate-list only + type: bool + interface: + description: Specify Per-link LFA exclusion information + type: dict + suboptions: + bvi: + description: Specify Bridge-Group Virtual Interface + type: list + elements: int + bundle_ether: + description: Specify Aggregated Ethernet interface(s) + type: list + elements: int + pos_int: + description: Specify Aggregated pos interface(s) + type: list + elements: int + fast_ethernet: + description: Specify FastEthernet/IEEE 802.3 interface(s) + type: list + elements: str + fiftygige: + description: Specify FiftyGigE/IEEE 802.3 interface(s) + type: list + elements: str + fortygige: + description: Specify FortyGigE/IEEE 802.3 interface(s) + type: list + elements: str + fourhundredgige: + description: Specify FourHundredGigE/IEEE 802.3 interface(s) + type: list + elements: str + gigabitethernet: + description: Specify GigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: str + hundredgige: + description: Specify HundredGigE/IEEE 802.3 interface(s) + type: list + elements: str + mgmteth: + description: Specify MgmtEth/IEEE 802.3 interface(s) + type: list + elements: str + multilink: + description: Specify Multilink network interface(s) + type: list + elements: str + pw_ether: + description: Specify PWHE Ethernet Interface + type: list + elements: int + pw_iw: + description: Specify PWHE VC11 IP Interworking Interface + type: list + elements: int + srp: + description: Specify SRP interface(s) + type: list + elements: str + serial: + description: Specify Serial network interface(s) + type: list + elements: str + tengige: + description: Specify TenGigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: str + twentyfivegige: + description: Specify TwentyFiveGigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: str + twohundredgige: + description: Specify TwoHundredGigE/IEEE 802.3 interface(s) + type: list + elements: str + nve: + description: Specify Network Virtualization Endpoint Interface(s) + type: list + elements: int + tunnel_ip: + description: Specify GRE/IPinIP Tunnel Interface(s) + type: list + elements: int + tunnel_ipsec: + description: Specify IPSec Tunnel interface(s) + type: list + elements: int + tunnel_mte: + description: Specify MPLS Traffic Engineering P2MP Tunnel interface(s) + type: list + elements: int + tunnel_mpls: + description: MPLS Transport Protocol Tunnel interface + type: int + flood_reduction: + description: Enable/Disable flood reduction + type: bool + hello_interval: + description: Specify Time between HELLO packets + type: int + instance_id: + description: Specify instance ID + type: int + mtu_ignore: + description: Enable/Disable ignoring of MTU in DBD packets + type: bool + mpls_ldp_sync: + description: Enable/Disable MPLS LDP Sync + type: bool + network: + description: Specify Network type + type: str + choices: ["broadcast", "non-broadcast", "point-to-multipoint", "point-to-point"] + nssa: + description: NSSA settings for the area + type: dict + suboptions: + set: + description: Configure area as NSSA + type: bool + default_information_originate: + description: Originate default Type 7 LSA + type: dict + suboptions: + set: + description: Set nssa to default information originate + type: bool + metric: + description: ospfv3 default metric + type: int + metric_type: + description: Metric type for default routes + type: int + no_redistribution: + description: Do not send redistributed LSAs into NSSA area + type: bool + no_summary: + description: Do not send summary LSAs into NSSA area + type: bool + translate: + description: Translate LSA + type: dict + suboptions: + type7: + description: Translate from Type 7 to Type 5 + type: dict + suboptions: + always: + description: Always translate LSAs + type: bool + required: true + packet_size: + description: Specify limit size of OSPFv3 packets + type: int + passive: + description: Enable/Disable routing updates on an interface + type: bool + prefix_suppression: + description: Hide all transit addresses on this interface + type: bool + priority: + description: Specify Router priority + type: int + ranges: + description: Summarize routes matching address/mask (border routers only) + type: list + elements: dict + suboptions: + address: + description: IP in Prefix format (X:X::X/length) + type: str + required: True + cost: + description: Specify user specified metric for this range + type: int + advertise: + description: Advertise this range (default) + type: bool + not_advertise: + description: DoNotAdvertise this range + type: bool + retransmit_interval: + description: Specify Delay between LSA retransmissions + type: int + stub: + description: Settings for configuring the area as a stub + type: dict + suboptions: + set: + description: Configure the area as a stub + type: bool + no_summary: + description: Do not send summary LSA into stub area + type: bool + transmit_delay: + description: Specify estimated time needed to send link-state update packet + type: int + virtual_link: + description: Define a virtual link + type: list + elements: dict + suboptions: + id: + description: Router-ID of virtual link neighbor (A.B.C.D) + type: str + required: True + authentication: + description: Enable authentication + type: dict + suboptions: + disable: + description: Do not authenticate OSPFv3 packets + type: bool + default: false + ipsec: + description: Specify IPSec AH authentication attributes + type: dict + suboptions: + spi: + description: Specify the Security Parameter Index value + type: int + algorithim_type: + description: Specify the type of algorithim + type: str + choices: ["md5", "sha1"] + key: + description: Specify key + type: str + clear_key: + description: Specify key in cleartext form + type: str + password_key: + description: Specify key in encrypted form + type: str + dead_interval: + description: Interval after which a neighbor is declared dead + type: int + hello_interval: + description: Time between HELLO packets + type: int + retransmit_interval: + description: Delay between LSA retransmissions + type: int + transmit_delay: + description: Link state transmit delay + type: int + encryption: + description: Encrypt and authenticate OSPFv3 packets + type: dict + suboptions: + disable: + description: Do not encrypt OSPFv3 packets + type: bool + default: false + ipsec: + description: Specify IPSec ESP encryption and authentication + type: dict + suboptions: + spi: + description: Specify the Security Parameter Index value + type: int + esp: + description: Specify encryption parameters + type: dict + suboptions: + triple_des: + description: This specify the triple DES algorithim + type: dict + suboptions: + key: + description: Cleartext 3DES key + type: str + clear_key: + description: Specify 3DES key in cleartext form + type: str + password_key: + description: Specify 3DES key in encrypted form + type: str + aes: + description: This specify the aes algorithim + type: dict + suboptions: + algorithim_type: + description: Specify the bit encryption for aes algorithim + type: str + choices: ["192", "256"] + key: + description: Cleartext AES key + type: str + clear_key: + description: Specify AES key in cleartext form + type: str + password_key: + description: Specify AES key in encrypted form + type: str + des: + description: This specify the des algorithim + type: dict + suboptions: + key: + description: Cleartext AES key + type: str + clear_key: + description: Specify AES key in cleartext form + type: str + password_key: + description: Specify AES key in encrypted form + type: str + null_encryption: + description: Specify null encryption attributes + type: dict + suboptions: + authentication: + description: Specify authentication parameters + type: dict + suboptions: + algorithim_type: + description: Specify the type of algorithim + type: str + choices: ["md5", "sha1"] + key: + description: Specify key + type: str + clear_key: + description: Specify key in cleartext form + type: str + password_key: + description: Specify key in encrypted form + type: str + encryption: + description: Encrypt and authenticate OSPFv3 packets + type: dict + suboptions: + disable: + description: Do not encrypt OSPFv3 packets + type: bool + default: false + ipsec: + description: Specify IPSec ESP encryption and authentication + type: dict + suboptions: + spi: + description: Specify the Security Parameter Index value + type: int + esp: + description: Specify encryption parameters + type: dict + suboptions: + triple_des: + description: This specify the triple DES algorithim + type: dict + suboptions: + key: + description: Cleartext 3DES key + type: str + clear_key: + description: Specify 3DES key in cleartext form + type: str + password_key: + description: Specify 3DES key in encrypted form + type: str + aes: + description: This specify the aes algorithim + type: dict + suboptions: + algorithim_type: + description: Specify the bit encryption for aes algorithim + type: str + choices: ["192", "256"] + key: + description: Cleartext AES key + type: str + clear_key: + description: Specify AES key in cleartext form + type: str + password_key: + description: Specify AES key in encrypted form + type: str + des: + description: This specify the des algorithim + type: dict + suboptions: + key: + description: Cleartext AES key + type: str + clear_key: + description: Specify AES key in cleartext form + type: str + password_key: + description: Specify AES key in encrypted form + type: str + null_encryption: + description: Specify null encryption attributes + type: dict + suboptions: + authentication: + description: Specify authentication parameters + type: dict + suboptions: + algorithim_type: + description: Specify the type of algorithim + type: str + choices: ["md5", "sha1"] + key: + description: Specify key + type: str + clear_key: + description: Specify key in cleartext form + type: str + password_key: + description: Specify key in encrypted form + type: str + capability: + description: Enable specific OSPFv3 feature + type: dict + suboptions: + type7: + description: Specify type7 nssa capability + type: dict + suboptions: + prefer: + description: Prefer type7 externals over type5 + type: bool + translate: + description: Translate type7 to type5 + type: bool + cost: + description: Specify Interface cost + type: int + database_filter: + description: Filter LSAs during synchronization and flooding + type: dict + suboptions: + all_outgoing_lsa: + description: Filter all outgoing LSA + type: bool + dead_interval: + description: Interval after which a neighbor is declared dead + type: int + default_information_originate: + description: Control distribution of default information + type: dict + suboptions: + always: + description: Always advertise default route + type: bool + metric: + description: ospfv3 default metric + type: int + metric_type: + description: ospfv3 metric type for default routes + type: int + route_policy: + description: Apply route-policy to default-information origination + type: str + tag: + description: Set tag for default route + type: int + set: + description: Enable distribution of default route + type: bool + default_metric: + description: Set metric of redistributed routes + type: int + demand_circuit: + description: Enable/Disable ospfv3 demand circuit + type: bool + distance: + description: Define an administrative distance + type: dict + suboptions: + admin_distance: + description: Administrative distance + type: int + ospfv3_distance: + description: ospfv3 administrative distance + type: dict + suboptions: + external: + description: Distance for external routes + type: int + inter_area: + description: Distance for inter-area routes + type: int + intra_area: + description: Distance for intra-area routes + type: int + distribute_list: + description: Filter prefixes to/from RIB + type: dict + suboptions: + prefix_list: + description: Filter prefixes based on an IPv6 prefix-list + type: list + elements: str + suboptions: + name: + description: Specify Prefix-list name + type: str + in: + description: Filter prefixes installed to RIB + type: bool + out: + description: Filter prefixes redistributed from RIB + type: bool + encryption: + description: Encrypt and authenticate OSPFv3 packets + type: dict + suboptions: + disable: + description: Do not encrypt OSPFv3 packets + type: bool + default: false + ipsec: + description: Specify IPSec ESP encryption and authentication + type: dict + suboptions: + spi: + description: Specify the Security Parameter Index value + type: int + esp: + description: Specify encryption parameters + type: dict + suboptions: + triple_des: + description: This specify the triple DES algorithim + type: dict + suboptions: + key: + description: Cleartext 3DES key + type: str + clear_key: + description: Specify 3DES key in cleartext form + type: str + password_key: + description: Specify 3DES key in encrypted form + type: str + aes: + description: This specify the aes algorithim + type: dict + suboptions: + algorithim_type: + description: Specify the bit encryption for aes algorithim + type: str + choices: ["192", "256"] + key: + description: Cleartext AES key + type: str + clear_key: + description: Specify AES key in cleartext form + type: str + password_key: + description: Specify AES key in encrypted form + type: str + des: + description: This specify the des algorithim + type: dict + suboptions: + key: + description: Cleartext AES key + type: str + clear_key: + description: Specify AES key in cleartext form + type: str + password_key: + description: Specify AES key in encrypted form + type: str + null_encryption: + description: Specify null encryption attributes + type: dict + suboptions: + authentication: + description: Specify authentication parameters + type: dict + suboptions: + algorithim_type: + description: Specify the type of algorithim + type: str + choices: ["md5", "sha1"] + key: + description: Specify key + type: str + clear_key: + description: Specify key in cleartext form + type: str + password_key: + description: Specify key in encrypted form + type: str + fast_reroute: + description: Specify IP Fast Reroute + type: dict + suboptions: + disabled: + description: Disable IP fast reroute + type: bool + per_link: + description: Specify per-prefix computation + type: dict + suboptions: + information_type: + description: Specify per-link LFA exclusion or FRR LFA candidate information + type: str + choices: ["exclude", "lfa_candidate"] + use_candidate_only: + description: Enable/Disable backup selection from candidate-list only + type: bool + interface: + description: Specify Per-link LFA exclusion information + type: dict + suboptions: + bvi: + description: Specify Bridge-Group Virtual Interface + type: list + elements: int + bundle_ether: + description: Specify Aggregated Ethernet interface(s) + type: list + elements: int + pos_int: + description: Specify Aggregated pos interface(s) + type: list + elements: int + fast_ethernet: + description: Specify FastEthernet/IEEE 802.3 interface(s) + type: list + elements: str + fiftygige: + description: Specify FiftyGigE/IEEE 802.3 interface(s) + type: list + elements: str + fortygige: + description: Specify FortyGigE/IEEE 802.3 interface(s) + type: list + elements: str + fourhundredgige: + description: Specify FourHundredGigE/IEEE 802.3 interface(s) + type: list + elements: str + gigabitethernet: + description: Specify GigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: str + hundredgige: + description: Specify HundredGigE/IEEE 802.3 interface(s) + type: list + elements: str + mgmteth: + description: Specify MgmtEth/IEEE 802.3 interface(s) + type: list + elements: str + multilink: + description: Specify Multilink network interface(s) + type: list + elements: str + pw_ether: + description: Specify PWHE Ethernet Interface + type: list + elements: int + pw_iw: + description: Specify PWHE VC11 IP Interworking Interface + type: list + elements: int + srp: + description: Specify SRP interface(s) + type: list + elements: str + serial: + description: Specify Serial network interface(s) + type: list + elements: str + tengige: + description: Specify TenGigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: str + twentyfivegige: + description: Specify TwentyFiveGigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: str + twohundredgige: + description: Specify TwoHundredGigE/IEEE 802.3 interface(s) + type: list + elements: str + nve: + description: Specify Network Virtualization Endpoint Interface(s) + type: list + elements: int + tunnel_ip: + description: Specify GRE/IPinIP Tunnel Interface(s) + type: list + elements: int + tunnel_ipsec: + description: Specify IPSec Tunnel interface(s) + type: list + elements: int + tunnel_mte: + description: Specify MPLS Traffic Engineering P2MP Tunnel interface(s) + type: list + elements: int + tunnel_mpls: + description: MPLS Transport Protocol Tunnel interface + type: int + per_prefix: + description: Specify per-prefix computation + type: dict + suboptions: + information_type: + description: Specify per_prefix LFA exclusion or FRR LFA candidate information + type: str + choices: ["exclude", "lfa_candidate"] + use_candidate_only: + description: Enable/Disable backup selection from candidate-list only + type: bool + interface: + description: Specify Per-link LFA exclusion information + type: dict + suboptions: + bvi: + description: Specify Bridge-Group Virtual Interface + type: list + elements: int + bundle_ether: + description: Specify Aggregated Ethernet interface(s) + type: list + elements: int + post_int: + description: Specify Aggregated pos interface(s) + type: list + elements: int + fast_ethernet: + description: Specify FastEthernet/IEEE 802.3 interface(s) + type: list + elements: str + fiftygige: + description: Specify FiftyGigE/IEEE 802.3 interface(s) + type: list + elements: str + fortygige: + description: Specify FortyGigE/IEEE 802.3 interface(s) + type: list + elements: str + fourhundredgige: + description: Specify FourHundredGigE/IEEE 802.3 interface(s) + type: list + elements: str + gigabitethernet: + description: Specify GigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: str + hundredgige: + description: Specify HundredGigE/IEEE 802.3 interface(s) + type: list + elements: str + mgmteth: + description: Specify MgmtEth/IEEE 802.3 interface(s) + type: list + elements: str + multilink: + description: Specify Multilink network interface(s) + type: list + elements: str + pw_ether: + description: Specify PWHE Ethernet Interface + type: list + elements: int + pw_iw: + description: Specify PWHE VC11 IP Interworking Interface + type: list + elements: int + srp: + description: Specify SRP interface(s) + type: list + elements: str + serial: + description: Specify Serial network interface(s) + type: list + elements: str + tengige: + description: Specify TenGigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: str + twentyfivegige: + description: Specify TwentyFiveGigabitEthernet/IEEE 802.3 interface(s) + type: list + elements: str + twohundredgige: + description: Specify TwoHundredGigE/IEEE 802.3 interface(s) + type: list + elements: str + nve: + description: Specify Network Virtualization Endpoint Interface(s) + type: list + elements: int + tunnel_ip: + description: Specify GRE/IPinIP Tunnel Interface(s) + type: list + elements: int + tunnel_ipsec: + description: Specify IPSec Tunnel interface(s) + type: list + elements: int + tunnel_mte: + description: Specify MPLS Traffic Engineering P2MP Tunnel interface(s) + type: list + elements: int + tunnel_mpls: + description: MPLS Transport Protocol Tunnel interface + type: int + flood_reduction: + description: Enable/Disable flood reduction + type: bool + graceful_restart: + description: Enable Graceful-Restart + type: dict + suboptions: + set: + description: Set graceful restart + type: bool + helper_disable: + description: Disable router's helper support level + type: bool + min_interval: + description: Minimum interval between Graceful Restarts + type: int + max_interval: + description: Maximum route lifetime following restart + type: int + hello_interval: + description: Specify Time between HELLO packets + type: int + ignore_mospf_type6_lsa: + description: Ignore MOSPF Type 6 LSA + type: bool + instance_id: + description: Specify instance ID + type: int + log_adjacency_changes: + description: Log adjacency state changes + type: dict + suboptions: + set: + description: Set log adjacency + type: bool + disable: + description: Disable log adjacency changes + type: bool + detail: + description: Log all state changes + type: bool + maximum: + description: Set OSPFv3 limits + type: dict + suboptions: + interfaces: + description: Specify limit for number of interfaces + type: int + paths: + description: Specify limit for number of paths + type: int + redistributed_prefixes: + description: Specify limit for number of redistributed prefixes + type: int + mpls_ldp_sync: + description: Enable/Disable MPLS LDP Sync + type: bool + mtu_ignore: + description: Enable/Disable ignoring of MTU in DBD packets + type: bool + network: + description: Specify Network type + type: str + choices: ["broadcast", "non-broadcast", "point-to-multipoint", "point-to-point"] + nsr: + description: Enable/Disable NSR for all VRFs in this process + type: bool + packet_size: + description: Specify limit size of OSPFv3 packets + type: int + passive: + description: Enable/Disable routing updates on an interface + type: bool + prefix_suppression: + description: Hide all transit addresses on this interface + type: bool + priority: + description: Specify Router priority + type: int + protocol_shutdown: + description: Gracefully shutdown the OSPFv3 protocol + type: bool + redistribute: + description: Redistribute information from another routing Protocol + type: dict + suboptions: + application: + description: Specify application routes + type: list + elements: dict + suboptions: + id: + description: OnePK Application name + type: str + required: true + set: + description: Set application route + type: bool + metric: + description: Specify metric for redistributed routes + type: int + metric_type: + description: Specify OSPFv3 exterior metric type for redistributed routes + type: int + route_policy: + description: Apply route policy to redistribution + type: str + tag: + description: Set tag for routes redistributed into OSPFv3 + type: int + bgp: + description: Specify bgp routes + type: list + elements: dict + suboptions: + id: + description: BGP process name + type: int + required: true + set: + description: Set bgp route number + type: bool + metric: + description: Specify metric for redistributed routes + type: int + metric_type: + description: Specify OSPFv3 exterior metric type for redistributed routes + type: int + preserved_med: + description: Specify preserve med of BGP routes + type: str + route_policy: + description: Apply route policy to redistribution + type: str + tag: + description: Set tag for routes redistributed into OSPFv3 + type: int + connected: + description: Specify connected routes + type: dict + suboptions: + set: + description: Set connected route + type: bool + metric: + description: Specify metric for redistributed routes + type: int + metric_type: + description: Specify OSPFv3 exterior metric type for redistributed routes + type: int + route_policy: + description: Apply route policy to redistribution + type: str + tag: + description: Set tag for routes redistributed into OSPFv3 + type: int + eigrp: + description: Specify eigrp routes + type: list + elements: dict + suboptions: + id: + description: EIGRP process name + type: int + required: true + set: + description: Set bgp route number + type: bool + match: + description: Redistribution of EIGRP routes + type: str + choices: ["external", "internal"] + metric: + description: Specify metric for redistributed routes + type: int + metric_type: + description: Specify OSPFv3 exterior metric type for redistributed routes + type: int + route_policy: + description: Apply route policy to redistribution + type: str + tag: + description: Set tag for routes redistributed into OSPFv3 + type: int + isis: + description: Specify IS-IS routes + type: list + elements: dict + suboptions: + id: + description: IS-IS name + type: str + required: true + set: + description: Set IS-IS route number + type: bool + level: + description: Specify IS-IS level routes + type: str + choices: ["level-1", "level-1-2", "level-2"] + metric: + description: Specify metric for redistributed routes + type: int + metric_type: + description: Specify OSPFv3 exterior metric type for redistributed routes + type: int + route_policy: + description: Apply route policy to redistribution + type: str + tag: + description: Set tag for routes redistributed into OSPFv3 + type: int + mobile: + description: Specify mobile routes + type: dict + suboptions: + set: + description: Set mobile route number + type: bool + metric: + description: Specify metric for redistributed routes + type: int + metric_type: + description: Specify OSPFv3 exterior metric type for redistributed routes + type: int + route_policy: + description: Apply route policy to redistribution + type: str + tag: + description: Set tag for routes redistributed into OSPFv3 + type: int + ospfv3: + description: Specify ospfv3 routes + type: list + elements: dict + suboptions: + id: + description: OSPFv3 process name + type: str + required: true + set: + description: Set ospfv3 route number + type: bool + match: + description: Redistribution of OSPFv3 routes + type: dict + suboptions: + external: + description: Redistribute OSPFv3 external routes + type: int + choices: ["1", "2"] + nssa_external: + description: Redistribute NSSA OSPFv3 external routes + type: int + choices: ["1", "2"] + internal: + description: Redistribute OSPFv3 internal routes + type: bool + metric: + description: Specify metric for redistributed routes + type: int + metric_type: + description: Specify OSPFv3 exterior metric type for redistributed routes + type: int + route_policy: + description: Apply route policy to redistribution + type: str + tag: + description: Set tag for routes redistributed into OSPFv3 + type: int + static: + description: Specify static routes + type: dict + suboptions: + set: + description: Set static route + type: bool + metric: + description: Specify metric for redistributed routes + type: int + metric_type: + description: Specify OSPFv3 exterior metric type for redistributed routes + type: int + route_policy: + description: Apply route policy to redistribution + type: str + tag: + description: Set tag for routes redistributed into OSPFv3 + type: int + subscriber: + description: Specify subscriber routes + type: dict + suboptions: + set: + description: Set static route + type: bool + metric: + description: Specify metric for redistributed routes + type: int + metric_type: + description: Specify OSPFv3 exterior metric type for redistributed routes + type: int + route_policy: + description: Apply route policy to redistribution + type: str + tag: + description: Set tag for routes redistributed into OSPFv3 + type: int + retransmit_interval: + description: Delay between LSA retransmissions + type: int + router_id: + description: ospfv3 router-id in IPv4 address format (A.B.C.D) + type: str + spf_prefix_priority: + description: Specify SPF configuration + type: dict + suboptions: + disable: + description: Disable SPF prefix priority + type: bool + route_policy: + description: Specify the route-policy to prioritize route install + type: list + elements: dict + suboptions: + name: + description: Specify name of the policy + type: str + value: + description: Specify parameter values for the policy () + type: str + stub_router: + description: Enter stub router configuration submode + type: dict + suboptions: + router_lsa: + description: Modify self originated router LSAs + type: dict + suboptions: + advertise_with: + description: Advertise LSAs with specified type + type: str + choices: ["max-metric", "r-bit", "v6-bit"] + always: + description: Force ospfv3 stub router mode unconditionally + type: bool + external_lsa: + description: Override External LSA metric in stub router mode + type: dict + suboptions: + set: + description: Set external lsa + type: bool + metric: + description: Metric to use while in stub router mode + type: int + include_stub: + description: Set maximum metric for stub links in stub router mode + type: bool + on_proc_migration: + description: Enter stub router mode on ospfv3 process migration + type: int + on_proc_restart: + description: Enter stub router mode on ospfv3 process restart + type: int + on_startup: + description: Enter stub router mode on startup + type: dict + suboptions: + time: + description: Time in seconds to stay in stub router mode + type: int + wait_for_bgp: + description: Exit stub router mode when BGP converges + type: bool + on_switchover: + description: Enter stub router mode on RP switchover + type: int + summary_lsa: + description: Override Summary LSA metric in stub router mode + type: dict + suboptions: + set: + description: Enable summary LSA + type: bool + metric: + description: Metric to use while in stub router mode + type: int + summary_prefix: + description: Configure IP address summaries + type: list + elements: dict + suboptions: + prefix: + description: IP summary address/mask (A.B.C.D/prefix) + type: str + required: True + not_advertise: + description: Suppress routes that match the specified prefix/mask pair + type: bool + tag: + description: Set tag + type: int + timers: + description: Adjust routing timers + type: dict + suboptions: + lsa_arrival: + description: Specify LSA arrival timers + type: int + pacing: + description: Specify pacing timers + type: dict + suboptions: + flood: + description: Flood pacing timer + type: int + lsa_group: + description: LSA group pacing timer + type: int + retransmission: + description: LSA group pacing timer + type: int + throttle: + description: Adjust throttle timers + type: dict + suboptions: + lsa: + description: Specify LSA throttle timers + type: dict + suboptions: + all_lsa_initial: + description: Delay to generate first occurrence of LSA in milliseconds + type: int + all_lsa_minimum: + description: Minimum delay between originating the same LSA in milliseconds + type: int + spf: + description: Specify SPF throttle timers + type: dict + suboptions: + spf_initial: + description: Delay to generate first occurrence of SPF in ms + type: int + spf_minimum: + description: Minimum delay between originating the same SPF in ms + type: int + trace: + description: Specify OSPF tracing options + type: dict + suboptions: + size: + description: Delete existing buffer and create one with N entries + type: str + value: + description: Specify trace entry + type: int + transmit_delay: + description: Estimated time needed to send link-state update packet + type: int + state: + description: + - The state the configuration should be left in + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:anton#show running-config router ospfv3 +# Thu Jun 11 15:54:44.569 UTC +# % No such configuration item(s) +# + +- name: Merge provided OSPFv3 configuration with the existing configuration + cisco.iosxr.iosxr_ospfv3: + config: + processes: + - process_id: 27 + areas: + - area_id: 10 + hello_interval: 2 + - process_id: 26 + authentication: + disable: true + - process_id: 10 + areas: + - area_id: 11 + default_cost: 5 + cost: 11 + - area_id: 22 + default_cost: 6 + - process_id: 30 + areas: + - area_id: 11 + default_cost: 5 + - area_id: 22 + default_cost: 6 + cost: 2 + default_metric: 10 + transmit_delay: 2 + hello_interval: 1 + dead_interval: 2 + retransmit_interval: 2 + packet_size: 577 + priority: 1 + router_id: '2.2.2.2' + demand_circuit: true + mtu_ignore: true + state: merged + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": {} +# +# "commands": [ +# "router ospfv3 10", +# "area 11 default-cost 5", +# "area 11 cost 11", +# "area 22 default-cost 6", +# "router ospfv3 26", +# "authentication disable", +# "router ospfv3 27", +# "area 10 hello-interval 2", +# "router ospfv3 30", +# "cost 2", +# "priority 1", +# "default-metric 10", +# "router-id 2.2.2.2", +# "demand-circuit", +# "packet-size 577", +# "transmit-delay 2", +# "dead-interval 2", +# "hello-interval 1", +# "retransmit-interval 2", +# "mtu-ignore", +# "area 11 default-cost 5", +# "area 22 default-cost 6" +# ] +# +# "after": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "11", +# "cost": 11, +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "process_id": "10" +# }, +# { +# "authentication": { +# "disable": true +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "hello_interval": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "areas": [ +# { +# "area_id": "11", +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "cost": 2, +# "dead_interval": 2, +# "default_metric": 10, +# "demand_circuit": true, +# "hello_interval": 1, +# "mtu_ignore": true, +# "packet_size": 577, +# "priority": 1, +# "process_id": "30", +# "retransmit_interval": 2, +# "router_id": "2.2.2.2", +# "transmit_delay": 2 +# } +# ] +# } +# +# +# ------------ +# After state +# ------------ +# +# RP/0/RP0/CPU0:anton#show running-config router ospfv3 +# router ospfv3 10 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospfv3 26 +# authentication disable +# ! +# router ospfv3 27 +# area 10 +# hello-interval 2 +# ! +# area 20 +# ! +# area 30 +# ! +# ! +# router ospfv3 30 +# cost 2 +# priority 1 +# mtu-ignore +# packet-size 577 +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit +# hello-interval 1 +# transmit-delay 2 +# router-id 2.2.2.2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# router ospfv3 10 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospfv3 26 +# authentication disable +# ! +# router ospfv3 27 +# area 10 +# hello-interval 2 +# ! +# area 20 +# ! +# area 30 +# ! +# ! +# router ospfv3 30 +# cost 2 +# priority 1 +# mtu-ignore +# packet-size 577 +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit +# hello-interval 1 +# transmit-delay 2 +# router-id 2.2.2.2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! + + + +# Using replaced +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/RP0/CPU0:anton#show running-config router ospf +# router ospfv3 10 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospfv3 26 +# authentication disable +# ! +# router ospfv3 27 +# area 10 +# hello-interval 2 +# ! +# area 20 +# ! +# area 30 +# ! +# ! +# router ospfv3 30 +# cost 2 +# priority 1 +# mtu-ignore +# packet-size 577 +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit +# hello-interval 1 +# transmit-delay 2 +# router-id 2.2.2.2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# router ospfv3 10 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospfv3 26 +# authentication disable +# ! +# router ospfv3 27 +# area 10 +# hello-interval 2 +# ! +# area 20 +# ! +# area 30 +# ! +# ! +# router ospfv3 30 +# cost 2 +# priority 1 +# mtu-ignore +# packet-size 577 +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit +# hello-interval 1 +# transmit-delay 2 +# router-id 2.2.2.2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! + +- name: Replace OSPFv3 routes configurations from the device + cisco.iosxr.iosxr_ospfv3: + config: + processes: + - process_id: 27 + areas: + - area_id: 10 + hello_interval: 2 + - area_id: 20 + cost: 2 + default_cost: 2 + - process_id: 26 + authentication: + disable: true + state: replaced + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "11", +# "cost": 11, +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "process_id": "10" +# }, +# { +# "authentication": { +# "disable": true +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "hello_interval": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "areas": [ +# { +# "area_id": "11", +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "cost": 2, +# "dead_interval": 2, +# "default_metric": 10, +# "demand_circuit": true, +# "hello_interval": 1, +# "mtu_ignore": true, +# "packet_size": 577, +# "priority": 1, +# "process_id": "30", +# "retransmit_interval": 2, +# "router_id": "2.2.2.2", +# "transmit_delay": 2 +# } +# ] +# } +# +# "commands": [ +# "router ospfv3 27", +# "area 20 default-cost 2", +# "area 20 cost 2" +# ] +# +# "after": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "11", +# "cost": 11, +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "process_id": "10" +# }, +# { +# "authentication": { +# "disable": true +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "hello_interval": 2 +# }, +# { +# "area_id": "20", +# "cost": 2, +# "default_cost": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "areas": [ +# { +# "area_id": "11", +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "cost": 2, +# "dead_interval": 2, +# "default_metric": 10, +# "demand_circuit": true, +# "hello_interval": 1, +# "mtu_ignore": true, +# "packet_size": 577, +# "priority": 1, +# "process_id": "30", +# "retransmit_interval": 2, +# "router_id": "2.2.2.2", +# "transmit_delay": 2 +# } +# ] +# } +# +# +# ----------- +# After state +# ----------- +# +# RP/0/RP0/CPU0:anton(config)#do show running-config router ospfv3 +# router ospfv3 10 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospfv3 26 +# authentication disable +# ! +# router ospfv3 27 +# area 10 +# hello-interval 2 +# ! +# area 20 +# cost 2 +# default-cost 2 +# ! +# area 30 +# ! +# ! +# router ospfv3 30 +# cost 2 +# priority 1 +# mtu-ignore +# packet-size 577 +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit +# hello-interval 1 +# transmit-delay 2 +# router-id 2.2.2.2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! + + +- name: Override existing OSPFv3 configurations from the device + cisco.iosxr.iosxr_ospfv3: + config: + processes: + - process_id: 27 + areas: + - area_id: 10 + hello_interval: 2 + authentication: + disable: true + - area_id: 20 + cost: 2 + default_cost: 2 + authentication: + disable: true + - process_id: 26 + areas: + - area_id: 10 + hello_interval: 2 + authentication: + disable: true + state: overridden + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "11", +# "cost": 11, +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "process_id": "10" +# }, +# { +# "authentication": { +# "disable": true +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "hello_interval": 2 +# }, +# { +# "area_id": "20", +# "cost": 2, +# "default_cost": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "areas": [ +# { +# "area_id": "11", +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "cost": 2, +# "dead_interval": 2, +# "default_metric": 10, +# "demand_circuit": true, +# "hello_interval": 1, +# "mtu_ignore": true, +# "packet_size": 577, +# "priority": 1, +# "process_id": "30", +# "retransmit_interval": 2, +# "router_id": "2.2.2.2", +# "transmit_delay": 2 +# } +# ] +# } +# +# "commands": [ +# "router ospfv3 10", +# "no area 11 default-cost 5", +# "no area 11 cost 11", +# "no area 22 default-cost 6", +# "router ospfv3 30", +# "no cost 2", +# "no priority 1", +# "no default-metric 10", +# "no router-id 2.2.2.2", +# "no demand-circuit", +# "no packet-size 577", +# "no transmit-delay 2", +# "no dead-interval 2", +# "no hello-interval 1", +# "no retransmit-interval 2", +# "no mtu-ignore", +# "no area 11 default-cost 5", +# "no area 22 default-cost 6", +# "router ospfv3 26", +# "area 10 hello-interval 4" +# ] +# +# "after": { +# "processes": [ +# { +# "process_id": "10" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "hello_interval": 4 +# } +# ], +# "authentication": { +# "disable": true +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "hello_interval": 2 +# }, +# { +# "area_id": "20", +# "cost": 2, +# "default_cost": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "process_id": "30" +# } +# ] +# } +# +# +# ----------- +# After state +# ----------- +# +# RP/0/RP0/CPU0:anton#show running-config router ospfv3 +# router ospfv3 10 +# area 11 +# ! +# area 22 +# ! +# ! +# router ospfv3 26 +# authentication disable +# area 10 +# hello-interval 4 +# ! +# ! +# router ospfv3 27 +# area 10 +# hello-interval 2 +# ! +# area 20 +# cost 2 +# default-cost 2 +# ! +# area 30 +# ! +# ! +# router ospfv3 30 +# area 11 +# ! +# area 22 +# ! +# ! + + + +# Using deleted +# +# ------------ +# Before state +# ------------ +# +# +# RP/0/RP0/CPU0:anton#show running-config router ospfv3 +# router ospfv3 10 +# area 11 +# ! +# area 22 +# ! +# ! +# router ospfv3 26 +# authentication disable +# area 10 +# hello-interval 4 +# ! +# ! +# router ospfv3 27 +# area 10 +# hello-interval 2 +# ! +# area 20 +# cost 2 +# default-cost 2 +# ! +# area 30 +# ! +# ! +# router ospfv3 30 +# area 11 +# ! +# area 22 +# ! +# ! + +- name: Deleted existing OSPFv3 configurations from the device + cisco.iosxr.iosxr_ospfv3: + config: + processes: + - process_id: '10' + - process_id: '26' + - process_id: '27' + - process_id: '30' + state: deleted + +# +# +# ------------------------ +# Module Execution Result +# ------------------------ +# +# "before": { +# "processes": [ +# { +# "process_id": "10" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "hello_interval": 4 +# } +# ], +# "authentication": { +# "disable": true +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "hello_interval": 2 +# }, +# { +# "area_id": "20", +# "cost": 2, +# "default_cost": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "process_id": "30" +# } +# ] +# }, +# +# "commands": [ +# "router ospfv3 26", +# "no authentication disable", +# "no area 10 hello-interval 4", +# "router ospfv3 27", +# "no area 10 hello-interval 2", +# "no area 20 default-cost 2", +# "no area 20 cost 2" +# ] +# +# "after": { +# "processes": [ +# { +# "process_id": "10" +# }, +# { +# "process_id": "26" +# }, +# { +# "process_id": "27" +# }, +# { +# "process_id": "30" +# } +# ] +# } +# +# +# ----------- +# After state +# ----------- +# +# RP/0/RP0/CPU0:anton(config)#show running-config router ospfv3 +# router ospfv3 10 +# ! +# router ospfv3 26 +# ! +# router ospfv3 27 +# ! +# router ospfv3 30 +# ! + + +# Using parsed +# parsed.cfg +# ------------ +# router ospfv3 10 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospfv3 26 +# authentication disable +# ! +# router ospfv3 27 +# area 10 +# hello-interval 2 +# ! +# ! +# router ospfv3 30 +# router-id 2.2.2.2 +# cost 2 +# packet-size 577 +# priority 1 +# mtu-ignore +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit +# hello-interval 1 +# transmit-delay 2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +- name: Parsed the device configuration to get output commands + cisco.iosxr.iosxr_ospfv3: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "11", +# "cost": 11, +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "process_id": "10" +# }, +# { +# "authentication": { +# "disable": true +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "hello_interval": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "areas": [ +# { +# "area_id": "11", +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "cost": 2, +# "dead_interval": 2, +# "default_metric": 10, +# "demand_circuit": true, +# "hello_interval": 1, +# "mtu_ignore": true, +# "packet_size": 577, +# "priority": 1, +# "process_id": "30", +# "retransmit_interval": 2, +# "router_id": "2.2.2.2", +# "transmit_delay": 2 +# } +# ] +# } +# +# Using rendered +# +# +- name: Render the commands for provided configuration + cisco.iosxr.iosxr_ospfv3: + config: + processes: + - process_id: 27 + areas: + - area_id: 10 + hello_interval: 2 + - process_id: 26 + authentication: + disable: true + - process_id: 10 + areas: + - area_id: 11 + default_cost: 5 + cost: 11 + - area_id: 22 + default_cost: 6 + - process_id: 30 + areas: + - area_id: 11 + default_cost: 5 + - area_id: 22 + default_cost: 6 + cost: 2 + default_metric: 10 + transmit_delay: 2 + hello_interval: 1 + dead_interval: 2 + retransmit_interval: 2 + packet_size: 577 + priority: 1 + router_id: '2.2.2.2' + demand_circuit: true + mtu_ignore: true + state: rendered + +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "rendered": [ +# "router ospfv3 27", +# "area 10 hello-interval 2", +# "router ospfv3 26", +# "authentication disable", +# "router ospfv3 10", +# "area 11 default-cost 5", +# "area 11 cost 11", +# "area 22 default-cost 6", +# "router ospfv3 30", +# "cost 2", +# "priority 1", +# "default-metric 10", +# "router-id 2.2.2.2", +# "demand-circuit", +# "packet-size 577", +# "transmit-delay 2", +# "dead-interval 2", +# "hello-interval 1", +# "retransmit-interval 2", +# "mtu-ignore", +# "area 11 default-cost 5", +# "area 22 default-cost 6" +# ] + + +# Using gathered +# +# Before state: +# ------------- +# +# RP/0/RP0/CPU0:anton#show running-config router ospf +# router ospfv3 10 +# area 11 +# cost 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! +# router ospfv3 26 +# authentication disable +# area 10 +# ! +# ! +# router ospfv3 27 +# area 10 +# hello-interval 2 +# ! +# area 20 +# ! +# area 30 +# ! +# ! +# router ospfv3 30 +# cost 2 +# priority 1 +# mtu-ignore +# packet-size 577 +# dead-interval 2 +# retransmit-interval 2 +# demand-circuit +# hello-interval 1 +# transmit-delay 2 +# router-id 2.2.2.2 +# default-metric 10 +# area 11 +# default-cost 5 +# ! +# area 22 +# default-cost 6 +# ! +# ! + +- name: Gather ospfv3 routes configuration + cisco.iosxr.iosxr_ospfv3: + state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "gathered": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "11", +# "cost": 11, +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "process_id": "10" +# }, +# { +# "authentication": { +# "disable": true +# }, +# "process_id": "26" +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "hello_interval": 2 +# } +# ], +# "process_id": "27" +# }, +# { +# "areas": [ +# { +# "area_id": "11", +# "default_cost": 5 +# }, +# { +# "area_id": "22", +# "default_cost": 6 +# } +# ], +# "cost": 2, +# "dead_interval": 2, +# "default_metric": 10, +# "demand_circuit": true, +# "hello_interval": 1, +# "mtu_ignore": true, +# "packet_size": 577, +# "priority": 1, +# "process_id": "30", +# "retransmit_interval": 2, +# "router_id": "2.2.2.2", +# "transmit_delay": 2 +# } +# ] +# } +# +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.ospfv3.ospfv3 import ( + Ospfv3Args, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.config.ospfv3.ospfv3 import ( + Ospfv3, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Ospfv3Args.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Ospfv3(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_static_routes.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_static_routes.py new file mode 100644 index 00000000..238582d2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_static_routes.py @@ -0,0 +1,854 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for iosxr_static_routes +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +module: iosxr_static_routes +short_description: Static routes resource module +description: +- This module manages static routes on devices running Cisco IOS-XR. +version_added: 1.0.0 +author: Nilashish Chakraborty (@NilashishC) +options: + 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-XR device + by executing the command B(show running-config router static). + - 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 + config: + description: A dictionary of static route options. + type: list + elements: dict + suboptions: + vrf: + description: + - The VRF to which the static route(s) belong. + type: str + address_families: + description: A dictionary specifying the address family to which the static + route(s) belong. + type: list + elements: dict + suboptions: + afi: + description: + - Specifies the top level address family indicator. + type: str + choices: + - ipv4 + - ipv6 + required: true + safi: + description: + - Specifies the subsequent address family indicator. + type: str + choices: + - unicast + - multicast + required: true + routes: + description: A dictionary that specifies the static route configurations. + elements: dict + type: list + suboptions: + dest: + description: + - An IPv4 or IPv6 address in CIDR notation that specifies the destination + network for the static route. + type: str + required: true + next_hops: + description: + - Next hops to the specified destination. + type: list + elements: dict + suboptions: + forward_router_address: + description: + - The IP address of the next hop that can be used to reach the + destination network. + type: str + interface: + description: + - The interface to use to reach the destination. + type: str + dest_vrf: + description: + - The destination VRF. + type: str + admin_distance: + description: + - The administrative distance for this static route. + - Refer to vendor documentation for valid values. + type: int + metric: + description: + - Specifes the metric for this static route. + - Refer to vendor documentation for valid values. + type: int + description: + description: + - Specifies the description for this static route. + type: str + vrflabel: + description: + - Specifies the VRF label for this static route. + - Refer to vendor documentation for valid values. + type: int + tag: + description: + - Specifies a numeric tag for this static route. + - Refer to vendor documentation for valid values. + type: int + track: + description: + - Specifies the object to be tracked. + - This enables object tracking for static routes. + type: str + tunnel_id: + description: + - Specifies a tunnel id for the route. + - Refer to vendor documentation for valid values. + type: int + state: + description: + - The state the configuration should be left in. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged + +""" +EXAMPLES = """ + +# Using merged + +# Before state +# ------------- +# RP/0/RP0/CPU0:ios#show running-config router static +# Sat Feb 22 07:46:30.089 UTC +# % No such configuration item(s) +# +- name: Merge the provided configuration with the exisiting running configuration + cisco.iosxr.iosxr_static_routes: + config: + - address_families: + - afi: ipv4 + safi: unicast + routes: + - dest: 192.0.2.16/28 + next_hops: + - forward_router_address: 192.0.2.10 + interface: FastEthernet0/0/0/1 + description: LAB + metric: 120 + tag: 10 + + - interface: FastEthernet0/0/0/5 + track: ip_sla_1 + + - dest: 192.0.2.32/28 + next_hops: + - forward_router_address: 192.0.2.11 + admin_distance: 100 + + - afi: ipv6 + safi: unicast + routes: + - dest: 2001:db8:1000::/36 + next_hops: + - interface: FastEthernet0/0/0/7 + description: DC + + - interface: FastEthernet0/0/0/8 + forward_router_address: 2001:db8:2000:2::1 + + - vrf: DEV_SITE + address_families: + - afi: ipv4 + safi: unicast + routes: + - dest: 192.0.2.48/28 + next_hops: + - forward_router_address: 192.0.2.12 + description: DEV + dest_vrf: test_1 + + - dest: 192.0.2.80/28 + next_hops: + - interface: FastEthernet0/0/0/2 + forward_router_address: 192.0.2.14 + dest_vrf: test_1 + track: ip_sla_2 + vrflabel: 124 + state: merged + +# After state +# ------------- +# RP/0/RP0/CPU0:ios#show running-config router static +# Sat Feb 22 07:49:11.754 UTC +# router static +# address-family ipv4 unicast +# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120 +# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1 +# 192.0.2.32/28 192.0.2.11 100 +# ! +# address-family ipv6 unicast +# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC +# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1 +# ! +# vrf DEV_SITE +# address-family ipv4 unicast +# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV +# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2 +# ! +# ! +# ! + +# Using merged to update existing static routes + +# Before state +# ------------- +# RP/0/RP0/CPU0:ios#show running-config router static +# Sat Feb 22 07:49:11.754 UTC +# router static +# address-family ipv4 unicast +# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120 +# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1 +# 192.0.2.32/28 192.0.2.11 100 +# ! +# address-family ipv6 unicast +# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC +# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1 +# ! +# vrf DEV_SITE +# address-family ipv4 unicast +# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV +# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2 +# ! +# ! +# ! + +- name: Update existing static routes configuration using merged + cisco.iosxr.iosxr_static_routes: + config: + - vrf: DEV_SITE + address_families: + - afi: ipv4 + safi: unicast + routes: + - dest: 192.0.2.48/28 + next_hops: + - forward_router_address: 192.0.2.12 + vrflabel: 2301 + dest_vrf: test_1 + + - dest: 192.0.2.80/28 + next_hops: + - interface: FastEthernet0/0/0/2 + forward_router_address: 192.0.2.14 + dest_vrf: test_1 + description: rt_test_1 + state: merged + +# After state +# ------------- +# RP/0/RP0/CPU0:ios#show running-config router static +# Sat Feb 22 07:49:11.754 UTC +# router static +# address-family ipv4 unicast +# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120 +# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1 +# 192.0.2.32/28 192.0.2.11 100 +# ! +# address-family ipv6 unicast +# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC +# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1 +# ! +# vrf DEV_SITE +# address-family ipv4 unicast +# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV vrflabel 2301 +# 192.0.2.80/28 vrf test_1 192.0.2.14 FastEthernet0/0/0/2 description rt_test_1 track ip_sla_2 vrflabel 124 +# ! +# ! +# ! + +# Using replaced to replace all next hop entries for a single destination network + +# Before state +# -------------- + +# RP/0/RP0/CPU0:ios#sh running-config router static +# Sat Feb 22 07:59:08.669 UTC +# router static +# address-family ipv4 unicast +# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120 +# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1 +# 192.0.2.32/28 192.0.2.11 100 +# ! +# address-family ipv6 unicast +# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC +# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1 +# ! +# vrf DEV_SITE +# address-family ipv4 unicast +# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV +# 192.0.2.48/28 GigabitEthernet0/0/0/1 192.0.3.24 vrflabel 2302 +# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2 +# ! +# ! +# ! + +- name: Replace device configurations of static routes with provided configurations + cisco.iosxr.iosxr_static_routes: + config: + - vrf: DEV_SITE + address_families: + - afi: ipv4 + safi: unicast + routes: + - dest: 192.0.2.48/28 + next_hops: + - forward_router_address: 192.0.2.15 + interface: FastEthernet0/0/0/3 + description: DEV_NEW + dest_vrf: dev_test_2 + state: replaced + +# After state +# ------------ +# RP/0/RP0/CPU0:ios#sh running-config router static +# Sat Feb 22 08:04:07.085 UTC +# router static +# address-family ipv4 unicast +# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120 +# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1 +# 192.0.2.32/28 192.0.2.11 100 +# ! +# address-family ipv6 unicast +# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC +# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1 +# ! +# vrf DEV_SITE +# address-family ipv4 unicast +# 192.0.2.48/28 vrf dev_test_2 FastEthernet0/0/0/3 192.0.2.15 description DEV_NEW +# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2 +# ! +# ! +# ! + +# Using overridden to override all static route entries on the device + +# Before state +# ------------- +# RP/0/RP0/CPU0:ios#sh running-config router static +# Sat Feb 22 07:59:08.669 UTC +# router static +# address-family ipv4 unicast +# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120 +# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1 +# 192.0.2.32/28 192.0.2.11 100 +# ! +# address-family ipv6 unicast +# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC +# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1 +# ! +# vrf DEV_SITE +# address-family ipv4 unicast +# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV +# 192.0.2.48/28 GigabitEthernet0/0/0/1 192.0.3.24 vrflabel 2302 +# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2 +# ! +# ! +# ! + +- name: Overridde all static routes configuration with provided configuration + cisco.iosxr.iosxr_static_routes: + config: + - vrf: DEV_NEW + address_families: + - afi: ipv4 + safi: unicast + routes: + - dest: 192.0.2.48/28 + next_hops: + - forward_router_address: 192.0.2.15 + interface: FastEthernet0/0/0/3 + description: DEV1 + - afi: ipv6 + safi: unicast + routes: + - dest: 2001:db8:3000::/36 + next_hops: + - interface: FastEthernet0/0/0/4 + forward_router_address: 2001:db8:2000:2::2 + description: PROD1 + track: ip_sla_1 + state: overridden + +# After state +# ------------- +# RP/0/RP0/CPU0:ios#sh running-config router static +# Sat Feb 22 08:07:41.516 UTC +# router static +# vrf DEV_NEW +# address-family ipv4 unicast +# 192.0.2.48/28 FastEthernet0/0/0/3 192.0.2.15 description DEV1 +# ! +# address-family ipv6 unicast +# 2001:db8:3000::/36 FastEthernet0/0/0/4 2001:db8:2000:2::2 description PROD1 track ip_sla_1 +# ! +# ! +# ! + +# Using deleted to delete all destination network entries under a single AFI + +# Before state +# ------------- +# RP/0/RP0/CPU0:ios#sh running-config router static +# Sat Feb 22 07:59:08.669 UTC +# router static +# address-family ipv4 unicast +# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120 +# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1 +# 192.0.2.32/28 192.0.2.11 100 +# ! +# address-family ipv6 unicast +# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC +# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1 +# ! +# vrf DEV_SITE +# address-family ipv4 unicast +# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV +# 192.0.2.48/28 GigabitEthernet0/0/0/1 192.0.3.24 vrflabel 2302 +# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2 +# ! +# ! +# ! + +- name: Delete all destination network entries under a single AFI + cisco.iosxr.iosxr_static_routes: + config: + - vrf: DEV_SITE + address_families: + - afi: ipv4 + safi: unicast + state: deleted + +# After state +# ------------ + +# RP/0/RP0/CPU0:ios#sh running-config router static +# Sat Feb 22 08:16:41.464 UTC +# router static +# address-family ipv4 unicast +# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120 +# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1 +# 192.0.2.32/28 192.0.2.11 100 +# ! +# address-family ipv6 unicast +# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC +# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1 +# ! +# vrf DEV_SITE +# ! +# ! + +# Using deleted to remove all static route entries from the device + +# Before state +# ------------- +# RP/0/RP0/CPU0:ios#sh running-config router static +# Sat Feb 22 07:59:08.669 UTC +# router static +# address-family ipv4 unicast +# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120 +# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1 +# 192.0.2.32/28 192.0.2.11 100 +# ! +# address-family ipv6 unicast +# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC +# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1 +# ! +# vrf DEV_SITE +# address-family ipv4 unicast +# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV +# 192.0.2.48/28 GigabitEthernet0/0/0/1 192.0.3.24 vrflabel 2302 +# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2 +# ! +# ! +# ! + +- name: Delete static routes configuration + cisco.iosxr.iosxr_static_routes: + state: deleted + +# After state +# ------------ +# RP/0/RP0/CPU0:ios#sh running-config router static +# Sat Feb 22 08:50:43.038 UTC +# % No such configuration item(s) + +# Using gathered to gather static route facts from the device + +- name: Gather static routes facts from the device using iosxr_static_routes module + cisco.iosxr.iosxr_static_routes: + state: gathered + +# Task output (redacted) +# ----------------------- +# "gathered": [ +# { +# "address_families": [ +# { +# "afi": "ipv4", +# "routes": [ +# { +# "dest": "192.0.2.16/28", +# "next_hops": [ +# { +# "description": "LAB", +# "forward_router_address": "192.0.2.10", +# "interface": "FastEthernet0/0/0/1", +# "metric": 120, +# "tag": 10 +# }, +# { +# "interface": "FastEthernet0/0/0/5", +# "track": "ip_sla_1" +# } +# ] +# }, +# { +# "dest": "192.0.2.32/28", +# "next_hops": [ +# { +# "admin_distance": 100, +# "forward_router_address": "192.0.2.11" +# } +# ] +# } +# ], +# "safi": "unicast" +# }, +# { +# "afi": "ipv6", +# "routes": [ +# { +# "dest": "2001:db8:1000::/36", +# "next_hops": [ +# { +# "description": "DC", +# "interface": "FastEthernet0/0/0/7" +# }, +# { +# "forward_router_address": "2001:db8:2000:2::1", +# "interface": "FastEthernet0/0/0/8" +# } +# ] +# } +# ], +# "safi": "unicast" +# } +# ] +# }, +# { +# "address_families": [ +# { +# "afi": "ipv4", +# "routes": [ +# { +# "dest": "192.0.2.48/28", +# "next_hops": [ +# { +# "description": "DEV", +# "dest_vrf": "test_1", +# "forward_router_address": "192.0.2.12" +# }, +# { +# "forward_router_address": "192.0.3.24", +# "interface": "GigabitEthernet0/0/0/1", +# "vrflabel": 2302 +# } +# ] +# }, +# { +# "dest": "192.0.2.80/28", +# "next_hops": [ +# { +# "dest_vrf": "test_1", +# "forward_router_address": "192.0.2.14", +# "interface": "FastEthernet0/0/0/2", +# "track": "ip_sla_2", +# "vrflabel": 124 +# } +# ] +# } +# ], +# "safi": "unicast" +# } +# ], +# "vrf": "DEV_SITE" +# } +# ] + +# Using rendered + +- name: Render platform specific commands (without connecting to the device) + cisco.iosxr.iosxr_static_routes: + config: + - vrf: DEV_SITE + address_families: + - afi: ipv4 + safi: unicast + routes: + - dest: 192.0.2.48/28 + next_hops: + - forward_router_address: 192.0.2.12 + description: DEV + dest_vrf: test_1 + + - dest: 192.0.2.80/28 + next_hops: + - interface: FastEthernet0/0/0/2 + forward_router_address: 192.0.2.14 + dest_vrf: test_1 + track: ip_sla_2 + vrflabel: 124 + +# Task Output (redacted) +# ----------------------- +# "rendered": [ +# "router static"s, +# "vrf DEV_SITE", +# "address-family ipv4 unicast", +# "192.0.2.48/28 vrf test_1 192.0.2.12 description DEV", +# "192.0.2.80/28 vrf test_1 192.0.2.14 FastEthernet0/0/0/2 track ip_sla_2 vrflabel 124" + +# Using parsed + +# parsed.cfg +# ------------ +# Fri Nov 29 21:10:41.896 UTC +# router static +# address-family ipv4 unicast +# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120 +# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1 +# 192.0.2.32/28 192.0.2.11 100 +# ! +# address-family ipv6 unicast +# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC +# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1 +# ! +# vrf DEV_SITE +# address-family ipv4 unicast +# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV +# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2 +# ! +# ! +# ! + +- name: Use parsed state to convert externally supplied device specific static routes + commands to structured format + cisco.iosxr.iosxr_static_routes: + running_config: "{{ lookup('file', '../../fixtures/parsed.cfg') }}" + state: parsed + +# Task output (redacted) +# ----------------------- +# "parsed": [ +# { +# "address_families": [ +# { +# "afi": "ipv4", +# "routes": [ +# { +# "dest": "192.0.2.16/28", +# "next_hops": [ +# { +# "description": "LAB", +# "forward_router_address": "192.0.2.10", +# "interface": "FastEthernet0/0/0/1", +# "metric": 120, +# "tag": 10 +# }, +# { +# "interface": "FastEthernet0/0/0/5", +# "track": "ip_sla_1" +# } +# ] +# }, +# { +# "dest": "192.0.2.32/28", +# "next_hops": [ +# { +# "admin_distance": 100, +# "forward_router_address": "192.0.2.11" +# } +# ] +# } +# ], +# "safi": "unicast" +# }, +# { +# "afi": "ipv6", +# "routes": [ +# { +# "dest": "2001:db8:1000::/36", +# "next_hops": [ +# { +# "description": "DC", +# "interface": "FastEthernet0/0/0/7" +# }, +# { +# "forward_router_address": "2001:db8:2000:2::1", +# "interface": "FastEthernet0/0/0/8" +# } +# ] +# } +# ], +# "safi": "unicast" +# } +# ] +# }, +# { +# "address_families": [ +# { +# "afi": "ipv4", +# "routes": [ +# { +# "dest": "192.0.2.48/28", +# "next_hops": [ +# { +# "description": "DEV", +# "dest_vrf": "test_1", +# "forward_router_address": "192.0.2.12" +# } +# ] +# }, +# { +# "dest": "192.0.2.80/28", +# "next_hops": [ +# { +# "dest_vrf": "test_1", +# "forward_router_address": "192.0.2.14", +# "interface": "FastEthernet0/0/0/2", +# "track": "ip_sla_2", +# "vrflabel": 124 +# } +# ] +# } +# ], +# "safi": "unicast" +# } +# ], +# "vrf": "DEV_SITE" +# } +# ] +# } +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +after: + description: The resulting configuration model invocation. + returned: when changed + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: + - router static + - vrf dev_site + - address-family ipv4 unicast + - 192.0.2.48/28 192.0.2.12 FastEthernet0/0/0/1 track ip_sla_10 description dev1 + - address-family ipv6 unicast + - no 2001:db8:1000::/36 + - 2001:db8:3000::/36 2001:db8:2000:2::2 FastEthernet0/0/0/4 track ip_sla_11 description prod1 +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.argspec.static_routes.static_routes import ( + Static_routesArgs, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.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", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=Static_routesArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + + result = Static_routes(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_system.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_system.py new file mode 100644 index 00000000..4e9ccc6f --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_system.py @@ -0,0 +1,943 @@ +#!/usr/bin/python +# +# 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 + + +DOCUMENTATION = """ +module: iosxr_system +author: +- Peter Sprygada (@privateip) +- Kedar Kekan (@kedarX) +short_description: Manage the system attributes on Cisco IOS XR devices +description: +- This module provides declarative management of node system attributes on Cisco IOS + XR devices. It provides an option to configure host system parameters or remove + those parameters from the device active configuration. +version_added: 1.0.0 +requirements: +- ncclient >= 0.5.3 when using netconf +- lxml >= 4.1.1 when using netconf +extends_documentation_fragment: +- cisco.iosxr.iosxr +notes: +- This module works with connection C(network_cli) and C(netconf). See L(the IOS-XR + Platform Options,../network/user_guide/platform_iosxr.html). +- Tested against IOS XRv 6.1.3 +- name-servers I(state=absent) operation with C(netconf) transport is a success, but + with rpc-error. This is due to XR platform issue. Recommended to use I(ignore_errors) + option with the task as a workaround. +options: + hostname: + description: + - Configure the device hostname parameter. This option takes an ASCII string value. + type: str + vrf: + description: + - VRF name for domain services + type: str + default: "default" + 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: str + 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: str + lookup_source: + description: + - The C(lookup_source) argument 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: + - Provides 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 + default: true + name_servers: + description: + - The C(name_serves) argument accepts a list of DNS name servers by way of either + FQDN or IP address to use to perform name resolution lookups. This argument + accepts wither a list of DNS servers See examples. + type: list + elements: str + 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 (default vrf=default) + cisco.iosxr.iosxr_system: + hostname: iosxr01 + domain_name: test.example.com + domain_search: + - ansible.com + - redhat.com + - cisco.com +- name: remove configuration + cisco.iosxr.iosxr_system: + hostname: iosxr01 + domain_name: test.example.com + domain_search: + - ansible.com + - redhat.com + - cisco.com + state: absent +- name: configure hostname and domain-name with vrf + cisco.iosxr.iosxr_system: + hostname: iosxr01 + vrf: nondefault + domain_name: test.example.com + domain_search: + - ansible.com + - redhat.com + - cisco.com +- name: configure DNS lookup sources + cisco.iosxr.iosxr_system: + lookup_source: MgmtEth0/0/CPU0/0 + lookup_enabled: true +- name: configure name servers + cisco.iosxr.iosxr_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 iosxr01 + - ip domain-name test.example.com +xml: + description: NetConf rpc xml sent to device with transport C(netconf) + returned: always (empty list when no xml rpc to send) + type: list + sample: + - '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <ip-domain xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ip-domain-cfg"> + <vrfs> + <vrf> + <vrf-name>default</vrf-name> + <lists> + <list xc:operation="merge"> + <order>0</order> + <list-name>redhat.com</list-name> + </list> + </lists> + </vrf> + </vrfs> + </ip-domain> + </config>' +""" + +import re +import collections + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + get_config, + load_config, + etree_findall, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + is_cliconf, + is_netconf, + etree_find, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + iosxr_argument_spec, + build_xml, +) + + +def diff_list(want, have): + adds = set(want).difference(have) + removes = set(have).difference(want) + return (adds, removes) + + +class ConfigBase(object): + def __init__(self, module): + self._module = module + self._result = {"changed": False, "warnings": []} + self._want = dict() + self._have = dict() + + def map_params_to_obj(self): + self._want.update( + { + "hostname": self._module.params["hostname"], + "vrf": self._module.params["vrf"], + "domain_name": self._module.params["domain_name"], + "domain_search": self._module.params["domain_search"], + "lookup_source": self._module.params["lookup_source"], + "lookup_enabled": self._module.params["lookup_enabled"], + "name_servers": self._module.params["name_servers"], + } + ) + + +class CliConfiguration(ConfigBase): + def __init__(self, module): + super(CliConfiguration, self).__init__(module) + + def map_obj_to_commands(self): + commands = list() + state = self._module.params["state"] + + def needs_update(x): + return self._want.get(x) and ( + self._want.get(x) != self._have.get(x) + ) + + if state == "absent": + if self._have["hostname"] != "ios": + commands.append("no hostname") + if self._have["domain_name"]: + commands.append("no domain name") + if self._have["lookup_source"]: + commands.append( + "no domain lookup source-interface {0!s}".format( + self._have["lookup_source"] + ) + ) + if not self._have["lookup_enabled"]: + commands.append("no domain lookup disable") + for item in self._have["name_servers"]: + commands.append("no domain name-server {0!s}".format(item)) + for item in self._have["domain_search"]: + commands.append("no domain list {0!s}".format(item)) + + elif state == "present": + if needs_update("hostname"): + commands.append( + "hostname {0!s}".format(self._want["hostname"]) + ) + + if needs_update("domain_name"): + commands.append( + "domain name {0!s}".format(self._want["domain_name"]) + ) + + if needs_update("lookup_source"): + commands.append( + "domain lookup source-interface {0!s}".format( + self._want["lookup_source"] + ) + ) + + cmd = None + if ( + not self._want["lookup_enabled"] + and self._have["lookup_enabled"] + ): + cmd = "domain lookup disable" + elif ( + self._want["lookup_enabled"] + and not self._have["lookup_enabled"] + ): + cmd = "no domain lookup disable" + if cmd is not None: + commands.append(cmd) + + if self._want["name_servers"] is not None: + adds, removes = diff_list( + self._want["name_servers"], self._have["name_servers"] + ) + for item in adds: + commands.append("domain name-server {0!s}".format(item)) + for item in removes: + commands.append("no domain name-server {0!s}".format(item)) + + if self._want["domain_search"] is not None: + adds, removes = diff_list( + self._want["domain_search"], self._have["domain_search"] + ) + for item in adds: + commands.append("domain list {0!s}".format(item)) + for item in removes: + commands.append("no domain list {0!s}".format(item)) + + self._result["commands"] = [] + if commands: + commit = not self._module.check_mode + diff = load_config(self._module, commands, commit=commit) + if diff: + self._result["diff"] = dict(prepared=diff) + + self._result["commands"] = commands + self._result["changed"] = True + + def parse_hostname(self, config): + match = re.search(r"^hostname (\S+)", config, re.M) + if match: + return match.group(1) + + def parse_domain_name(self, config): + match = re.search(r"^domain name (\S+)", config, re.M) + if match: + return match.group(1) + + def parse_lookup_source(self, config): + match = re.search( + r"^domain lookup source-interface (\S+)", config, re.M + ) + if match: + return match.group(1) + + def map_config_to_obj(self): + config = get_config(self._module) + self._have.update( + { + "hostname": self.parse_hostname(config), + "domain_name": self.parse_domain_name(config), + "domain_search": re.findall( + r"^domain list (\S+)", config, re.M + ), + "lookup_source": self.parse_lookup_source(config), + "lookup_enabled": "domain lookup disable" not in config, + "name_servers": re.findall( + r"^domain name-server (\S+)", config, re.M + ), + } + ) + + def run(self): + self.map_params_to_obj() + self.map_config_to_obj() + self.map_obj_to_commands() + + return self._result + + +class NCConfiguration(ConfigBase): + def __init__(self, module): + super(NCConfiguration, self).__init__(module) + self._system_meta = collections.OrderedDict() + self._system_domain_meta = collections.OrderedDict() + self._system_server_meta = collections.OrderedDict() + self._hostname_meta = collections.OrderedDict() + self._lookup_source_meta = collections.OrderedDict() + self._lookup_meta = collections.OrderedDict() + + def map_obj_to_xml_rpc(self): + self._system_meta.update( + [ + ( + "vrfs", + { + "xpath": "ip-domain/vrfs", + "tag": True, + "operation": "edit", + }, + ), + ( + "vrf", + { + "xpath": "ip-domain/vrfs/vrf", + "tag": True, + "operation": "edit", + }, + ), + ( + "a:vrf", + { + "xpath": "ip-domain/vrfs/vrf/vrf-name", + "operation": "edit", + }, + ), + ( + "a:domain_name", + { + "xpath": "ip-domain/vrfs/vrf/name", + "operation": "edit", + "attrib": "operation", + }, + ), + ] + ) + + self._system_domain_meta.update( + [ + ( + "vrfs", + { + "xpath": "ip-domain/vrfs", + "tag": True, + "operation": "edit", + }, + ), + ( + "vrf", + { + "xpath": "ip-domain/vrfs/vrf", + "tag": True, + "operation": "edit", + }, + ), + ( + "a:vrf", + { + "xpath": "ip-domain/vrfs/vrf/vrf-name", + "operation": "edit", + }, + ), + ( + "lists", + { + "xpath": "ip-domain/vrfs/vrf/lists", + "tag": True, + "operation": "edit", + }, + ), + ( + "list", + { + "xpath": "ip-domain/vrfs/vrf/lists/list", + "tag": True, + "operation": "edit", + "attrib": "operation", + }, + ), + ( + "a:order", + { + "xpath": "ip-domain/vrfs/vrf/lists/list/order", + "operation": "edit", + }, + ), + ( + "a:domain_search", + { + "xpath": "ip-domain/vrfs/vrf/lists/list/list-name", + "operation": "edit", + }, + ), + ] + ) + + self._system_server_meta.update( + [ + ( + "vrfs", + { + "xpath": "ip-domain/vrfs", + "tag": True, + "operation": "edit", + }, + ), + ( + "vrf", + { + "xpath": "ip-domain/vrfs/vrf", + "tag": True, + "operation": "edit", + }, + ), + ( + "a:vrf", + { + "xpath": "ip-domain/vrfs/vrf/vrf-name", + "operation": "edit", + }, + ), + ( + "servers", + { + "xpath": "ip-domain/vrfs/vrf/servers", + "tag": True, + "operation": "edit", + }, + ), + ( + "server", + { + "xpath": "ip-domain/vrfs/vrf/servers/server", + "tag": True, + "operation": "edit", + "attrib": "operation", + }, + ), + ( + "a:order", + { + "xpath": "ip-domain/vrfs/vrf/servers/server/order", + "operation": "edit", + }, + ), + ( + "a:name_servers", + { + "xpath": "ip-domain/vrfs/vrf/servers/server/server-address", + "operation": "edit", + }, + ), + ] + ) + + self._hostname_meta.update( + [ + ( + "a:hostname", + { + "xpath": "host-names/host-name", + "operation": "edit", + "attrib": "operation", + }, + ) + ] + ) + + self._lookup_source_meta.update( + [ + ( + "vrfs", + { + "xpath": "ip-domain/vrfs", + "tag": True, + "operation": "edit", + }, + ), + ( + "vrf", + { + "xpath": "ip-domain/vrfs/vrf", + "tag": True, + "operation": "edit", + }, + ), + ( + "a:vrf", + { + "xpath": "ip-domain/vrfs/vrf/vrf-name", + "operation": "edit", + }, + ), + ( + "a:lookup_source", + { + "xpath": "ip-domain/vrfs/vrf/source-interface", + "operation": "edit", + "attrib": "operation", + }, + ), + ] + ) + + self._lookup_meta.update( + [ + ( + "vrfs", + { + "xpath": "ip-domain/vrfs", + "tag": True, + "operation": "edit", + }, + ), + ( + "vrf", + { + "xpath": "ip-domain/vrfs/vrf", + "tag": True, + "operation": "edit", + }, + ), + ( + "a:vrf", + { + "xpath": "ip-domain/vrfs/vrf/vrf-name", + "operation": "edit", + }, + ), + ( + "lookup", + { + "xpath": "ip-domain/vrfs/vrf/lookup", + "tag": True, + "operation": "edit", + "attrib": "operation", + }, + ), + ] + ) + + state = self._module.params["state"] + _get_filter = build_xml("ip-domain", opcode="filter") + running = get_config( + self._module, source="running", config_filter=_get_filter + ) + _get_filter = build_xml("host-names", opcode="filter") + hostname_runn = get_config( + self._module, source="running", config_filter=_get_filter + ) + + hostname_ele = etree_find(hostname_runn, "host-name") + hostname = hostname_ele.text if hostname_ele is not None else None + + vrf_ele = etree_findall(running, "vrf") + vrf_map = {} + for vrf in vrf_ele: + name_server_list = list() + domain_list = list() + vrf_name_ele = etree_find(vrf, "vrf-name") + vrf_name = vrf_name_ele.text if vrf_name_ele is not None else None + + domain_name_ele = etree_find(vrf, "name") + domain_name = ( + domain_name_ele.text if domain_name_ele is not None else None + ) + + domain_ele = etree_findall(vrf, "list-name") + for domain in domain_ele: + domain_list.append(domain.text) + + server_ele = etree_findall(vrf, "server-address") + for server in server_ele: + name_server_list.append(server.text) + + lookup_source_ele = etree_find(vrf, "source-interface") + lookup_source = ( + lookup_source_ele.text + if lookup_source_ele is not None + else None + ) + + lookup_enabled = ( + False if etree_find(vrf, "lookup") is not None else True + ) + + vrf_map[vrf_name] = { + "domain_name": domain_name, + "domain_search": domain_list, + "name_servers": name_server_list, + "lookup_source": lookup_source, + "lookup_enabled": lookup_enabled, + } + + opcode = None + hostname_param = {} + lookup_param = {} + system_param = {} + sys_server_params = list() + sys_domain_params = list() + add_domain_params = list() + del_domain_params = list() + add_server_params = list() + del_server_params = list() + lookup_source_params = {} + + try: + sys_node = vrf_map[self._want["vrf"]] + except KeyError: + sys_node = { + "domain_name": None, + "domain_search": [], + "name_servers": [], + "lookup_source": None, + "lookup_enabled": True, + } + + if state == "absent": + opcode = "delete" + + def needs_update(x): + return ( + self._want[x] is not None and self._want[x] == sys_node[x] + ) + + if needs_update("domain_name"): + system_param = { + "vrf": self._want["vrf"], + "domain_name": self._want["domain_name"], + } + + if needs_update("hostname"): + hostname_param = {"hostname": hostname} + + if ( + not self._want["lookup_enabled"] + and not sys_node["lookup_enabled"] + ): + lookup_param["vrf"] = self._want["vrf"] + + if needs_update("lookup_source"): + lookup_source_params["vrf"] = self._want["vrf"] + lookup_source_params["lookup_source"] = self._want[ + "lookup_source" + ] + + if self._want["domain_search"]: + domain_param = {} + domain_param["domain_name"] = self._want["domain_name"] + domain_param["vrf"] = self._want["vrf"] + domain_param["order"] = "0" + for domain in self._want["domain_search"]: + if domain in sys_node["domain_search"]: + domain_param["domain_search"] = domain + sys_domain_params.append(domain_param.copy()) + + if self._want["name_servers"]: + server_param = {} + server_param["vrf"] = self._want["vrf"] + server_param["order"] = "0" + for server in self._want["name_servers"]: + if server in sys_node["name_servers"]: + server_param["name_servers"] = server + sys_server_params.append(server_param.copy()) + + elif state == "present": + opcode = "merge" + + def needs_update(x): + return self._want[x] is not None and ( + sys_node[x] is None + or ( + sys_node[x] is not None + and self._want[x] != sys_node[x] + ) + ) + + if needs_update("domain_name"): + system_param = { + "vrf": self._want["vrf"], + "domain_name": self._want["domain_name"], + } + + if ( + self._want["hostname"] is not None + and self._want["hostname"] != hostname + ): + hostname_param = {"hostname": self._want["hostname"]} + + if not self._want["lookup_enabled"] and sys_node["lookup_enabled"]: + lookup_param["vrf"] = self._want["vrf"] + + if needs_update("lookup_source"): + lookup_source_params["vrf"] = self._want["vrf"] + lookup_source_params["lookup_source"] = self._want[ + "lookup_source" + ] + + if self._want["domain_search"]: + domain_adds, domain_removes = diff_list( + self._want["domain_search"], sys_node["domain_search"] + ) + domain_param = {} + domain_param["domain_name"] = self._want["domain_name"] + domain_param["vrf"] = self._want["vrf"] + domain_param["order"] = "0" + for domain in domain_adds: + if domain not in sys_node["domain_search"]: + domain_param["domain_search"] = domain + add_domain_params.append(domain_param.copy()) + for domain in domain_removes: + if domain in sys_node["domain_search"]: + domain_param["domain_search"] = domain + del_domain_params.append(domain_param.copy()) + + if self._want["name_servers"]: + server_adds, server_removes = diff_list( + self._want["name_servers"], sys_node["name_servers"] + ) + server_param = {} + server_param["vrf"] = self._want["vrf"] + server_param["order"] = "0" + for domain in server_adds: + if domain not in sys_node["name_servers"]: + server_param["name_servers"] = domain + add_server_params.append(server_param.copy()) + for domain in server_removes: + if domain in sys_node["name_servers"]: + server_param["name_servers"] = domain + del_server_params.append(server_param.copy()) + + self._result["xml"] = [] + _edit_filter_list = list() + if opcode: + if hostname_param: + _edit_filter_list.append( + build_xml( + "host-names", + xmap=self._hostname_meta, + params=hostname_param, + opcode=opcode, + ) + ) + + if system_param: + _edit_filter_list.append( + build_xml( + "ip-domain", + xmap=self._system_meta, + params=system_param, + opcode=opcode, + ) + ) + + if lookup_source_params: + _edit_filter_list.append( + build_xml( + "ip-domain", + xmap=self._lookup_source_meta, + params=lookup_source_params, + opcode=opcode, + ) + ) + if lookup_param: + _edit_filter_list.append( + build_xml( + "ip-domain", + xmap=self._lookup_meta, + params=lookup_param, + opcode=opcode, + ) + ) + + if opcode == "delete": + if sys_domain_params: + _edit_filter_list.append( + build_xml( + "ip-domain", + xmap=self._system_domain_meta, + params=sys_domain_params, + opcode=opcode, + ) + ) + if sys_server_params: + _edit_filter_list.append( + build_xml( + "ip-domain", + xmap=self._system_server_meta, + params=sys_server_params, + opcode=opcode, + ) + ) + if self._want["vrf"] != "default": + self._result["warnings"] = [ + "name-servers delete operation with non-default vrf is a success, " + "but with rpc-error. Recommended to use 'ignore_errors' option with the task as a workaround" + ] + elif opcode == "merge": + if add_domain_params: + _edit_filter_list.append( + build_xml( + "ip-domain", + xmap=self._system_domain_meta, + params=add_domain_params, + opcode=opcode, + ) + ) + if del_domain_params: + _edit_filter_list.append( + build_xml( + "ip-domain", + xmap=self._system_domain_meta, + params=del_domain_params, + opcode="delete", + ) + ) + + if add_server_params: + _edit_filter_list.append( + build_xml( + "ip-domain", + xmap=self._system_server_meta, + params=add_server_params, + opcode=opcode, + ) + ) + if del_server_params: + _edit_filter_list.append( + build_xml( + "ip-domain", + xmap=self._system_server_meta, + params=del_server_params, + opcode="delete", + ) + ) + + diff = None + if _edit_filter_list: + commit = not self._module.check_mode + diff = load_config( + self._module, + _edit_filter_list, + commit=commit, + running=running, + nc_get_filter=_get_filter, + ) + + if diff: + if self._module._diff: + self._result["diff"] = dict(prepared=diff) + + self._result["xml"] = _edit_filter_list + self._result["changed"] = True + + def run(self): + self.map_params_to_obj() + self.map_obj_to_xml_rpc() + + return self._result + + +def main(): + """ Main entry point for Ansible module execution + """ + argument_spec = dict( + hostname=dict(type="str"), + vrf=dict(type="str", default="default"), + domain_name=dict(type="str"), + domain_search=dict(type="list", elements="str"), + name_servers=dict(type="list", elements="str"), + lookup_source=dict(type="str"), + lookup_enabled=dict(type="bool", default=True), + state=dict(choices=["present", "absent"], default="present"), + ) + + argument_spec.update(iosxr_argument_spec) + + module = AnsibleModule( + argument_spec=argument_spec, supports_check_mode=True + ) + + config_object = None + if is_cliconf(module): + # Commenting the below cliconf deprecation support call for Ansible 2.9 as it'll be continued to be supported + # module.deprecate("cli support for 'iosxr_interface' is deprecated. Use transport netconf instead", + # version='2.9') + config_object = CliConfiguration(module) + elif is_netconf(module): + config_object = NCConfiguration(module) + + result = None + if config_object: + result = config_object.run() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_user.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_user.py new file mode 100644 index 00000000..e1c39905 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/modules/iosxr_user.py @@ -0,0 +1,973 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2017, 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 + + +DOCUMENTATION = """ +module: iosxr_user +author: +- Trishna Guha (@trishnaguha) +- Sebastiaan van Doesselaar (@sebasdoes) +- Kedar Kekan (@kedarX) +short_description: Manage the aggregate of local users on Cisco IOS XR 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 +extends_documentation_fragment: +- cisco.iosxr.iosxr +notes: +- This module works with connection C(network_cli) and C(netconf). See L(the IOS-XR + Platform Options,../network/user_guide/platform_iosxr.html). +- Tested against IOS XRv 6.1.3 +options: + aggregate: + description: + - The set of username objects to be configured on the remote Cisco IOS XR 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 XR 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 XR device. The password needs + to be provided in clear text. Password is encrypted on the device when used + with I(cli) and by Ansible when used with I(netconf) using the same MD5 hash + technique with salt size of 3. 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. + type: str + choices: + - on_create + - always + group: + description: + - Configures the group for the username in the device running configuration. The + argument accepts a string value defining the group name. This argument does + not check if the group has been configured on the device. + type: str + aliases: + - role + groups: + description: + - Configures the groups for the username in the device running configuration. + The argument accepts a list of group names. This argument does not check if + the group has been configured on the device. It is similar to the aggregate + command for usernames, but lets you configure multiple groups for the user(s). + type: list + elements: str + admin: + description: + - Enters into administration configuration mode for making config changes to the + device. + - Applicable only when using network_cli transport + 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 + type: str + choices: + - present + - absent + public_key: + description: + - Configures the contents of the public keyfile to upload to the IOS-XR node. + This enables users to login using the accompanying private key. IOS-XR only + accepts base64 decoded files, so this will be decoded and uploaded to the node. + Do note that this requires an OpenSSL public key file, PuTTy generated files + will not work! Mutually exclusive with public_key_contents. If used with multiple + users in aggregates, then the same key file is used for all users. + type: str + public_key_contents: + description: + - Configures the contents of the public keyfile to upload to the IOS-XR node. + This enables users to login using the accompanying private key. IOS-XR only + accepts base64 decoded files, so this will be decoded and uploaded to the node. + Do note that this requires an OpenSSL public key file, PuTTy generated files + will not work! Mutually exclusive with public_key.If used with multiple users + in aggregates, then the same key file is used for all users. + type: str + name: + description: + - The username to be configured on the Cisco IOS XR 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 XR device. The password needs + to be provided in clear text. Password is encrypted on the device when used + with I(cli) and by Ansible when used with I(netconf) using the same MD5 hash + technique with salt size of 3. 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. + type: str + default: always + choices: + - on_create + - always + group: + description: + - Configures the group for the username in the device running configuration. The + argument accepts a string value defining the group name. This argument does + not check if the group has been configured on the device. + type: str + aliases: + - role + groups: + description: + - Configures the groups for the username in the device running configuration. + The argument accepts a list of group names. This argument does not check if + the group has been configured on the device. It is similar to the aggregate + command for usernames, but lets you configure multiple groups for the user(s). + type: list + elements: str + 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 and the current defined set of users. + type: bool + default: false + admin: + description: + - Enters into administration configuration mode for making config changes to the + device. + - Applicable only when using network_cli transport + 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 + type: str + default: present + choices: + - present + - absent + public_key: + description: + - Configures the contents of the public keyfile to upload to the IOS-XR node. + This enables users to login using the accompanying private key. IOS-XR only + accepts base64 decoded files, so this will be decoded and uploaded to the node. + Do note that this requires an OpenSSL public key file, PuTTy generated files + will not work! Mutually exclusive with public_key_contents. If used with multiple + users in aggregates, then the same key file is used for all users. + type: str + public_key_contents: + description: + - Configures the contents of the public keyfile to upload to the IOS-XR node. + This enables users to login using the accompanying private key. IOS-XR only + accepts base64 decoded files, so this will be decoded and uploaded to the node. + Do note that this requires an OpenSSL public key file, PuTTy generated files + will not work! Mutually exclusive with public_key.If used with multiple users + in aggregates, then the same key file is used for all users. + type: str + +requirements: +- ncclient >= 0.5.3 when using netconf +- lxml >= 4.1.1 when using netconf +- base64 when using I(public_key_contents) or I(public_key) +- paramiko when using I(public_key_contents) or I(public_key) +""" + +EXAMPLES = """ +- name: create a new user + cisco.iosxr.iosxr_user: + name: ansible + configured_password: mypassword + state: present +- name: create a new user in admin configuration mode + cisco.iosxr.iosxr_user: + name: ansible + configured_password: mypassword + admin: true + state: present +- name: remove all users except admin + cisco.iosxr.iosxr_user: + purge: true +- name: set multiple users to group sys-admin + cisco.iosxr.iosxr_user: + aggregate: + - name: netop + - name: netend + group: sysadmin + state: present +- name: set multiple users to multiple groups + cisco.iosxr.iosxr_user: + aggregate: + - name: netop + - name: netend + groups: + - sysadmin + - root-system + state: present +- name: Change Password for User netop + cisco.iosxr.iosxr_user: + name: netop + configured_password: '{{ new_password }}' + update_password: always + state: present +- name: Add private key authentication for user netop + cisco.iosxr.iosxr_user: + name: netop + state: present + public_key_contents: "{{ lookup('file', '/home/netop/.ssh/id_rsa.pub' }}" +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - username ansible secret password group sysadmin + - username admin secret admin +xml: + description: NetConf rpc xml sent to device with transport C(netconf) + returned: always (empty list when no xml rpc to send) + type: list + sample: + - '<config xmlns:xc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"> + <aaa xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XR-aaa-lib-cfg\"> + <usernames xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XR-aaa-locald-cfg\"> + <username xc:operation=\"merge\"> + <name>test7</name> + <usergroup-under-usernames> + <usergroup-under-username> + <name>sysadmin</name> + </usergroup-under-username> + </usergroup-under-usernames> + <secret>$1$ZsXC$zZ50wqhDC543ZWQkkAHLW0</secret> + </username> + </usernames> + </aaa> + </config>' +""" + +import os +from functools import partial +from copy import deepcopy +import collections + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.compat.paramiko import paramiko +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + get_config, + load_config, + is_netconf, + is_cliconf, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + iosxr_argument_spec, + build_xml, + etree_findall, +) + +try: + from base64 import b64decode + + HAS_B64 = True +except ImportError: + HAS_B64 = False + + +class PublicKeyManager(object): + def __init__(self, module, result): + self._module = module + self._result = result + + def convert_key_to_base64(self): + """ IOS-XR only accepts base64 decoded files, this converts the public key to a temp file. + """ + if self._module.params["aggregate"]: + name = "aggregate" + else: + name = self._module.params["name"] + + if self._module.params["public_key_contents"]: + key = self._module.params["public_key_contents"] + elif self._module.params["public_key"]: + readfile = open(self._module.params["public_key"], "r") + key = readfile.read() + splitfile = key.split()[1] + + base64key = b64decode(splitfile) + base64file = open("/tmp/publickey_%s.b64" % (name), "wb") + base64file.write(base64key) + base64file.close() + + return "/tmp/publickey_%s.b64" % (name) + + def copy_key_to_node(self, base64keyfile): + """ Copy key to IOS-XR node. We use SFTP because older IOS-XR versions don't handle SCP very well. + """ + provider = self._module.params.get("provider") or {} + node = provider.get("host") + if node is None: + return False + + user = provider.get("username") + if user is None: + return False + + password = provider.get("password") + ssh_keyfile = provider.get("ssh_keyfile") + + if self._module.params["aggregate"]: + name = "aggregate" + else: + name = self._module.params["name"] + + src = base64keyfile + dst = "/harddisk:/publickey_%s.b64" % (name) + + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + if not ssh_keyfile: + ssh.connect(node, username=user, password=password) + else: + ssh.connect(node, username=user, allow_agent=True) + sftp = ssh.open_sftp() + sftp.put(src, dst) + sftp.close() + ssh.close() + + def addremovekey(self, command): + """ Add or remove key based on command + """ + provider = self._module.params.get("provider") or {} + node = provider.get("host") + if node is None: + return False + + user = provider.get("username") + if user is None: + return False + + password = provider.get("password") + ssh_keyfile = provider.get("ssh_keyfile") + + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + if not ssh_keyfile: + ssh.connect(node, username=user, password=password) + else: + ssh.connect(node, username=user, allow_agent=True) + ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command( + "%s \r" % (command) + ) + readmsg = ssh_stdout.read( + 100 + ) # We need to read a bit to actually apply for some reason + if ( + ("already" in readmsg) + or ("removed" in readmsg) + or ("really" in readmsg) + ): + ssh_stdin.write("yes\r") + ssh_stdout.read( + 1 + ) # We need to read a bit to actually apply for some reason + ssh.close() + + return readmsg + + def run(self): + if self._module.params["state"] == "present": + if not self._module.check_mode: + key = self.convert_key_to_base64() + copykeys = self.copy_key_to_node(key) + if copykeys is False: + self._result["warnings"].append( + "Please set up your provider before running this playbook" + ) + + if self._module.params["aggregate"]: + for user in self._module.params["aggregate"]: + cmdtodo = ( + "admin crypto key import authentication rsa username %s harddisk:/publickey_aggregate.b64" + % (user) + ) + addremove = self.addremovekey(cmdtodo) + if addremove is False: + self._result["warnings"].append( + "Please set up your provider before running this playbook" + ) + else: + cmdtodo = ( + "admin crypto key import authentication rsa username %s harddisk:/publickey_%s.b64" + % ( + self._module.params["name"], + self._module.params["name"], + ) + ) + addremove = self.addremovekey(cmdtodo) + if addremove is False: + self._result["warnings"].append( + "Please set up your provider before running this playbook" + ) + elif self._module.params["state"] == "absent": + if not self._module.check_mode: + if self._module.params["aggregate"]: + for user in self._module.params["aggregate"]: + cmdtodo = ( + "admin crypto key zeroize authentication rsa username %s" + % (user) + ) + addremove = self.addremovekey(cmdtodo) + if addremove is False: + self._result["warnings"].append( + "Please set up your provider before running this playbook" + ) + else: + cmdtodo = ( + "admin crypto key zeroize authentication rsa username %s" + % (self._module.params["name"]) + ) + addremove = self.addremovekey(cmdtodo) + if addremove is False: + self._result["warnings"].append( + "Please set up your provider before running this playbook" + ) + elif self._module.params["purge"] is True: + if not self._module.check_mode: + cmdtodo = "admin crypto key zeroize authentication rsa all" + addremove = self.addremovekey(cmdtodo) + if addremove is False: + self._result["warnings"].append( + "Please set up your provider before running this playbook" + ) + + return self._result + + +def search_obj_in_list(name, lst): + for o in lst: + if o["name"] == name: + return o + + return None + + +class ConfigBase(object): + def __init__(self, module, result, flag=None): + self._module = module + self._result = result + self._want = list() + self._have = list() + + def get_param_value(self, key, item): + # if key doesn't exist in the item, get it from module.params + if not item.get(key): + value = self._module.params[key] + + # if key does exist, do a type check on it to validate it + else: + value_type = self._module.argument_spec[key].get("type", "str") + type_checker = self._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, self._module) + + return value + + def map_params_to_obj(self): + users = self._module.params["aggregate"] + + aggregate = list() + if not users: + if ( + not self._module.params["name"] + and self._module.params["purge"] + ): + pass + elif not self._module.params["name"]: + self._module.fail_json(msg="username is required") + else: + aggregate = [{"name": self._module.params["name"]}] + else: + for item in users: + if not isinstance(item, dict): + aggregate.append({"name": item}) + elif "name" not in item: + self._module.fail_json(msg="name is required") + else: + aggregate.append(item) + + for item in aggregate: + get_value = partial(self.get_param_value, item=item) + item["configured_password"] = get_value("configured_password") + item["group"] = get_value("group") + item["groups"] = get_value("groups") + item["state"] = get_value("state") + self._want.append(item) + + +class CliConfiguration(ConfigBase): + def __init__(self, module, result): + super(CliConfiguration, self).__init__(module, result) + + def map_config_to_obj(self): + data = get_config(self._module, config_filter="username") + users = data.strip().rstrip("!").split("!") + + for user in users: + user_config = user.strip().splitlines() + + name = user_config[0].strip().split()[1] + group = None + + if len(user_config) > 1: + group_or_secret = user_config[1].strip().split() + if group_or_secret[0] == "group": + group = group_or_secret[1] + + obj = { + "name": name, + "state": "present", + "configured_password": None, + "group": group, + } + self._have.append(obj) + + def map_obj_to_commands(self): + commands = list() + + for w in self._want: + name = w["name"] + state = w["state"] + + obj_in_have = search_obj_in_list(name, self._have) + + if state == "absent" and obj_in_have: + commands.append("no username " + name) + elif state == "present" and not obj_in_have: + user_cmd = "username " + name + commands.append(user_cmd) + + if w["configured_password"]: + commands.append( + user_cmd + " secret " + w["configured_password"] + ) + if w["group"]: + commands.append(user_cmd + " group " + w["group"]) + elif w["groups"]: + for group in w["groups"]: + commands.append(user_cmd + " group " + group) + + elif state == "present" and obj_in_have: + user_cmd = "username " + name + + if ( + self._module.params["update_password"] == "always" + and w["configured_password"] + ): + commands.append( + user_cmd + " secret " + w["configured_password"] + ) + if w["group"] and w["group"] != obj_in_have["group"]: + commands.append(user_cmd + " group " + w["group"]) + elif w["groups"]: + for group in w["groups"]: + commands.append(user_cmd + " group " + group) + + if self._module.params["purge"]: + want_users = [x["name"] for x in self._want] + have_users = [x["name"] for x in self._have] + for item in set(have_users).difference(set(want_users)): + if item != "admin": + commands.append("no username %s" % item) + + if "no username admin" in commands: + self._module.fail_json(msg="cannot delete the `admin` account") + + self._result["commands"] = [] + if commands: + commit = not self._module.check_mode + admin = self._module.params["admin"] + diff = load_config( + self._module, commands, commit=commit, admin=admin + ) + if diff: + self._result["diff"] = dict(prepared=diff) + + self._result["commands"] = commands + self._result["changed"] = True + + def run(self): + self.map_params_to_obj() + self.map_config_to_obj() + self.map_obj_to_commands() + + return self._result + + +class NCConfiguration(ConfigBase): + def __init__(self, module, result): + super(NCConfiguration, self).__init__(module, result) + self._locald_meta = collections.OrderedDict() + self._locald_group_meta = collections.OrderedDict() + + def generate_md5_hash(self, arg): + """ + Generate MD5 hash with randomly generated salt size of 3. + :param arg: + :return passwd: + """ + cmd = "openssl passwd -salt `openssl rand -base64 3` -1 " + return os.popen(cmd + arg).readlines()[0].strip() + + def map_obj_to_xml_rpc(self): + self._locald_meta.update( + [ + ( + "aaa_locald", + {"xpath": "aaa/usernames", "tag": True, "ns": True}, + ), + ( + "username", + { + "xpath": "aaa/usernames/username", + "tag": True, + "attrib": "operation", + }, + ), + ("a:name", {"xpath": "aaa/usernames/username/name"}), + ( + "a:configured_password", + { + "xpath": "aaa/usernames/username/secret", + "operation": "edit", + }, + ), + ] + ) + + self._locald_group_meta.update( + [ + ( + "aaa_locald", + {"xpath": "aaa/usernames", "tag": True, "ns": True}, + ), + ( + "username", + { + "xpath": "aaa/usernames/username", + "tag": True, + "attrib": "operation", + }, + ), + ("a:name", {"xpath": "aaa/usernames/username/name"}), + ( + "usergroups", + { + "xpath": "aaa/usernames/username/usergroup-under-usernames", + "tag": True, + "operation": "edit", + }, + ), + ( + "usergroup", + { + "xpath": "aaa/usernames/username/usergroup-under-usernames/usergroup-under-username", + "tag": True, + "operation": "edit", + }, + ), + ( + "a:group", + { + "xpath": "aaa/usernames/username/usergroup-under-usernames/usergroup-under-username/name", + "operation": "edit", + }, + ), + ] + ) + + state = self._module.params["state"] + _get_filter = build_xml("aaa", opcode="filter") + running = get_config( + self._module, source="running", config_filter=_get_filter + ) + + elements = etree_findall(running, "username") + users = list() + for element in elements: + name_list = etree_findall(element, "name") + users.append(name_list[0].text) + list_size = len(name_list) + if list_size == 1: + self._have.append( + {"name": name_list[0].text, "group": None, "groups": None} + ) + elif list_size == 2: + self._have.append( + { + "name": name_list[0].text, + "group": name_list[1].text, + "groups": None, + } + ) + elif list_size > 2: + name_iter = iter(name_list) + next(name_iter) + tmp_list = list() + for name in name_iter: + tmp_list.append(name.text) + + self._have.append( + { + "name": name_list[0].text, + "group": None, + "groups": tmp_list, + } + ) + + locald_params = list() + locald_group_params = list() + opcode = None + + if state == "absent": + opcode = "delete" + for want_item in self._want: + if want_item["name"] in users: + want_item["configured_password"] = None + locald_params.append(want_item) + elif state == "present": + opcode = "merge" + for want_item in self._want: + if want_item["name"] not in users: + want_item["configured_password"] = self.generate_md5_hash( + want_item["configured_password"] + ) + locald_params.append(want_item) + + if want_item["group"] is not None: + locald_group_params.append(want_item) + if want_item["groups"] is not None: + for group in want_item["groups"]: + want_item["group"] = group + locald_group_params.append(want_item.copy()) + else: + if ( + self._module.params["update_password"] == "always" + and want_item["configured_password"] is not None + ): + want_item[ + "configured_password" + ] = self.generate_md5_hash( + want_item["configured_password"] + ) + locald_params.append(want_item) + else: + want_item["configured_password"] = None + + obj_in_have = search_obj_in_list( + want_item["name"], self._have + ) + if ( + want_item["group"] is not None + and want_item["group"] != obj_in_have["group"] + ): + locald_group_params.append(want_item) + elif want_item["groups"] is not None: + for group in want_item["groups"]: + want_item["group"] = group + locald_group_params.append(want_item.copy()) + + purge_params = list() + if self._module.params["purge"]: + want_users = [x["name"] for x in self._want] + have_users = [x["name"] for x in self._have] + for item in set(have_users).difference(set(want_users)): + if item != "admin": + purge_params.append({"name": item}) + + self._result["xml"] = [] + _edit_filter_list = list() + if opcode is not None: + if locald_params: + _edit_filter_list.append( + build_xml( + "aaa", + xmap=self._locald_meta, + params=locald_params, + opcode=opcode, + ) + ) + + if locald_group_params: + _edit_filter_list.append( + build_xml( + "aaa", + xmap=self._locald_group_meta, + params=locald_group_params, + opcode=opcode, + ) + ) + + if purge_params: + _edit_filter_list.append( + build_xml( + "aaa", + xmap=self._locald_meta, + params=purge_params, + opcode="delete", + ) + ) + + diff = None + if _edit_filter_list: + commit = not self._module.check_mode + diff = load_config( + self._module, + _edit_filter_list, + commit=commit, + running=running, + nc_get_filter=_get_filter, + ) + + if diff: + if self._module._diff: + self._result["diff"] = dict(prepared=diff) + + self._result["xml"] = _edit_filter_list + self._result["changed"] = True + + def run(self): + self.map_params_to_obj() + self.map_obj_to_xml_rpc() + + return self._result + + +def main(): + """ main entry point for module execution + """ + element_spec = dict( + name=dict(type="str"), + configured_password=dict(type="str", no_log=True), + update_password=dict( + type="str", default="always", choices=["on_create", "always"] + ), + admin=dict(type="bool", default=False), + public_key=dict(type="str"), + public_key_contents=dict(type="str"), + group=dict(type="str", aliases=["role"]), + groups=dict(type="list", elements="str"), + state=dict( + type="str", 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) + + mutually_exclusive = [ + ("name", "aggregate"), + ("public_key", "public_key_contents"), + ("group", "groups"), + ] + + 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(iosxr_argument_spec) + + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + + if module.params["public_key_contents"] or module.params["public_key"]: + if not HAS_B64: + module.fail_json( + msg="library base64 is required but does not appear to be " + "installed. It can be installed using `pip install base64`" + ) + if paramiko is None: + module.fail_json( + msg="library paramiko is required but does not appear to be " + "installed. It can be installed using `pip install paramiko`" + ) + + result = {"changed": False, "warnings": []} + + config_object = None + if is_cliconf(module): + # Commenting the below cliconf deprecation support call for Ansible 2.9 as it'll be continued to be supported + # module.deprecate("cli support for 'iosxr_interface' is deprecated. Use transport netconf instead", + # version='2.9') + config_object = CliConfiguration(module, result) + elif is_netconf(module): + config_object = NCConfiguration(module, result) + + if config_object: + result = config_object.run() + + if module.params["public_key_contents"] or module.params["public_key"]: + pubkey_object = PublicKeyManager(module, result) + result = pubkey_object.run() + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/netconf/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/netconf/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/netconf/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/netconf/iosxr.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/netconf/iosxr.py new file mode 100644 index 00000000..6e9255e0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/netconf/iosxr.py @@ -0,0 +1,297 @@ +# +# (c) 2017 Red Hat Inc. +# (c) 2017 Kedar Kekan (kkekan@redhat.com) +# +# 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 +netconf: iosxr +short_description: Use iosxr netconf plugin to run netconf commands on Cisco IOSXR + platform +description: +- This iosxr plugin provides low level abstraction apis for sending and receiving + netconf commands from Cisco iosxr network devices. +version_added: 1.0.0 +options: + ncclient_device_handler: + type: str + default: iosxr + description: + - Specifies the ncclient device handler name for Cisco iosxr network os. To identify + the ncclient device handler name refer ncclient library documentation. +""" + +import json +import re +import collections + +from ansible.module_utils._text import to_native, to_text +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import ( + remove_namespaces, +) +from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import ( + build_xml, + etree_find, +) +from ansible.errors import AnsibleConnectionFailure +from ansible.plugins.netconf import NetconfBase, ensure_ncclient + +try: + from ncclient import manager + from ncclient.operations import RPCError + from ncclient.transport.errors import SSHUnknownHostError + from ncclient.xml_ import to_xml + + HAS_NCCLIENT = True +except ( + ImportError, + AttributeError, +): # paramiko and gssapi are incompatible and raise AttributeError not ImportError + HAS_NCCLIENT = False + + +class Netconf(NetconfBase): + def get_device_info(self): + device_info = {} + device_info["network_os"] = "iosxr" + install_meta = collections.OrderedDict() + install_meta.update( + [ + ( + "boot-variables", + {"xpath": "install/boot-variables", "tag": True}, + ), + ( + "boot-variable", + { + "xpath": "install/boot-variables/boot-variable", + "tag": True, + "lead": True, + }, + ), + ("software", {"xpath": "install/software", "tag": True}), + ( + "alias-devices", + {"xpath": "install/software/alias-devices", "tag": True}, + ), + ( + "alias-device", + { + "xpath": "install/software/alias-devices/alias-device", + "tag": True, + }, + ), + ( + "m:device-name", + { + "xpath": "install/software/alias-devices/alias-device/device-name", + "value": "disk0:", + }, + ), + ] + ) + + install_filter = build_xml("install", install_meta, opcode="filter") + try: + reply = self.get(install_filter) + resp = remove_namespaces( + re.sub(r'<\?xml version="1.0" encoding="UTF-8"\?>', "", reply) + ) + ele_boot_variable = etree_find(resp, "boot-variable/boot-variable") + if ele_boot_variable is not None: + device_info["network_os_image"] = re.split( + "[:|,]", ele_boot_variable.text + )[1] + ele_package_name = etree_find(reply, "package-name") + if ele_package_name is not None: + device_info["network_os_package"] = ele_package_name.text + device_info["network_os_version"] = re.split( + "-", ele_package_name.text + )[-1] + + hostname_filter = build_xml("host-names", opcode="filter") + reply = self.get(hostname_filter) + resp = remove_namespaces( + re.sub(r'<\?xml version="1.0" encoding="UTF-8"\?>', "", reply) + ) + hostname_ele = etree_find(resp.strip(), "host-name") + device_info["network_os_hostname"] = ( + hostname_ele.text if hostname_ele is not None else None + ) + except Exception as exc: + self._connection.queue_message( + "vvvv", "Fail to retrieve device info %s" % exc + ) + return device_info + + def get_capabilities(self): + result = dict() + result["rpc"] = self.get_base_rpc() + result["network_api"] = "netconf" + result["device_info"] = self.get_device_info() + result["server_capabilities"] = list(self.m.server_capabilities) + result["client_capabilities"] = list(self.m.client_capabilities) + result["session_id"] = self.m.session_id + result["device_operations"] = self.get_device_operations( + result["server_capabilities"] + ) + return json.dumps(result) + + @staticmethod + @ensure_ncclient + def guess_network_os(obj): + """ + Guess the remote network os name + :param obj: Netconf connection class object + :return: Network OS name + """ + try: + m = manager.connect( + host=obj._play_context.remote_addr, + port=obj._play_context.port or 830, + username=obj._play_context.remote_user, + password=obj._play_context.password, + key_filename=obj.key_filename, + hostkey_verify=obj.get_option("host_key_checking"), + look_for_keys=obj.get_option("look_for_keys"), + allow_agent=obj._play_context.allow_agent, + timeout=obj.get_option("persistent_connect_timeout"), + # We need to pass in the path to the ssh_config file when guessing + # the network_os so that a jumphost is correctly used if defined + ssh_config=obj._ssh_config, + ) + except SSHUnknownHostError as exc: + raise AnsibleConnectionFailure(to_native(exc)) + + guessed_os = None + for c in m.server_capabilities: + if re.search("IOS-XR", c): + guessed_os = "iosxr" + break + + m.close_session() + return guessed_os + + # TODO: change .xml to .data_xml, when ncclient supports data_xml on all platforms + def get(self, filter=None, remove_ns=False): + if isinstance(filter, list): + filter = tuple(filter) + try: + resp = self.m.get(filter=filter) + if remove_ns: + response = remove_namespaces(resp) + else: + response = ( + resp.data_xml if hasattr(resp, "data_xml") else resp.xml + ) + return response + except RPCError as exc: + raise Exception(to_xml(exc.xml)) + + def get_config(self, source=None, filter=None, remove_ns=False): + if isinstance(filter, list): + filter = tuple(filter) + try: + resp = self.m.get_config(source=source, filter=filter) + if remove_ns: + response = remove_namespaces(resp) + else: + response = ( + resp.data_xml if hasattr(resp, "data_xml") else resp.xml + ) + return response + except RPCError as exc: + raise Exception(to_xml(exc.xml)) + + def edit_config( + self, + config=None, + format="xml", + target="candidate", + default_operation=None, + test_option=None, + error_option=None, + remove_ns=False, + ): + if config is None: + raise ValueError("config value must be provided") + try: + resp = self.m.edit_config( + config, + format=format, + target=target, + default_operation=default_operation, + test_option=test_option, + error_option=error_option, + ) + if remove_ns: + response = remove_namespaces(resp) + else: + response = ( + resp.data_xml if hasattr(resp, "data_xml") else resp.xml + ) + return response + except RPCError as exc: + raise Exception(to_xml(exc.xml)) + + def commit( + self, confirmed=False, timeout=None, persist=None, remove_ns=False + ): + timeout = to_text(timeout, errors="surrogate_or_strict") + try: + resp = self.m.commit( + confirmed=confirmed, timeout=timeout, persist=persist + ) + if remove_ns: + response = remove_namespaces(resp) + else: + response = ( + resp.data_xml if hasattr(resp, "data_xml") else resp.xml + ) + return response + except RPCError as exc: + raise Exception(to_xml(exc.xml)) + + def validate(self, source="candidate", remove_ns=False): + try: + resp = self.m.validate(source=source) + if remove_ns: + response = remove_namespaces(resp) + else: + response = ( + resp.data_xml if hasattr(resp, "data_xml") else resp.xml + ) + return response + except RPCError as exc: + raise Exception(to_xml(exc.xml)) + + def discard_changes(self, remove_ns=False): + try: + resp = self.m.discard_changes() + if remove_ns: + response = remove_namespaces(resp) + else: + response = ( + resp.data_xml if hasattr(resp, "data_xml") else resp.xml + ) + return response + except RPCError as exc: + raise Exception(to_xml(exc.xml)) diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/terminal/__init__.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/terminal/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/terminal/__init__.py diff --git a/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/terminal/iosxr.py b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/terminal/iosxr.py new file mode 100644 index 00000000..89a7fd15 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cisco/iosxr/plugins/terminal/iosxr.py @@ -0,0 +1,57 @@ +# +# (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 re + +from ansible.plugins.terminal import TerminalBase +from ansible.errors import AnsibleConnectionFailure + + +class TerminalModule(TerminalBase): + + terminal_stdout_re = [ + re.compile(br"[\r\n]*[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"), + re.compile(br"]]>]]>[\r\n]?"), + ] + + terminal_stderr_re = [ + re.compile(br"% ?Error"), + re.compile(br"% ?Bad secret"), + re.compile(br"% ?This command is not authorized"), + 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.I), + re.compile(br"'[^']' +returned error code: ?\d+"), + re.compile(br"Failed to commit", re.I), + ] + + def on_open_shell(self): + try: + for cmd in ( + b"terminal length 0", + b"terminal width 512", + b"terminal exec prompt no-timestamp", + ): + self._exec_cli_command(cmd) + except AnsibleConnectionFailure: + raise AnsibleConnectionFailure("unable to set terminal parameters") |